Files
publisher-dashboard/apps/api-server/src/procedures/admin/orgs/sites.ts
RevIQ 1bf05465c3 Replace void returns with { success: true } across all API endpoints
- Add successResponseSchema to common.ts for explicit success responses
- Update all auth, me, orgs, and admin procedures to return { success: true }
- Update contract.ts to use successResponseSchema instead of z.void()
- Add ast-grep rule to prevent future z.void() usage in contracts
- Add build:packages script to root package.json
- Fix test file lint errors with eslint-disable comments

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 16:30:22 +08:00

102 lines
2.7 KiB
TypeScript

/**
* 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();
});
return { success: true };
});
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" });
}
return { success: true };
});