import { first, run } from "./db";
import { hashPassword, randomToken, verifyPassword } from "./crypto";
import { consumeAuthToken, revokeUserAuthTokens, storeAuthToken } from "./auth-tokens";
import { sessionCreatedAt } from "./auth";
import { enforceRateLimit } from "./limits";
import { emailTemplates, queueEmail } from "./email";
import { publicUrl } from "./events/emit";
import { badRequest, conflict } from "./errors";
import { invalidateUserSignatures } from "./security/signatures";
const VERIFY_TTL_SECONDS = 60 * 60 * 24;
const RESET_TTL_SECONDS = 60 * 60;
type VerifyPayload = { email: string };
type ResetPayload = { passwordHash: string | null };
function parseVerify(raw: string): VerifyPayload {
const parsed: unknown = JSON.parse(raw);
const email = typeof parsed === "object" && parsed !== null && "email" in parsed ? parsed.email : null;
if (typeof email !== "string") throw badRequest("This verification link is invalid");
return { email };
}
function parseReset(raw: string): ResetPayload {
const parsed: unknown = JSON.parse(raw);
const hash = typeof parsed === "object" && parsed !== null && "passwordHash" in parsed ? parsed.passwordHash : null;
return { passwordHash: typeof hash === "string" ? hash : null };
}
async function displayNameFor(userId: string): Promise<string> {
return (await first<{ name: string }>("SELECT name FROM accounts WHERE id = ?", userId))?.name ?? "there";
}
/** Sends a single-use verification link; confirming it sets (or changes to) this address. */
export async function sendEmailVerification(userId: string, email: string): Promise<void> {
const token = randomToken("verify");
await revokeUserAuthTokens("email-verify", userId);
const payload: VerifyPayload = { email };
await storeAuthToken("email-verify", token, userId, JSON.stringify(payload), VERIFY_TTL_SECONDS);
const url = publicUrl(`/verify-email?token=${encodeURIComponent(token)}`);