Atproto AMA app
1import { randomBytes } from "node:crypto";
2import { and, eq, gt } from "drizzle-orm";
3
4import { db } from "~/lib/db";
5import { sessions } from "~/lib/schema";
6
7export const SESSION_COOKIE_NAME = "askimut_session";
8/** @deprecated use SESSION_COOKIE_NAME */
9export const SESSION_COOKIE = SESSION_COOKIE_NAME;
10
11const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000;
12
13export async function createSession(did: string): Promise<string> {
14 const id = randomBytes(32).toString("hex");
15 const expiresAt = new Date(Date.now() + THIRTY_DAYS_MS);
16
17 console.log("[createSession] Creating new session:", {
18 did,
19 sessionId: `${id.slice(0, 8)}...`,
20 expiresAt,
21 timestamp: new Date().toISOString()
22 });
23
24 await db.insert(sessions).values({ id, did, expiresAt });
25 return id;
26}
27
28export async function getSession(sessionId: string) {
29 console.log("[getSession] Looking up session:", {
30 sessionId: sessionId ? `${sessionId.slice(0, 8)}...` : null,
31 timestamp: new Date().toISOString()
32 });
33
34 const row = await db.query.sessions.findFirst({
35 where: and(eq(sessions.id, sessionId), gt(sessions.expiresAt, new Date())),
36 with: { user: true },
37 });
38
39 console.log("[getSession] Database result:", {
40 found: !!row,
41 hasUser: !!row?.user,
42 expired: row ? row.expiresAt < new Date() : null,
43 expiresAt: row?.expiresAt,
44 userHandle: row?.user?.handle,
45 timestamp: new Date().toISOString()
46 });
47
48 return row ?? null;
49}
50
51export async function deleteSession(sessionId: string): Promise<void> {
52 await db.delete(sessions).where(eq(sessions.id, sessionId));
53}
54
55export function buildSessionCookie(sessionId: string): string {
56 const maxAgeSec = Math.floor(THIRTY_DAYS_MS / 1000);
57 const secure = process.env.APP_URL?.startsWith("https://") ? "; Secure" : "";
58 return `${SESSION_COOKIE_NAME}=${sessionId}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${maxAgeSec}${secure}`;
59}
60
61export function parseSessionCookie(cookieHeader: string | null): string | undefined {
62 console.log("[parseSessionCookie] Parsing cookie header:", {
63 hasCookieHeader: !!cookieHeader,
64 cookieHeader: cookieHeader ? `${cookieHeader.slice(0, 50)}...` : null,
65 timestamp: new Date().toISOString()
66 });
67
68 if (!cookieHeader) return undefined;
69
70 for (const part of cookieHeader.split(";")) {
71 const [name, ...rest] = part.trim().split("=");
72 if (name === SESSION_COOKIE_NAME && rest.length > 0) {
73 const sessionId = decodeURIComponent(rest.join("=").trim());
74 console.log("[parseSessionCookie] Found session cookie:", {
75 sessionId: `${sessionId.slice(0, 8)}...`,
76 timestamp: new Date().toISOString()
77 });
78 return sessionId;
79 }
80 }
81
82 console.log("[parseSessionCookie] No session cookie found");
83 return undefined;
84}