Refactor admin procedures into separate files
Extract admin procedures from router.ts into dedicated files under procedures/admin/ with consolidated exports via _routes.ts. Adds shared helper functions for response transformation and includes race condition fixes via transaction-scoped existence checks. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
97
apps/api-server/src/procedures/admin/orgs/sites.ts
Normal file
97
apps/api-server/src/procedures/admin/orgs/sites.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* admin.orgs.listSites, admin.orgs.addSite, admin.orgs.removeSite
|
||||
* Site management for organizations
|
||||
*/
|
||||
|
||||
import { ORPCError } from "@orpc/server";
|
||||
import { authMiddleware, os, superuserMiddleware } from "../../base.js";
|
||||
import { toSiteResponse } from "../helpers.js";
|
||||
|
||||
export const adminOrgsListSites = os.admin.orgs.listSites
|
||||
.use(authMiddleware)
|
||||
.use(superuserMiddleware)
|
||||
.handler(async ({ input, context }) => {
|
||||
const { slug } = input;
|
||||
|
||||
const org = await context.db
|
||||
.selectFrom("orgs")
|
||||
.where("slug", "=", slug)
|
||||
.select(["id"])
|
||||
.executeTakeFirst();
|
||||
if (!org) {
|
||||
throw new ORPCError("NOT_FOUND", { message: "Organization not found" });
|
||||
}
|
||||
|
||||
const sites = await context.db
|
||||
.selectFrom("org_sites")
|
||||
.where("org_id", "=", org.id)
|
||||
.selectAll()
|
||||
.execute();
|
||||
|
||||
return sites.map(toSiteResponse);
|
||||
});
|
||||
|
||||
export const adminOrgsAddSite = os.admin.orgs.addSite
|
||||
.use(authMiddleware)
|
||||
.use(superuserMiddleware)
|
||||
.handler(async ({ input, context }) => {
|
||||
const { slug, domain } = input;
|
||||
|
||||
// Use transaction to prevent race condition on site creation
|
||||
await context.db.transaction().execute(async (trx) => {
|
||||
const org = await trx
|
||||
.selectFrom("orgs")
|
||||
.where("slug", "=", slug)
|
||||
.select(["id"])
|
||||
.executeTakeFirst();
|
||||
if (!org) {
|
||||
throw new ORPCError("NOT_FOUND", { message: "Organization not found" });
|
||||
}
|
||||
|
||||
// Check if site already exists (inside transaction)
|
||||
const existingSite = await trx
|
||||
.selectFrom("org_sites")
|
||||
.where("domain", "=", domain)
|
||||
.select(["id"])
|
||||
.executeTakeFirst();
|
||||
if (existingSite) {
|
||||
throw new ORPCError("CONFLICT", {
|
||||
message: "Site with this domain already exists",
|
||||
});
|
||||
}
|
||||
|
||||
await trx
|
||||
.insertInto("org_sites")
|
||||
.values({
|
||||
org_id: org.id,
|
||||
domain,
|
||||
})
|
||||
.execute();
|
||||
});
|
||||
});
|
||||
|
||||
export const adminOrgsRemoveSite = os.admin.orgs.removeSite
|
||||
.use(authMiddleware)
|
||||
.use(superuserMiddleware)
|
||||
.handler(async ({ input, context }) => {
|
||||
const { slug, domain } = input;
|
||||
|
||||
const org = await context.db
|
||||
.selectFrom("orgs")
|
||||
.where("slug", "=", slug)
|
||||
.select(["id"])
|
||||
.executeTakeFirst();
|
||||
if (!org) {
|
||||
throw new ORPCError("NOT_FOUND", { message: "Organization not found" });
|
||||
}
|
||||
|
||||
const result = await context.db
|
||||
.deleteFrom("org_sites")
|
||||
.where("org_id", "=", org.id)
|
||||
.where("domain", "=", domain)
|
||||
.executeTakeFirst();
|
||||
|
||||
if (!result.numDeletedRows || result.numDeletedRows === 0n) {
|
||||
throw new ORPCError("NOT_FOUND", { message: "Site not found" });
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user