Soft-delete cascade was the missing half of archive: stamping workspaces.archived_at alone left objects visible to anyone with a direct id. The cascade runs in one transaction so the partial state isn't reachable, and restore inverts it for any archived row in the workspace — provenance-blind on purpose until we have a use case that needs to distinguish per-workspace from per-object archives. audit_log keeps the keyset index on (workspace_id, created_at) and the actor_user_id FK with onDelete set null. recordAudit() refuses to write a null actor without a metadata.system_actor label so the audit view always has something to render. workspaces and invites mutations call recordAudit on success; objects-router instrumentation and the markdown importer's system-actor flow are filed as P2 follow-ups because each needs a thoughtful "what's audit-worthy?" pass, not mechanical wiring. Settings → Audit log lives at /<slug>/settings/audit, owner-gated, keyset-paginated. ACTION_LABELS is small on purpose; new actions fall back to their raw key so missing a label degrades gracefully. Co-authored-by: Cursor <cursoragent@cursor.com>
36 lines
1.3 KiB
TypeScript
36 lines
1.3 KiB
TypeScript
import { router, createCallerFactory } from "@/server/trpc";
|
|
import { healthRouter } from "@/server/routers/health";
|
|
import { objectsRouter } from "@/server/routers/objects";
|
|
import { propertiesRouter } from "@/server/routers/properties";
|
|
import { relationsRouter } from "@/server/routers/relations";
|
|
import { aiRouter } from "@/server/routers/ai";
|
|
import { templatesRouter } from "@/server/routers/templates";
|
|
import { searchRouter } from "@/server/routers/search";
|
|
import { workspacesRouter } from "@/server/routers/workspaces";
|
|
import { typesRouter } from "@/server/routers/types";
|
|
import { formsRouter } from "@/server/routers/forms";
|
|
import { favoritesRouter } from "@/server/routers/favorites";
|
|
import { identityRouter } from "@/server/routers/identity";
|
|
import { invitesRouter } from "@/server/routers/invites";
|
|
import { auditRouter } from "@/server/routers/audit";
|
|
|
|
export const appRouter = router({
|
|
health: healthRouter,
|
|
workspaces: workspacesRouter,
|
|
types: typesRouter,
|
|
objects: objectsRouter,
|
|
properties: propertiesRouter,
|
|
relations: relationsRouter,
|
|
ai: aiRouter,
|
|
templates: templatesRouter,
|
|
search: searchRouter,
|
|
forms: formsRouter,
|
|
favorites: favoritesRouter,
|
|
identity: identityRouter,
|
|
invites: invitesRouter,
|
|
audit: auditRouter,
|
|
});
|
|
|
|
export type AppRouter = typeof appRouter;
|
|
|
|
export const createCaller = createCallerFactory(appRouter);
|