/** * Basic org procedures - list, create, get */ import { ORPCError } from "@orpc/server"; import { authedProcedure } from "../base.js"; import { getMembership, lookupOrgBySlug } from "./helpers.js"; /** * List all orgs the current user is a member of */ export const orgsList = authedProcedure.orgs.list.handler( async ({ context }) => { const orgs = await context.db .selectFrom("org_members") .innerJoin("orgs", "orgs.id", "org_members.org_id") .where("org_members.user_id", "=", context.user.id) .select([ "orgs.id", "orgs.slug", "orgs.display_name", "orgs.logo_url", "orgs.created_at", ]) .orderBy("orgs.created_at", "desc") .execute(); return orgs.map((o) => ({ id: o.id, slug: o.slug, displayName: o.display_name, logoUrl: o.logo_url, createdAt: o.created_at, })); }, ); /** * Create a new org * The creating user becomes the owner */ export const orgsCreate = authedProcedure.orgs.create.handler( async ({ input, context }) => { const { slug, displayName } = input; try { await context.db.transaction().execute(async (trx) => { // Create the org const org = await trx .insertInto("orgs") .values({ slug, display_name: displayName, }) .returning(["id"]) .executeTakeFirstOrThrow(); // Add the creating user as owner await trx .insertInto("org_members") .values({ org_id: org.id, user_id: context.user.id, role: "owner", }) .execute(); }); return { slug }; } catch (error) { // Handle unique constraint violation on slug if (error instanceof Error && error.message.includes("orgs_slug_key")) { throw new ORPCError("CONFLICT", { message: "Slug already in use" }); } throw error; } }, ); /** * Get a single org by slug * Requires membership */ export const orgsGet = authedProcedure.orgs.get.handler( async ({ input, context }) => { const { slug } = input; // Lookup org and verify membership const org = await lookupOrgBySlug(context.db, slug); await getMembership(context.db, org.id, context.user.id); return { id: org.id, slug: org.slug, displayName: org.displayName, logoUrl: org.logoUrl, createdAt: org.createdAt, }; }, );