Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history

auth.ts

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

auth.tsBlame104 lines · 1 contributor
06d5ffeClaude1/**
2 * Auth middleware — reads session cookie, injects user into context.
05b973eClaude3 * Uses in-memory session cache to avoid DB roundtrip on every request.
06d5ffeClaude4 */
5
6import { createMiddleware } from "hono/factory";
7import { getCookie } from "hono/cookie";
8import { eq, gt } from "drizzle-orm";
9import { db } from "../db";
10import { sessions, users } from "../db/schema";
11import type { User } from "../db/schema";
05b973eClaude12import { sessionCache } from "../lib/cache";
06d5ffeClaude13
14export type AuthEnv = {
15 Variables: {
16 user: User | null;
17 };
18};
19
20/**
21 * Soft auth — sets c.get("user") to the current user or null.
22 * Does NOT block unauthenticated requests.
05b973eClaude23 * Caches session->user mapping for 2 minutes to avoid DB roundtrip per request.
06d5ffeClaude24 */
25export const softAuth = createMiddleware<AuthEnv>(async (c, next) => {
26 const token = getCookie(c, "session");
27 if (!token) {
28 c.set("user", null);
29 return next();
30 }
31
05b973eClaude32 // Check session cache first
33 const cachedUser = sessionCache.get(token) as User | null | undefined;
34 if (cachedUser !== undefined) {
35 c.set("user", cachedUser);
36 return next();
37 }
38
06d5ffeClaude39 try {
40 const [session] = await db
41 .select()
42 .from(sessions)
43 .where(eq(sessions.token, token))
44 .limit(1);
45
46 if (!session || new Date(session.expiresAt) < new Date()) {
05b973eClaude47 sessionCache.set(token, null as any);
06d5ffeClaude48 c.set("user", null);
49 return next();
50 }
51
52 const [user] = await db
53 .select()
54 .from(users)
55 .where(eq(users.id, session.userId))
56 .limit(1);
57
05b973eClaude58 // Cache the result (user or null)
59 sessionCache.set(token, (user || null) as any);
06d5ffeClaude60 c.set("user", user || null);
61 } catch {
62 c.set("user", null);
63 }
64
65 return next();
66});
67
68/**
69 * Hard auth — redirects to /login if not authenticated.
70 */
71export const requireAuth = createMiddleware<AuthEnv>(async (c, next) => {
72 const token = getCookie(c, "session");
73 if (!token) {
74 return c.redirect(`/login?redirect=${encodeURIComponent(c.req.path)}`);
75 }
76
77 try {
78 const [session] = await db
79 .select()
80 .from(sessions)
81 .where(eq(sessions.token, token))
82 .limit(1);
83
84 if (!session || new Date(session.expiresAt) < new Date()) {
85 return c.redirect(`/login?redirect=${encodeURIComponent(c.req.path)}`);
86 }
87
88 const [user] = await db
89 .select()
90 .from(users)
91 .where(eq(users.id, session.userId))
92 .limit(1);
93
94 if (!user) {
95 return c.redirect(`/login?redirect=${encodeURIComponent(c.req.path)}`);
96 }
97
98 c.set("user", user);
99 } catch {
100 return c.redirect(`/login?redirect=${encodeURIComponent(c.req.path)}`);
101 }
102
103 return next();
104});