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 .executeTakeFirst();
28 if (!session) return null;
29 return {
30 id: session.id,
31 username: session.username,
32 isAdmin: session.username === ADMIN_USERNAME,
33 avatar_version: session.avatar_version,
34 };
35}
36
37export function requireAuth(user: SessionUser | null): Response | null {
38 if (!user) {
39 return new Response(null, {
40 status: 302,
41 headers: { Location: "/login" },
42 });
43 }
44 return null;
45}
46
47export function requireAdmin(user: SessionUser | null): Response | null {
48 if (!user) {
49 return new Response(null, {
50 status: 302,
51 headers: { Location: "/login" },
52 });
53 }
54 if (!user.isAdmin) {
55 return new Response("Forbidden", {
56 status: 403,
57 headers: { "Content-Type": "text/plain; charset=utf-8" },
58 });
59 }
60 return null;
61}
62