import { env } from "cloudflare:workers"; import { first } from "./db"; import type { UserRow } from "~/lib/types"; const USER_SELECT = `SELECT a.*, u.email, u.password_hash, u.github_id, u.is_admin, u.email_verified_at, u.totp_enabled_at, u.sessions_revoked_at FROM accounts a JOIN users u ON u.account_id = a.id`; export async function loadUserById(id: string): Promise { return first(`${USER_SELECT} WHERE a.id = ?`, id); } export async function loadActiveUserById(id: string): Promise { return first(`${USER_SELECT} WHERE a.id = ? AND a.disabled_at IS NULL`, id); } export async function loadUserByLogin(login: string): Promise { return first(`${USER_SELECT} WHERE a.name = ? OR u.email = ?`, login, login); } /** * The per-request viewer lookup, answered by the user's AccountState from a short-lived copy of the directory * row instead of a D1 read per page view. Writers that change the row call `userChanged`. */ export async function loadActiveUserCached(id: string): Promise { try { return (await env.ACCOUNT_STATE.get(env.ACCOUNT_STATE.idFromName(id)).cachedUser(id)) as UserRow | null; } catch (error) { console.error("Cached user lookup failed; reading the directory", id, error); return loadActiveUserById(id); } } /** Drops the cached directory row after a write to `users` / `accounts` that the viewer must see at once. */ export async function userChanged(id: string): Promise { try { await env.ACCOUNT_STATE.get(env.ACCOUNT_STATE.idFromName(id)).forgetUser(); } catch (error) { console.error("User cache invalidation failed", id, error); } }