- 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>
102 lines
2.7 KiB
TypeScript
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 };
|
|
});
|