Files
publisher-dashboard/apps/api-server/src/procedures/orgs/basic.ts
igm 73ef3df01f Add pre-configured procedures and use them throughout codebase
- Add authedProcedure, superuserProcedure, loginRequestProcedure,
  orgMemberProcedure in base.ts
- Create procedures/me/_base.ts with meRoute = authedProcedure.me
- Update all me procedures to use meRoute.X.handler()
- Update auth/logout and auth/resend-verification to use authedProcedure
- Update all admin procedures to use superuserProcedure
- Update all orgs procedures to use authedProcedure

This reduces boilerplate and makes middleware usage consistent.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 17:57:15 +08:00

101 lines
2.4 KiB
TypeScript

/**
* 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,
};
},
);