360 lines · 13.4 KB
import { createServerFn } from "@tanstack/react-start";
import { z } from "zod";
import { requireViewer, viewerMiddleware } from "./middleware";
import { all, batch, first, run, stmt } from "~/server/db";
import { randomId } from "~/server/crypto";
import { limitWrite } from "~/server/limits";
import { canCreateInAccount, requireRepoAccess } from "~/server/repos";
import { loadIssue } from "~/server/issues";
import { assertCount, PLANNING_CAPS } from "~/server/planning";
import {
  loadProjectAccess,
  notifyProject,
  parseOptions,
  projectFields,
  requireProjectWrite,
  visibleItemClause,
} from "~/server/projects";
import { badRequest, notFoundError } from "~/server/errors";
import { accountName } from "~/lib/validation";

const projectRef = z.object({ owner: accountName, number: z.number().int().positive() });
const DEFAULT_STATUS = ["Todo", "In progress", "Done"];
const ITEM_REF = /^([^/\s]+)\/([^#\s]+)#(\d+)$/;

export const listProjects = createServerFn({ method: "GET" })
  .middleware([viewerMiddleware])
  .validator(z.object({ owner: accountName, closed: z.boolean().default(false) }))
  .handler(async ({ data, context }) => {
    const account = await first<{ id: string; name: string }>(
      "SELECT id, name FROM accounts WHERE name = ?",
      data.owner,
    );
    if (!account) throw notFoundError("Account not found");
    const canCreate = await canCreateInAccount(account.id, context.viewer);
    const { clause, viewerId } = visibleItemClause(context.viewer);
    const rows = await all<{
      number: number;
      title: string;
      description: string;
      visibility: string;