- 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>
101 lines
2.4 KiB
TypeScript
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,
|
|
};
|
|
},
|
|
);
|