123 lines · 4.4 KB
import { env } from "cloudflare:workers";
import { deleteCookie, getCookie, setCookie } from "@tanstack/react-start/server";
import { first } from "./db";
import { randomToken, sha256Hex } from "./crypto";
import type { UserRow, Viewer } from "~/lib/types";

const SESSION_COOKIE = "stig_session";
const SESSION_TTL_SECONDS = 60 * 60 * 24 * 30;
export const PAT_PREFIX = "stig_pat";

type SessionRecord = { userId: string; createdAt: number };

function sessionKey(tokenHash: string): string {
  return `session:${tokenHash}`;
}

export function toViewer(user: UserRow): Viewer {
  return {
    id: user.id,
    name: user.name,
    displayName: user.display_name,
    email: user.email,
    isAdmin: user.is_admin === 1,
  };
}

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 loadUserByLogin(login: string): Promise<UserRow | null> {
  return first<UserRow>(`${USER_SELECT} WHERE a.name = ? OR u.email = ?`, login, login);
}

async function issueSessionToken(userId: string): Promise<string> {
  const token = randomToken("sess");