124 lines · 5.1 KB
import { createServerFn } from "@tanstack/react-start";
import { z } from "zod";
import { requireViewer, viewerMiddleware } from "./middleware";
import { repoDb } from "~/server/scoped-db";
import { requireRepoAccess } from "~/server/repos";
import { accountOrGhost, accountsById } from "~/server/people";
import { reviewDeployment as decideDeployment } from "~/server/ci/deployments";
import type { DeploymentRow } from "~/server/ci/deployments";
import { cacheQuotaBytes, deleteCacheEntry } from "~/server/ci/cache-api";
import { forbidden } from "~/server/errors";
import { limitBrowse, limitWrite } from "~/server/limits";
import { repoRef } from "~/lib/validation";

const DEPLOYMENT_PAGE = 50;
const CACHE_PAGE = 100;

export const listDeployments = createServerFn({ method: "GET" })
  .middleware([viewerMiddleware])
  .validator(repoRef.extend({ environment: z.string().optional() }))
  .handler(async ({ data, context }) => {
    await limitBrowse(context.viewer);
    const { repo } = await requireRepoAccess(data.owner, data.repo, context.viewer, "read");
    const environments = await repoDb(repo.id).all<{ id: string; name: string }>(
      "SELECT id, name FROM environments WHERE repo_id = ? ORDER BY name",
      repo.id,
    );
    const selected = environments.find((environment) => environment.name === data.environment) ?? null;
    const rows = await repoDb(repo.id).all<
      DeploymentRow & { environment_name: string; run_number: number; workflow_name: string }
    >(
      `SELECT d.*, e.name AS environment_name, w.run_number, w.workflow_name FROM deployments d
       JOIN environments e ON e.id = d.environment_id JOIN workflow_runs w ON w.id = d.run_id
       WHERE d.repo_id = ? ${selected ? "AND d.environment_id = ?" : ""} ORDER BY d.created_at DESC LIMIT ?`,
      ...(selected ? [repo.id, selected.id, DEPLOYMENT_PAGE] : [repo.id, DEPLOYMENT_PAGE]),
    );
    const people = await accountsById(rows.flatMap((row) => [row.creator_id, row.reviewed_by]));
    const latest = new Map<string, DeploymentRow>();
    for (const row of rows)
      if (!latest.has(row.environment_id) && row.status === "success") latest.set(row.environment_id, row);
    return {