mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-20 00:11:34 +00:00
Replace cookie-based sessions with SQLite-backed storage using better-sqlite3 and React Router's createSessionStorage. Sessions are now stored in ~/.arc/arc-web.db with a session ID cookie, enabling larger payloads and server-side revocation. - Add db.server.ts (lazy singleton, WAL mode, web_sessions table) - Add session-storage.server.ts (CRUD ops, probabilistic cleanup) - Fetch primary verified email from /user/emails during OAuth - Add emails:read to GitHub App manifest default_permissions - Expand session data: userUrl, githubId, githubNodeId, email - Default ARC_API_BASE_URL to localhost:3000 - Whitelist better-sqlite3 in trustedDependencies - Externalize better-sqlite3 from Vite SSR bundling Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
55 lines
1.5 KiB
TypeScript
55 lines
1.5 KiB
TypeScript
import { redirect } from "react-router";
|
|
import { createSqliteSessionStorage } from "./session-storage.server";
|
|
|
|
interface SessionData {
|
|
userUrl: string;
|
|
githubId: number;
|
|
githubNodeId: string;
|
|
githubLogin: string;
|
|
name: string;
|
|
email: string;
|
|
avatarUrl: string;
|
|
accessToken: string;
|
|
}
|
|
|
|
function getSessionStorage() {
|
|
const secret = process.env.SESSION_SECRET;
|
|
if (!secret) {
|
|
throw new Error("SESSION_SECRET is not set");
|
|
}
|
|
return createSqliteSessionStorage(secret);
|
|
}
|
|
|
|
export async function getSession(request: Request) {
|
|
const storage = getSessionStorage();
|
|
return storage.getSession(request.headers.get("Cookie"));
|
|
}
|
|
|
|
export async function commitSession(session: Awaited<ReturnType<typeof getSession>>) {
|
|
const storage = getSessionStorage();
|
|
return storage.commitSession(session);
|
|
}
|
|
|
|
export async function destroySession(session: Awaited<ReturnType<typeof getSession>>) {
|
|
const storage = getSessionStorage();
|
|
return storage.destroySession(session);
|
|
}
|
|
|
|
export async function getUser(request: Request) {
|
|
const session = await getSession(request);
|
|
const githubLogin = session.get("githubLogin");
|
|
if (!githubLogin) return null;
|
|
return {
|
|
userUrl: session.get("userUrl") ?? "",
|
|
githubLogin,
|
|
name: session.get("name") ?? githubLogin,
|
|
email: session.get("email") ?? "",
|
|
avatarUrl: session.get("avatarUrl") ?? "",
|
|
};
|
|
}
|
|
|
|
export async function requireUser(request: Request) {
|
|
const user = await getUser(request);
|
|
if (!user) throw redirect("/auth/login");
|
|
return user;
|
|
}
|