Replace Appwrite with Supabase
This commit is contained in:
parent
0ea0d8b73c
commit
c2c1acf5c6
16 changed files with 71 additions and 390 deletions
|
@ -1 +0,0 @@
|
|||
<slot />
|
|
@ -1,155 +0,0 @@
|
|||
---
|
||||
// Properties
|
||||
const {Title, Type} = Astro.props
|
||||
|
||||
// Check if user is already logged in, redirect if so
|
||||
const { user } = Astro.locals;
|
||||
if (user) {
|
||||
return Astro.redirect("/account");
|
||||
}
|
||||
---
|
||||
|
||||
<img id="bg" src="/login-dark-mode.png"/>
|
||||
<div class="login-dialog">
|
||||
<div>
|
||||
<img src="/login-dark-mode.png"/>
|
||||
</div>
|
||||
<div class="ld-main">
|
||||
<h2>{Title}</h2>
|
||||
<form method="POST">
|
||||
{
|
||||
()=> {
|
||||
if (Type === "SignUp") {
|
||||
return <input id="name" name="name" placeholder="Your name" autocomplete="off" type="text"/>
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
<input id="email" name="email" type="email" placeholder="Email" autocomplete="off"/>
|
||||
<input id="password" name="password" type="password" placeholder="Password" minlength="8" maxlength="512"/>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between;">
|
||||
{
|
||||
()=> {
|
||||
if (Type === "SignIn") {
|
||||
return <p>Don't have an account? <a href="/signup/">Sign Up</a></p>
|
||||
<button style="height: max-content;" type="submit">Login</button>
|
||||
} else if (Type === "SignUp") {
|
||||
return <p>Already have an account? <a href="/signin/">Sign In</a></p>
|
||||
<button style="height: max-content;" type="submit">Sign Up</button>
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<!-- <center><p>or</p></center> -->
|
||||
</form>
|
||||
<!-- <form method="POST" action="/api/discord">
|
||||
<center><button style="background: #5865F2"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 127.14 96.36"><path fill="#fff" d="M107.7,8.07A105.15,105.15,0,0,0,81.47,0a72.06,72.06,0,0,0-3.36,6.83A97.68,97.68,0,0,0,49,6.83,72.37,72.37,0,0,0,45.64,0,105.89,105.89,0,0,0,19.39,8.09C2.79,32.65-1.71,56.6.54,80.21h0A105.73,105.73,0,0,0,32.71,96.36,77.7,77.7,0,0,0,39.6,85.25a68.42,68.42,0,0,1-10.85-5.18c.91-.66,1.8-1.34,2.66-2a75.57,75.57,0,0,0,64.32,0c.87.71,1.76,1.39,2.66,2a68.68,68.68,0,0,1-10.87,5.19,77,77,0,0,0,6.89,11.1A105.25,105.25,0,0,0,126.6,80.22h0C129.24,52.84,122.09,29.11,107.7,8.07ZM42.45,65.69C36.18,65.69,31,60,31,53s5-12.74,11.43-12.74S54,46,53.89,53,48.84,65.69,42.45,65.69Zm42.24,0C78.41,65.69,73.25,60,73.25,53s5-12.74,11.44-12.74S96.23,46,96.12,53,91.08,65.69,84.69,65.69Z"/></svg> Login with Discord</button></center>
|
||||
</form> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
body {
|
||||
color: white;
|
||||
background: black;
|
||||
font-family: arial;
|
||||
overflow: hidden;
|
||||
*:focus {
|
||||
outline: none;
|
||||
}
|
||||
a {
|
||||
color: #fb4e72;
|
||||
}
|
||||
}
|
||||
|
||||
body::before {
|
||||
content: "";
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
z-index: 0;
|
||||
backdrop-filter: blur(24px) contrast(0.8) brightness(0.4);
|
||||
}
|
||||
#bg {
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.login-dialog {
|
||||
position: fixed;
|
||||
top: 6%;
|
||||
left: 50%;
|
||||
transform: translate(-50%);
|
||||
max-width: 800px;
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: 50% 50%;
|
||||
background: #070707;
|
||||
border: 2px #262626 solid;
|
||||
transition: 0.3s border-color;
|
||||
border-radius: 12px;
|
||||
font-size: 14px;
|
||||
&:hover {
|
||||
border-color: #fb4e72;
|
||||
transition: 1s border-color;
|
||||
}
|
||||
img {
|
||||
border-radius: 10px 0px 0px 10px;
|
||||
width: 100%;
|
||||
margin-bottom: -3px;
|
||||
}
|
||||
.ld-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding: 0px 48px;
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
input {
|
||||
background: transparent;
|
||||
color: white;
|
||||
border: none;
|
||||
border-bottom: 2px #371f25 solid;
|
||||
padding: 6px 12px;
|
||||
&:hover {
|
||||
border-color: #551c28;
|
||||
}
|
||||
&:focus {
|
||||
border-color: #fb4e72;
|
||||
}
|
||||
}
|
||||
button {
|
||||
background: #fb4e72;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 6px 12px;
|
||||
border-radius: 3rem;
|
||||
display: flex;
|
||||
max-width: max-content;
|
||||
cursor: pointer;
|
||||
svg {
|
||||
width: 15px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
&:hover {
|
||||
filter: brightness(0.7)
|
||||
}
|
||||
&:focus {
|
||||
filter: brightness(1.1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
14
src/library/supabase.ts
Normal file
14
src/library/supabase.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { createClient } from "@supabase/supabase-js";
|
||||
|
||||
export const supabase = createClient(
|
||||
import.meta.env.SUPABASE_URL,
|
||||
import.meta.env.SUPABASE_ANON_KEY,
|
||||
{
|
||||
auth: {
|
||||
flowType: "pkce",
|
||||
autoRefreshToken: false,
|
||||
detectSessionInUrl: false,
|
||||
persistSession: true,
|
||||
},
|
||||
},
|
||||
);
|
|
@ -1,11 +0,0 @@
|
|||
import { defineMiddleware } from "astro:middleware";
|
||||
import { createSessionClient } from "./server/appwrite";
|
||||
|
||||
export const onRequest = defineMiddleware(async ({ request, locals }, next) => {
|
||||
try {
|
||||
const { account } = createSessionClient(request);
|
||||
locals.user = await account.get();
|
||||
} catch {}
|
||||
|
||||
return next();
|
||||
});
|
55
src/middleware/index.ts
Normal file
55
src/middleware/index.ts
Normal file
|
@ -0,0 +1,55 @@
|
|||
import { defineMiddleware } from "astro:middleware"
|
||||
import { supabase } from "../library/supabase"
|
||||
|
||||
const protectedRoutes = ["/dashboard"]
|
||||
const redirectRoutes = ["/signin", "/register"]
|
||||
|
||||
export const onRequest = defineMiddleware(
|
||||
async ({ locals, url, cookies, redirect }, next) => {
|
||||
if (protectedRoutes.includes(url.pathname)) {
|
||||
const accessToken = cookies.get("sb-access-token")
|
||||
const refreshToken = cookies.get("sb-refresh-token")
|
||||
|
||||
if (!accessToken || !refreshToken) {
|
||||
return redirect("/signin")
|
||||
}
|
||||
|
||||
const { data, error } = await supabase.auth.setSession({
|
||||
refresh_token: refreshToken.value,
|
||||
access_token: accessToken.value,
|
||||
})
|
||||
|
||||
if (error) {
|
||||
cookies.delete("sb-access-token", {
|
||||
path: "/",
|
||||
})
|
||||
cookies.delete("sb-refresh-token", {
|
||||
path: "/",
|
||||
})
|
||||
return redirect("/signin")
|
||||
}
|
||||
|
||||
locals.email = data.user?.email!
|
||||
cookies.set("sb-access-token", data?.session?.access_token!, {
|
||||
sameSite: "strict",
|
||||
path: "/",
|
||||
secure: true,
|
||||
})
|
||||
cookies.set("sb-refresh-token", data?.session?.refresh_token!, {
|
||||
sameSite: "strict",
|
||||
path: "/",
|
||||
secure: true,
|
||||
})
|
||||
}
|
||||
|
||||
if (redirectRoutes.includes(url.pathname)) {
|
||||
const accessToken = cookies.get("sb-access-token")
|
||||
const refreshToken = cookies.get("sb-refresh-token")
|
||||
|
||||
if (accessToken && refreshToken) {
|
||||
return redirect("/dashboard")
|
||||
}
|
||||
}
|
||||
return next()
|
||||
},
|
||||
)
|
|
@ -1,24 +0,0 @@
|
|||
import type { APIRoute } from "astro";
|
||||
import { createAdminClient, SESSION_COOKIE } from "../../server/appwrite";
|
||||
|
||||
export const GET: APIRoute = async ({ cookies, redirect, url }) => {
|
||||
const { account } = createAdminClient();
|
||||
|
||||
cookies.set('anonymous', 'true')
|
||||
const secret = url.searchParams.get("secret");
|
||||
|
||||
const session = await account.createAnonymousSession(secret);
|
||||
if (!session.secret) {
|
||||
throw new Error("Failed to create session from token");
|
||||
}
|
||||
|
||||
cookies.set(SESSION_COOKIE, session.secret, {
|
||||
sameSite: "strict",
|
||||
expires: new Date(session.expire),
|
||||
secure: true,
|
||||
httpOnly: true,
|
||||
path: "/",
|
||||
});
|
||||
|
||||
return redirect("/account");
|
||||
};
|
|
@ -1,15 +0,0 @@
|
|||
// When given all roles to ID that is used, the system says
|
||||
// the role "applications" is still a missing scope.
|
||||
import type { APIRoute } from "astro";
|
||||
import { createAdminClient, SESSION_COOKIE } from "../../../server/appwrite";
|
||||
|
||||
export const GET: APIRoute = async ({ cookies, redirect, url }) => {
|
||||
const { account } = createAdminClient();
|
||||
|
||||
const result = await account.createVerification(
|
||||
'http://localhost:4321/account/'
|
||||
);
|
||||
|
||||
console.log(result);
|
||||
return redirect("/account");
|
||||
};
|
|
@ -1,40 +0,0 @@
|
|||
import type { APIRoute } from "astro";
|
||||
import { createAdminClient, SESSION_COOKIE } from "../server/appwrite";
|
||||
|
||||
export const POST: APIRoute = async ({ redirect, url }) => {
|
||||
const { account } = createAdminClient();
|
||||
|
||||
const redirectUrl = await account.createOAuth2Token(
|
||||
"discord",
|
||||
`${url.origin}/oauth`,
|
||||
`${url.origin}/signin`
|
||||
);
|
||||
|
||||
return redirect(redirectUrl);
|
||||
};
|
||||
|
||||
export const GET: APIRoute = async ({ cookies, redirect, url }) => {
|
||||
const userId = url.searchParams.get("userId");
|
||||
const secret = url.searchParams.get("secret");
|
||||
|
||||
if (!userId || !secret) {
|
||||
throw new Error("OAuth2 did not provide userId or secret");
|
||||
}
|
||||
|
||||
const { account } = createAdminClient();
|
||||
|
||||
const session = await account.createSession(userId, secret);
|
||||
if (!session || !session.secret) {
|
||||
throw new Error("Failed to create session from token");
|
||||
}
|
||||
|
||||
cookies.set(SESSION_COOKIE, session.secret, {
|
||||
sameSite: "strict",
|
||||
expires: new Date(session.expire),
|
||||
secure: true,
|
||||
httpOnly: true,
|
||||
path: "/",
|
||||
});
|
||||
|
||||
return redirect("/account");
|
||||
};
|
|
@ -1,34 +0,0 @@
|
|||
---
|
||||
import { Users } from "node-appwrite";
|
||||
import { createAdminClient } from "../../server/appwrite";
|
||||
|
||||
if (Astro.request.method === "POST") {
|
||||
const data = await Astro.request.formData();
|
||||
const name = data.get("name") as string;
|
||||
|
||||
const { account } = createAdminClient();
|
||||
|
||||
const promise = await account.updateName(name);
|
||||
|
||||
promise.then(function (response) {
|
||||
console.log(response);
|
||||
}, function (error) {
|
||||
console.log(error);
|
||||
});
|
||||
|
||||
|
||||
return Astro.redirect("/account");
|
||||
}
|
||||
---
|
||||
|
||||
<form method="POST">
|
||||
<label for="name">New Name</label>
|
||||
<input
|
||||
id="name"
|
||||
name="name"
|
||||
placeholder="Your name"
|
||||
autocomplete="off"
|
||||
type="text"
|
||||
/>
|
||||
<button type="submit">Update Name</button>
|
||||
</form>
|
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
Astro.cookies.delete('session-token')
|
||||
Astro.cookies.delete('anonymous')
|
||||
---
|
||||
|
||||
<script is:inline>location.href = '/'</script>
|
|
@ -1,43 +0,0 @@
|
|||
---
|
||||
import SignInUp from "../layouts/SignInUp.astro";
|
||||
|
||||
import { ID } from "node-appwrite";
|
||||
import { SESSION_COOKIE, createAdminClient } from "../server/appwrite";
|
||||
|
||||
|
||||
|
||||
if (Astro.request.method === "POST") {
|
||||
const data = await Astro.request.formData();
|
||||
|
||||
const email = data.get("email") as string;
|
||||
const password = data.get("password") as string;
|
||||
const name = data.get("name") as string;
|
||||
|
||||
const { account } = createAdminClient();
|
||||
|
||||
await account.create(ID.unique(), email, password, name);
|
||||
const session = await account.createEmailPasswordSession(email, password);
|
||||
const promise = account.createVerification("http://localhost:4321/verify");
|
||||
|
||||
promise.then(
|
||||
function (response) {
|
||||
console.log('VERI:' + response);
|
||||
},
|
||||
function (error) {
|
||||
console.log(error);
|
||||
},
|
||||
);
|
||||
|
||||
Astro.cookies.set(SESSION_COOKIE, session.secret, {
|
||||
path: "/",
|
||||
expires: new Date(session.expire),
|
||||
sameSite: "strict",
|
||||
secure: true,
|
||||
httpOnly: true,
|
||||
});
|
||||
|
||||
return Astro.redirect("/account");
|
||||
}
|
||||
---
|
||||
|
||||
<SignInUp Title="Demo Registeration" Type="SignUp"/>
|
|
@ -1,45 +0,0 @@
|
|||
import { Client, Account } from "node-appwrite";
|
||||
|
||||
export const SESSION_COOKIE = "session-token";
|
||||
|
||||
export function createAdminClient() {
|
||||
const client = new Client()
|
||||
.setEndpoint(import.meta.env.PUBLIC_APPWRITE_ENDPOINT)
|
||||
.setProject(import.meta.env.PUBLIC_APPWRITE_PROJECT_ID)
|
||||
.setKey(import.meta.env.APPWRITE_KEY);
|
||||
|
||||
return {
|
||||
get account() {
|
||||
return new Account(client);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createSessionClient(request: Request) {
|
||||
const client = new Client()
|
||||
.setEndpoint(import.meta.env.PUBLIC_APPWRITE_ENDPOINT)
|
||||
.setProject(import.meta.env.PUBLIC_APPWRITE_PROJECT_ID);
|
||||
|
||||
const cookies = parseCookies(request.headers.get("cookie") ?? "");
|
||||
const session = cookies.get(SESSION_COOKIE);
|
||||
if (!session) {
|
||||
throw new Error("No session");
|
||||
}
|
||||
|
||||
client.setSession(session);
|
||||
|
||||
return {
|
||||
get account() {
|
||||
return new Account(client);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function parseCookies(cookies: string): Map<string, string | null> {
|
||||
const map = new Map<string, string | null>();
|
||||
for (const cookie of cookies.split(";")) {
|
||||
const [name, value] = cookie.split("=");
|
||||
map.set(name.trim(), value ?? null);
|
||||
}
|
||||
return map;
|
||||
}
|
Reference in a new issue