85 lines · 3.5 KB
import { z } from "zod";
import { all, first, run } from "../db";
import { createOrg } from "../accounts";
import { findCollaboratorUser, setCollaborator } from "../collaborators";
import { badRequest, forbidden, notFoundError } from "../errors";
import { limitWrite } from "../limits";
import { canAdministerAccount } from "../repos";
import { accountName } from "~/lib/validation";
import { noContent, readJson, requireAccountScope, requireScopedRepo } from "./http";
import type { ApiContext } from "./http";

async function loadOrg(name: string): Promise<{ id: string; name: string }> {
  const org = await first<{ id: string; name: string }>(
    "SELECT id, name FROM accounts WHERE name = ? AND kind = 'org'",
    name,
  );
  if (!org) throw notFoundError("Organization not found");
  return org;
}

const orgInput = z.object({ name: accountName, display_name: z.string().max(100).nullable().default(null) });

/** Mirrors fn:account.createOrganization. */
export async function createOrgApi(ctx: ApiContext): Promise<Response> {
  requireAccountScope(ctx);
  const data = await readJson(ctx.request, orgInput);
  await limitWrite(ctx.viewer);
  await createOrg(data.name, data.display_name, ctx.viewer.id);
  return Response.json({ name: data.name }, { status: 201 });
}

/** Membership is public on the org profile page, so any token may read it. */
export async function listOrgMembers(_ctx: ApiContext, orgName: string): Promise<Response> {
  const org = await loadOrg(orgName);
  const members = await all<{ name: string; role: string }>(
    "SELECT a.name, m.role FROM org_members m JOIN accounts a ON a.id = m.user_id WHERE m.org_id = ? ORDER BY a.name",
    org.id,
  );
  return Response.json(members.map((member) => ({ login: member.name, role: member.role })));
}