130 lines · 5.0 KB
import { all, first, MAX_IN_LIST, placeholders, run } from "../db";
import { encryptRepoSecret } from "../secrets";
import { assertBelowCap, CAPS } from "../limits";
import { canAdministerAccount } from "../repos";
import { notFoundError, forbidden } from "../errors";
import type { OwnerVisibility } from "./config-values";
import type { Viewer } from "~/lib/types";

export type ConfigScope =
  | { kind: "repo"; id: string }
  | { kind: "environment"; id: string }
  | { kind: "owner"; id: string; visibility: OwnerVisibility; selectedRepos: string[] };

const TABLES = {
  variable: {
    repo: ["repo_variables", "repo_id"],
    environment: ["environment_variables", "environment_id"],
    owner: ["owner_variables", "owner_id"],
  },
  secret: {
    repo: ["secrets", "repo_id"],
    environment: ["environment_secrets", "environment_id"],
    owner: ["owner_secrets", "owner_id"],
  },
} as const;

type Kind = keyof typeof TABLES;

function table(kind: Kind, scope: ConfigScope): readonly [string, string] {
  return TABLES[kind][scope.kind];
}

async function assertRoom(kind: Kind, scope: ConfigScope, name: string): Promise<void> {
  const [target, column] = table(kind, scope);
  if (await first(`SELECT 1 AS ok FROM ${target} WHERE ${column} = ? AND name = ?`, scope.id, name)) return;
  const count = await first<{ count: number }>(`SELECT COUNT(*) AS count FROM ${target} WHERE ${column} = ?`, scope.id);
  assertBelowCap(count?.count ?? 0, kind === "secret" ? CAPS.secretsPerRepo : CAPS.variablesPerScope, `${kind}s`);
}

function ownerColumns(scope: ConfigScope): { names: string; values: string; params: string[] } {