131 lines · 5.0 KB
import { env } from "cloudflare:workers";
import { batch, first, run, stmt } from "./db";
import { hashPassword, randomId } from "./crypto";
import { badRequest, conflict, forbidden, notFoundError } from "./errors";
import { canAdministerAccount } from "./repos";
import type { Viewer } from "~/lib/types";
import { isReservedName } from "~/lib/reserved-names";

/** Names that still redirect stay retired so old URLs and git remotes can't be taken over; only their owner may reclaim one. */
export async function accountNameTaken(name: string, reclaimingAccountId: string | null = null): Promise<boolean> {
  if (isReservedName(name)) return true;
  if ((await first<{ id: string }>("SELECT id FROM accounts WHERE name = ?", name)) !== null) return true;
  if ((await first("SELECT 1 AS ok FROM retired_account_names WHERE name = ? COLLATE NOCASE", name)) !== null) {
    return true;
  }
  const redirect = await first<{ account_id: string }>(
    "SELECT account_id FROM account_redirects WHERE old_name = ? COLLATE NOCASE",
    name,
  );
  return redirect !== null && redirect.account_id !== reclaimingAccountId;
}

export function signupsEnabled(): boolean {
  return env.SIGNUPS_ENABLED !== "false";
}

export function githubOAuthEnabled(): boolean {
  return Boolean(env.GITHUB_CLIENT_ID && env.GITHUB_CLIENT_SECRET);
}

export async function createUser(input: {
  name: string;
  email: string;
  password: string | null;
  githubId: string | null;
  displayName: string | null;
}): Promise<string> {
  if (await accountNameTaken(input.name)) throw conflict("That username is already taken");
  if (await first<{ account_id: string }>("SELECT account_id FROM users WHERE email = ?", input.email)) {
    throw conflict("An account with that email already exists");