session.ts
Raw
1import { ADMIN_USERNAME } from "../constants.ts";
2import { db } from "../db";
3
4export interface SessionUser {
5 id: number;
6 username: string;
7 isAdmin: boolean;
8 avatar_version: number;
9}
10
11export async function resolveSession(
12 cookie: string | undefined,
13): Promise<SessionUser | null> {
14 if (!cookie) return null;
15 const now = new Date().toISOString();
16 const session = await db
17 .selectFrom("sessions")
18 .innerJoin("users", "users.id", "sessions.user_id")
19 .select([
20 "users.id",
21 "users.username",
22 "users.avatar_version",
23 "sessions.expires_at",
24 ])
25 .where("sessions.id", "=", cookie)
26 .where("sessions.expires_at", ">", now)
27 .where("users.is_pending", "=", 0)
28 .executeTakeFirst();
29 if (!session) return null;
30 return {
31 id: session.id,
32 username: session.username,
33 isAdmin: session.username === ADMIN_USERNAME,
34 avatar_version: session.avatar_version,
35 };
36}
37
38export function requireAuth(user: SessionUser | null): Response | null {
39 if (!user) {
40 return new Response(null, {
41 status: 302,
42 headers: { Location: "/login" },
43 });
44 }
45 return null;
46}
47
48export function requireAdmin(user: SessionUser | null): Response | null {
49 if (!user) {
50 return new Response(null, {
51 status: 302,
52 headers: { Location: "/login" },
53 });
54 }
55 if (!user.isAdmin) {
56 return new Response("Forbidden", {
57 status: 403,
58 headers: { "Content-Type": "text/plain; charset=utf-8" },
59 });
60 }
61 return null;
62}
63