import { env, waitUntil } from "cloudflare:workers";
import { deleteCookie, getCookie, getRequestHeader, setCookie } from "@tanstack/react-start/server";
import { randomId, sha256Hex, toHex } from "./crypto";
import { accountDb } from "./scoped-db";
import { forgetLocator, locate, registerLocator } from "./scoped-db/locators";
import { accountToken, embeddedAccountId } from "./account-scope";
import type { UserRow, Viewer } from "~/lib/types";
import { noteSignIn } from "./notify/sign-in";
import { emailConfigured } from "./email";
import { accountDisableState, assertAccountActive, disabledNotice } from "./account-disable";
import type { DisabledNotice } from "~/lib/account-disable";
import { loadActiveUserCached } from "./user-row";
/** Sliding idle timeout (AUTH-A12); expired and idle sessions are deleted by the account's housekeeping alarm. */
import { SESSION_IDLE_MS } from "./account-state/housekeeping";
const SESSION_COOKIE_BASE = "gitfor_session";
const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "[::1]"]);
/** `__Host-` (Secure, Path=/, no Domain) everywhere browsers accept Secure cookies; a plain-http dev host gets neither. */
export function cookieSecure(): boolean {
const url = new URL(env.PUBLIC_URL);
return url.protocol === "https:" || LOOPBACK_HOSTS.has(url.hostname);
}
export function cookieName(base: string): string {
return cookieSecure() ? `__Host-${base}` : base;
}
/** Attributes for hand-built Set-Cookie headers. */
export function cookieAttributes(maxAge: number): string {
return `Path=/; HttpOnly;${cookieSecure() ? " Secure;" : ""} SameSite=Lax; Max-Age=${maxAge}`;
}
/** Absolute lifetime of a browser session; the cookie carries the same Max-Age. */
const SESSION_TTL_SECONDS = 60 * 60 * 24 * 30;
/** `last_seen_at` is written at most this often so ordinary browsing does not cost a D1 write per request. */
const SESSION_TOUCH_MS = 5 * 60 * 1000;
const MAX_USER_AGENT = 200;
export const PAT_PREFIX = "gitfor_pat";
type SessionRow = {