71 lines · 3.2 KB
import { accountDb, repoDb } from "../scoped-db";
import type { RepoRow } from "~/lib/types";

export type OwnerVisibility = "all" | "private" | "selected";

type EncryptedRow = { name: string; ciphertext: string; iv: string };
type OwnerScoped = { visibility: OwnerVisibility; selected_repos: string };

/** Owner-level secrets and variables apply to all repos, only private repos, or an explicit allow-list. */
export function ownerScopeAllows(row: OwnerScoped, repo: Pick<RepoRow, "id" | "visibility">): boolean {
  if (row.visibility === "all") return true;
  if (row.visibility === "private") return repo.visibility === "private";
  const selected: unknown = JSON.parse(row.selected_repos);
  return Array.isArray(selected) && selected.includes(repo.id);
}

export async function environmentIdFor(repoId: string, name: string | null | undefined): Promise<string | null> {
  if (!name) return null;
  const row = await repoDb(repoId).first<{ id: string }>(
    "SELECT id FROM environments WHERE repo_id = ? AND name = ?",
    repoId,
    name,
  );
  return row?.id ?? null;
}

/** The `vars` context: environment variables override repository variables, which override owner variables. */
export async function loadVariables(repo: RepoRow, environmentId: string | null): Promise<Record<string, string>> {
  const db = repoDb(repo.id);
  const [owner, repoRows, envRows] = await Promise.all([
    accountDb(repo.owner_id).all<OwnerScoped & { name: string; value: string }>(
      "SELECT name, value, visibility, selected_repos FROM owner_variables WHERE owner_id = ?",
      repo.owner_id,
    ),
    db.all<{ name: string; value: string }>("SELECT name, value FROM repo_variables WHERE repo_id = ?", repo.id),
    environmentId
      ? db.all<{ name: string; value: string }>(
          "SELECT name, value FROM environment_variables WHERE environment_id = ?",
          environmentId,
        )