394 lines · 14.9 KB
import { createServerFn } from "@tanstack/react-start";
import { z } from "zod";
import { requireViewer, viewerMiddleware } from "./middleware";
import { first } from "~/server/db";
import { accountDb, stmt } from "~/server/scoped-db";
import { forgetLocator, registerLocator } from "~/server/scoped-db/locators";
import { scopedId } from "~/server/scoped-db/ids";
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,
  visibleProjectIssues,
} from "~/server/projects";
import type { ProjectIssue } 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 db = accountDb(account.id);
    const projects = await db.all<{