1
Fork 0

Replace Appwrite with Supabase

This commit is contained in:
Korbs 2024-07-16 23:39:02 -04:00
parent 0ea0d8b73c
commit c2c1acf5c6
16 changed files with 71 additions and 390 deletions

View file

@ -1 +0,0 @@
<slot />

View file

@ -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
View 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,
},
},
);

View file

@ -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
View 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()
},
)

View file

@ -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");
};

View file

@ -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");
};

View file

@ -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");
};

View file

@ -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>

View file

@ -1,6 +0,0 @@
---
Astro.cookies.delete('session-token')
Astro.cookies.delete('anonymous')
---
<script is:inline>location.href = '/'</script>

View file

@ -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"/>

View file

@ -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;
}