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<UserRow | null> {
return first<UserRow>(`${USER_SELECT} WHERE a.id = ?`, id);
}
export async function loadActiveUserById(id: string): Promise<UserRow | null> {
return first<UserRow>(`${USER_SELECT} WHERE a.id = ? AND a.disabled_at IS NULL`, id);
}
export async function loadUserByLogin(login: string): Promise<UserRow | null> {
return first<UserRow>(`${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<UserRow | null> {
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<void> {
try {
await env.ACCOUNT_STATE.get(env.ACCOUNT_STATE.idFromName(id)).forgetUser();
} catch (error) {
console.error("User cache invalidation failed", id, error);
}