@shipfox/api-workspaces 10.1.0 → 10.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +19 -0
  3. package/README.md +7 -1
  4. package/dist/core/admin-workspaces.d.ts +16 -0
  5. package/dist/core/admin-workspaces.d.ts.map +1 -1
  6. package/dist/core/admin-workspaces.js +52 -0
  7. package/dist/core/admin-workspaces.js.map +1 -1
  8. package/dist/core/errors.d.ts +12 -0
  9. package/dist/core/errors.d.ts.map +1 -1
  10. package/dist/core/errors.js +24 -0
  11. package/dist/core/errors.js.map +1 -1
  12. package/dist/core/index.d.ts +2 -2
  13. package/dist/core/index.d.ts.map +1 -1
  14. package/dist/core/index.js +2 -2
  15. package/dist/core/index.js.map +1 -1
  16. package/dist/db/admin-workspace-commands.d.ts +17 -0
  17. package/dist/db/admin-workspace-commands.d.ts.map +1 -0
  18. package/dist/db/admin-workspace-commands.js +86 -0
  19. package/dist/db/admin-workspace-commands.js.map +1 -0
  20. package/dist/db/db.d.ts +256 -0
  21. package/dist/db/db.d.ts.map +1 -1
  22. package/dist/db/db.js +2 -0
  23. package/dist/db/db.js.map +1 -1
  24. package/dist/db/index.d.ts +1 -0
  25. package/dist/db/index.d.ts.map +1 -1
  26. package/dist/db/index.js +1 -0
  27. package/dist/db/index.js.map +1 -1
  28. package/dist/db/schema/admin-command-results.d.ts +135 -0
  29. package/dist/db/schema/admin-command-results.d.ts.map +1 -0
  30. package/dist/db/schema/admin-command-results.js +18 -0
  31. package/dist/db/schema/admin-command-results.js.map +1 -0
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +7 -2
  34. package/dist/index.js.map +1 -1
  35. package/dist/presentation/routes/admin-workspaces.d.ts.map +1 -1
  36. package/dist/presentation/routes/admin-workspaces.js +119 -14
  37. package/dist/presentation/routes/admin-workspaces.js.map +1 -1
  38. package/dist/tsconfig.test.tsbuildinfo +1 -1
  39. package/drizzle/0002_wide_enchantress.sql +11 -0
  40. package/drizzle/meta/0002_snapshot.json +555 -0
  41. package/drizzle/meta/_journal.json +7 -0
  42. package/package.json +5 -5
  43. package/src/core/admin-workspaces.ts +102 -0
  44. package/src/core/errors.ts +28 -0
  45. package/src/core/index.ts +8 -0
  46. package/src/db/admin-workspace-commands.ts +169 -0
  47. package/src/db/db.ts +2 -0
  48. package/src/db/index.ts +1 -0
  49. package/src/db/schema/admin-command-results.ts +30 -0
  50. package/src/index.test.ts +3 -1
  51. package/src/index.ts +17 -2
  52. package/src/presentation/routes/admin-workspaces.test.ts +273 -3
  53. package/src/presentation/routes/admin-workspaces.ts +143 -13
  54. package/test/globalSetup.ts +3 -1
  55. package/tsconfig.build.tsbuildinfo +1 -1
  56. package/vitest.config.ts +3 -0
@@ -1,10 +1,82 @@
1
1
  import { AUTH_USER, getUserContext } from '@shipfox/api-auth-context';
2
2
  import { authInterModuleContract } from '@shipfox/api-auth-dto/inter-module';
3
- import { listWorkspaceAdminSummariesResponseSchema, workspaceAdminLookupQuerySchema } from '@shipfox/api-workspaces-dto';
3
+ import { listWorkspaceAdminSummariesResponseSchema, workspaceAdministrationMutationBodySchema, workspaceAdministrationMutationResponseSchema, workspaceAdminLookupQuerySchema } from '@shipfox/api-workspaces-dto';
4
4
  import { isInterModuleKnownError } from '@shipfox/inter-module';
5
5
  import { decodeStringIdCursor, encodeStringIdCursor } from '@shipfox/node-drizzle';
6
6
  import { ClientError, defineRoute } from '@shipfox/node-fastify';
7
- import { listWorkspaceAdministratorSummaries } from '#core/admin-workspaces.js';
7
+ import { z } from 'zod';
8
+ import { listWorkspaceAdministratorSummaries, reactivateWorkspace, suspendWorkspace } from '#core/admin-workspaces.js';
9
+ import { WorkspaceAdminIdempotencyKeyReuseError, WorkspaceAlreadySuspendedError, WorkspaceDeletedError, WorkspaceNotFoundError, WorkspaceNotSuspendedError } from '#core/errors.js';
10
+ const idempotencyKeyMaxLength = 256;
11
+ function requireActorId(request) {
12
+ const client = getUserContext(request);
13
+ if (!client) {
14
+ throw new ClientError('Authentication required', 'unauthorized', {
15
+ status: 401
16
+ });
17
+ }
18
+ return client.userId;
19
+ }
20
+ function requireIdempotencyKey(request) {
21
+ const value = request.headers['idempotency-key'];
22
+ const key = Array.isArray(value) ? value[0] : value;
23
+ if (!key || key.trim().length === 0 || key.length > idempotencyKeyMaxLength) {
24
+ throw new ClientError('Idempotency-Key header is required', 'idempotency-key-required', {
25
+ status: 400
26
+ });
27
+ }
28
+ return key;
29
+ }
30
+ function translateAdminRoleError(error, message) {
31
+ if (isInterModuleKnownError(authInterModuleContract.methods.requireAdminRole, error) && error.code === 'admin-role-required') {
32
+ throw new ClientError(message, 'forbidden', {
33
+ status: 403,
34
+ details: {
35
+ required_role: error.details.requiredRole
36
+ }
37
+ });
38
+ }
39
+ }
40
+ function translateAdministrationError(error) {
41
+ translateAdminRoleError(error, 'Administrator operator role required');
42
+ if (error instanceof WorkspaceNotFoundError) {
43
+ throw new ClientError('Workspace not found', 'workspace-not-found', {
44
+ status: 404
45
+ });
46
+ }
47
+ if (error instanceof WorkspaceDeletedError) {
48
+ throw new ClientError('Workspace is deleted', 'workspace-deleted', {
49
+ status: 409
50
+ });
51
+ }
52
+ if (error instanceof WorkspaceAlreadySuspendedError) {
53
+ throw new ClientError('Workspace is already suspended', 'workspace-already-suspended', {
54
+ status: 409
55
+ });
56
+ }
57
+ if (error instanceof WorkspaceNotSuspendedError) {
58
+ throw new ClientError('Workspace is not suspended', 'workspace-not-suspended', {
59
+ status: 409
60
+ });
61
+ }
62
+ if (error instanceof WorkspaceAdminIdempotencyKeyReuseError) {
63
+ throw new ClientError('Idempotency-Key was already used for a different workspace command', 'idempotency-key-reused', {
64
+ status: 409
65
+ });
66
+ }
67
+ throw error;
68
+ }
69
+ function translateObserverAdministrationError(error) {
70
+ translateAdminRoleError(error, 'Administrator observer role required');
71
+ throw error;
72
+ }
73
+ function toWorkspaceAdministrationMutationDto(result) {
74
+ return {
75
+ workspace_id: result.workspaceId,
76
+ status: result.status,
77
+ correlation_id: result.correlationId
78
+ };
79
+ }
8
80
  function toWorkspaceAdministratorSummaryDto(summary) {
9
81
  return {
10
82
  id: summary.id,
@@ -29,17 +101,7 @@ export function createAdminWorkspacesRoutes(params) {
29
101
  200: listWorkspaceAdminSummariesResponseSchema
30
102
  }
31
103
  },
32
- errorHandler: (error)=>{
33
- if (isInterModuleKnownError(authInterModuleContract.methods.requireAdminRole, error) && error.code === 'admin-role-required') {
34
- throw new ClientError('Administrator observer role required', 'forbidden', {
35
- status: 403,
36
- details: {
37
- required_role: error.details.requiredRole
38
- }
39
- });
40
- }
41
- throw error;
42
- },
104
+ errorHandler: translateObserverAdministrationError,
43
105
  handler: async (request)=>{
44
106
  const client = getUserContext(request);
45
107
  if (!client) {
@@ -73,10 +135,53 @@ export function createAdminWorkspacesRoutes(params) {
73
135
  };
74
136
  }
75
137
  });
138
+ const createMutationRoute = (routeParams)=>defineRoute({
139
+ method: 'POST',
140
+ path: `/:workspaceId/${routeParams.action}`,
141
+ description: routeParams.description,
142
+ auth: AUTH_USER,
143
+ schema: {
144
+ params: z.object({
145
+ workspaceId: z.string().uuid()
146
+ }),
147
+ body: workspaceAdministrationMutationBodySchema,
148
+ response: {
149
+ 200: workspaceAdministrationMutationResponseSchema
150
+ }
151
+ },
152
+ errorHandler: translateAdministrationError,
153
+ handler: async (request)=>{
154
+ const actorId = requireActorId(request);
155
+ const actorRole = (await params.auth.requireAdminRole({
156
+ userId: actorId,
157
+ minimumRole: 'admin-operator'
158
+ })).role;
159
+ return toWorkspaceAdministrationMutationDto(await routeParams.execute({
160
+ actorId,
161
+ actorRole,
162
+ workspaceId: request.params.workspaceId,
163
+ reason: request.body.reason,
164
+ idempotencyKey: requireIdempotencyKey(request),
165
+ correlationId: request.id
166
+ }));
167
+ }
168
+ });
169
+ const suspendRoute = createMutationRoute({
170
+ action: 'suspend',
171
+ description: 'Suspend a workspace for reversible administrator moderation.',
172
+ execute: suspendWorkspace
173
+ });
174
+ const reactivateRoute = createMutationRoute({
175
+ action: 'reactivate',
176
+ description: 'Reactivate a suspended workspace.',
177
+ execute: reactivateWorkspace
178
+ });
76
179
  return {
77
180
  prefix: '/admin/workspaces',
78
181
  routes: [
79
- listRoute
182
+ listRoute,
183
+ suspendRoute,
184
+ reactivateRoute
80
185
  ]
81
186
  };
82
187
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/routes/admin-workspaces.ts"],"sourcesContent":["import {AUTH_USER, getUserContext} from '@shipfox/api-auth-context';\nimport {\n type AuthInterModuleClient,\n authInterModuleContract,\n} from '@shipfox/api-auth-dto/inter-module';\nimport type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';\nimport type {RunnersInterModuleClient} from '@shipfox/api-runners-dto/inter-module';\nimport {\n listWorkspaceAdminSummariesResponseSchema,\n workspaceAdminLookupQuerySchema,\n} from '@shipfox/api-workspaces-dto';\nimport {isInterModuleKnownError} from '@shipfox/inter-module';\nimport {decodeStringIdCursor, encodeStringIdCursor} from '@shipfox/node-drizzle';\nimport {ClientError, defineRoute, type RouteGroup} from '@shipfox/node-fastify';\nimport {\n listWorkspaceAdministratorSummaries,\n type WorkspaceAdministratorSummary,\n} from '#core/admin-workspaces.js';\n\nfunction toWorkspaceAdministratorSummaryDto(summary: WorkspaceAdministratorSummary) {\n return {\n id: summary.id,\n name: summary.name,\n status: summary.status,\n member_summary: summary.memberSummary,\n project_summary: summary.projectSummary,\n job_counts: summary.jobCounts,\n created_at: summary.createdAt.toISOString(),\n updated_at: summary.updatedAt.toISOString(),\n };\n}\n\nexport function createAdminWorkspacesRoutes(params: {\n auth: AuthInterModuleClient;\n projects: ProjectsModuleClient;\n runners: RunnersInterModuleClient;\n}): RouteGroup {\n const listRoute = defineRoute({\n method: 'GET',\n path: '/',\n description: 'List bounded safe workspace summaries for administrators.',\n auth: AUTH_USER,\n schema: {\n querystring: workspaceAdminLookupQuerySchema,\n response: {200: listWorkspaceAdminSummariesResponseSchema},\n },\n errorHandler: (error) => {\n if (\n isInterModuleKnownError(authInterModuleContract.methods.requireAdminRole, error) &&\n error.code === 'admin-role-required'\n ) {\n throw new ClientError('Administrator observer role required', 'forbidden', {\n status: 403,\n details: {required_role: error.details.requiredRole},\n });\n }\n throw error;\n },\n handler: async (request) => {\n const client = getUserContext(request);\n if (!client) {\n throw new ClientError('Authentication required', 'unauthorized', {status: 401});\n }\n await params.auth.requireAdminRole({\n userId: client.userId,\n minimumRole: 'admin-observer',\n });\n\n const {workspace_id: workspaceId, search, status, limit, cursor} = request.query;\n const decodedCursor = decodeStringIdCursor(cursor);\n if (cursor && !decodedCursor) {\n throw new ClientError('Invalid cursor', 'invalid-cursor', {status: 400});\n }\n\n const result = await listWorkspaceAdministratorSummaries({\n workspaceId,\n search,\n status,\n limit,\n cursor: decodedCursor,\n projects: params.projects,\n runners: params.runners,\n });\n\n return {\n workspaces: result.workspaces.map(toWorkspaceAdministratorSummaryDto),\n next_cursor: result.nextCursor ? encodeStringIdCursor(result.nextCursor) : null,\n };\n },\n });\n\n return {prefix: '/admin/workspaces', routes: [listRoute]};\n}\n"],"names":["AUTH_USER","getUserContext","authInterModuleContract","listWorkspaceAdminSummariesResponseSchema","workspaceAdminLookupQuerySchema","isInterModuleKnownError","decodeStringIdCursor","encodeStringIdCursor","ClientError","defineRoute","listWorkspaceAdministratorSummaries","toWorkspaceAdministratorSummaryDto","summary","id","name","status","member_summary","memberSummary","project_summary","projectSummary","job_counts","jobCounts","created_at","createdAt","toISOString","updated_at","updatedAt","createAdminWorkspacesRoutes","params","listRoute","method","path","description","auth","schema","querystring","response","errorHandler","error","methods","requireAdminRole","code","details","required_role","requiredRole","handler","request","client","userId","minimumRole","workspace_id","workspaceId","search","limit","cursor","query","decodedCursor","result","projects","runners","workspaces","map","next_cursor","nextCursor","prefix","routes"],"mappings":"AAAA,SAAQA,SAAS,EAAEC,cAAc,QAAO,4BAA4B;AACpE,SAEEC,uBAAuB,QAClB,qCAAqC;AAG5C,SACEC,yCAAyC,EACzCC,+BAA+B,QAC1B,8BAA8B;AACrC,SAAQC,uBAAuB,QAAO,wBAAwB;AAC9D,SAAQC,oBAAoB,EAAEC,oBAAoB,QAAO,wBAAwB;AACjF,SAAQC,WAAW,EAAEC,WAAW,QAAwB,wBAAwB;AAChF,SACEC,mCAAmC,QAE9B,4BAA4B;AAEnC,SAASC,mCAAmCC,OAAsC;IAChF,OAAO;QACLC,IAAID,QAAQC,EAAE;QACdC,MAAMF,QAAQE,IAAI;QAClBC,QAAQH,QAAQG,MAAM;QACtBC,gBAAgBJ,QAAQK,aAAa;QACrCC,iBAAiBN,QAAQO,cAAc;QACvCC,YAAYR,QAAQS,SAAS;QAC7BC,YAAYV,QAAQW,SAAS,CAACC,WAAW;QACzCC,YAAYb,QAAQc,SAAS,CAACF,WAAW;IAC3C;AACF;AAEA,OAAO,SAASG,4BAA4BC,MAI3C;IACC,MAAMC,YAAYpB,YAAY;QAC5BqB,QAAQ;QACRC,MAAM;QACNC,aAAa;QACbC,MAAMjC;QACNkC,QAAQ;YACNC,aAAa/B;YACbgC,UAAU;gBAAC,KAAKjC;YAAyC;QAC3D;QACAkC,cAAc,CAACC;YACb,IACEjC,wBAAwBH,wBAAwBqC,OAAO,CAACC,gBAAgB,EAAEF,UAC1EA,MAAMG,IAAI,KAAK,uBACf;gBACA,MAAM,IAAIjC,YAAY,wCAAwC,aAAa;oBACzEO,QAAQ;oBACR2B,SAAS;wBAACC,eAAeL,MAAMI,OAAO,CAACE,YAAY;oBAAA;gBACrD;YACF;YACA,MAAMN;QACR;QACAO,SAAS,OAAOC;YACd,MAAMC,SAAS9C,eAAe6C;YAC9B,IAAI,CAACC,QAAQ;gBACX,MAAM,IAAIvC,YAAY,2BAA2B,gBAAgB;oBAACO,QAAQ;gBAAG;YAC/E;YACA,MAAMa,OAAOK,IAAI,CAACO,gBAAgB,CAAC;gBACjCQ,QAAQD,OAAOC,MAAM;gBACrBC,aAAa;YACf;YAEA,MAAM,EAACC,cAAcC,WAAW,EAAEC,MAAM,EAAErC,MAAM,EAAEsC,KAAK,EAAEC,MAAM,EAAC,GAAGR,QAAQS,KAAK;YAChF,MAAMC,gBAAgBlD,qBAAqBgD;YAC3C,IAAIA,UAAU,CAACE,eAAe;gBAC5B,MAAM,IAAIhD,YAAY,kBAAkB,kBAAkB;oBAACO,QAAQ;gBAAG;YACxE;YAEA,MAAM0C,SAAS,MAAM/C,oCAAoC;gBACvDyC;gBACAC;gBACArC;gBACAsC;gBACAC,QAAQE;gBACRE,UAAU9B,OAAO8B,QAAQ;gBACzBC,SAAS/B,OAAO+B,OAAO;YACzB;YAEA,OAAO;gBACLC,YAAYH,OAAOG,UAAU,CAACC,GAAG,CAAClD;gBAClCmD,aAAaL,OAAOM,UAAU,GAAGxD,qBAAqBkD,OAAOM,UAAU,IAAI;YAC7E;QACF;IACF;IAEA,OAAO;QAACC,QAAQ;QAAqBC,QAAQ;YAACpC;SAAU;IAAA;AAC1D"}
1
+ {"version":3,"sources":["../../../src/presentation/routes/admin-workspaces.ts"],"sourcesContent":["import {AUTH_USER, getUserContext} from '@shipfox/api-auth-context';\nimport {\n type AuthInterModuleClient,\n authInterModuleContract,\n} from '@shipfox/api-auth-dto/inter-module';\nimport type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';\nimport type {RunnersInterModuleClient} from '@shipfox/api-runners-dto/inter-module';\nimport {\n listWorkspaceAdminSummariesResponseSchema,\n workspaceAdministrationMutationBodySchema,\n workspaceAdministrationMutationResponseSchema,\n workspaceAdminLookupQuerySchema,\n} from '@shipfox/api-workspaces-dto';\nimport {isInterModuleKnownError} from '@shipfox/inter-module';\nimport {decodeStringIdCursor, encodeStringIdCursor} from '@shipfox/node-drizzle';\nimport {ClientError, defineRoute, type RouteGroup} from '@shipfox/node-fastify';\nimport type {FastifyRequest} from 'fastify';\nimport {z} from 'zod';\nimport {\n listWorkspaceAdministratorSummaries,\n reactivateWorkspace,\n suspendWorkspace,\n type WorkspaceAdministrationMutationContext,\n type WorkspaceAdministratorSummary,\n} from '#core/admin-workspaces.js';\nimport {\n WorkspaceAdminIdempotencyKeyReuseError,\n WorkspaceAlreadySuspendedError,\n WorkspaceDeletedError,\n WorkspaceNotFoundError,\n WorkspaceNotSuspendedError,\n} from '#core/errors.js';\n\nconst idempotencyKeyMaxLength = 256;\n\nfunction requireActorId(request: FastifyRequest): string {\n const client = getUserContext(request);\n if (!client) {\n throw new ClientError('Authentication required', 'unauthorized', {status: 401});\n }\n return client.userId;\n}\n\nfunction requireIdempotencyKey(request: FastifyRequest): string {\n const value = request.headers['idempotency-key'];\n const key = Array.isArray(value) ? value[0] : value;\n if (!key || key.trim().length === 0 || key.length > idempotencyKeyMaxLength) {\n throw new ClientError('Idempotency-Key header is required', 'idempotency-key-required', {\n status: 400,\n });\n }\n return key;\n}\n\nfunction translateAdminRoleError(error: unknown, message: string): void {\n if (\n isInterModuleKnownError(authInterModuleContract.methods.requireAdminRole, error) &&\n error.code === 'admin-role-required'\n ) {\n throw new ClientError(message, 'forbidden', {\n status: 403,\n details: {required_role: error.details.requiredRole},\n });\n }\n}\n\nfunction translateAdministrationError(error: unknown): never {\n translateAdminRoleError(error, 'Administrator operator role required');\n if (error instanceof WorkspaceNotFoundError) {\n throw new ClientError('Workspace not found', 'workspace-not-found', {status: 404});\n }\n if (error instanceof WorkspaceDeletedError) {\n throw new ClientError('Workspace is deleted', 'workspace-deleted', {status: 409});\n }\n if (error instanceof WorkspaceAlreadySuspendedError) {\n throw new ClientError('Workspace is already suspended', 'workspace-already-suspended', {\n status: 409,\n });\n }\n if (error instanceof WorkspaceNotSuspendedError) {\n throw new ClientError('Workspace is not suspended', 'workspace-not-suspended', {\n status: 409,\n });\n }\n if (error instanceof WorkspaceAdminIdempotencyKeyReuseError) {\n throw new ClientError(\n 'Idempotency-Key was already used for a different workspace command',\n 'idempotency-key-reused',\n {status: 409},\n );\n }\n throw error;\n}\n\nfunction translateObserverAdministrationError(error: unknown): never {\n translateAdminRoleError(error, 'Administrator observer role required');\n throw error;\n}\n\nfunction toWorkspaceAdministrationMutationDto(result: {\n workspaceId: string;\n status: 'active' | 'suspended';\n correlationId: string;\n}) {\n return {\n workspace_id: result.workspaceId,\n status: result.status,\n correlation_id: result.correlationId,\n };\n}\n\nfunction toWorkspaceAdministratorSummaryDto(summary: WorkspaceAdministratorSummary) {\n return {\n id: summary.id,\n name: summary.name,\n status: summary.status,\n member_summary: summary.memberSummary,\n project_summary: summary.projectSummary,\n job_counts: summary.jobCounts,\n created_at: summary.createdAt.toISOString(),\n updated_at: summary.updatedAt.toISOString(),\n };\n}\n\nexport function createAdminWorkspacesRoutes(params: {\n auth: AuthInterModuleClient;\n projects: ProjectsModuleClient;\n runners: RunnersInterModuleClient;\n}): RouteGroup {\n const listRoute = defineRoute({\n method: 'GET',\n path: '/',\n description: 'List bounded safe workspace summaries for administrators.',\n auth: AUTH_USER,\n schema: {\n querystring: workspaceAdminLookupQuerySchema,\n response: {200: listWorkspaceAdminSummariesResponseSchema},\n },\n errorHandler: translateObserverAdministrationError,\n handler: async (request) => {\n const client = getUserContext(request);\n if (!client) {\n throw new ClientError('Authentication required', 'unauthorized', {status: 401});\n }\n await params.auth.requireAdminRole({\n userId: client.userId,\n minimumRole: 'admin-observer',\n });\n\n const {workspace_id: workspaceId, search, status, limit, cursor} = request.query;\n const decodedCursor = decodeStringIdCursor(cursor);\n if (cursor && !decodedCursor) {\n throw new ClientError('Invalid cursor', 'invalid-cursor', {status: 400});\n }\n\n const result = await listWorkspaceAdministratorSummaries({\n workspaceId,\n search,\n status,\n limit,\n cursor: decodedCursor,\n projects: params.projects,\n runners: params.runners,\n });\n\n return {\n workspaces: result.workspaces.map(toWorkspaceAdministratorSummaryDto),\n next_cursor: result.nextCursor ? encodeStringIdCursor(result.nextCursor) : null,\n };\n },\n });\n\n const createMutationRoute = (routeParams: {\n action: 'suspend' | 'reactivate';\n description: string;\n execute: (context: WorkspaceAdministrationMutationContext) => Promise<{\n workspaceId: string;\n status: 'active' | 'suspended';\n correlationId: string;\n }>;\n }) =>\n defineRoute({\n method: 'POST',\n path: `/:workspaceId/${routeParams.action}`,\n description: routeParams.description,\n auth: AUTH_USER,\n schema: {\n params: z.object({workspaceId: z.string().uuid()}),\n body: workspaceAdministrationMutationBodySchema,\n response: {200: workspaceAdministrationMutationResponseSchema},\n },\n errorHandler: translateAdministrationError,\n handler: async (request) => {\n const actorId = requireActorId(request);\n const actorRole = (\n await params.auth.requireAdminRole({userId: actorId, minimumRole: 'admin-operator'})\n ).role;\n return toWorkspaceAdministrationMutationDto(\n await routeParams.execute({\n actorId,\n actorRole,\n workspaceId: request.params.workspaceId,\n reason: request.body.reason,\n idempotencyKey: requireIdempotencyKey(request),\n correlationId: request.id,\n }),\n );\n },\n });\n\n const suspendRoute = createMutationRoute({\n action: 'suspend',\n description: 'Suspend a workspace for reversible administrator moderation.',\n execute: suspendWorkspace,\n });\n const reactivateRoute = createMutationRoute({\n action: 'reactivate',\n description: 'Reactivate a suspended workspace.',\n execute: reactivateWorkspace,\n });\n\n return {prefix: '/admin/workspaces', routes: [listRoute, suspendRoute, reactivateRoute]};\n}\n"],"names":["AUTH_USER","getUserContext","authInterModuleContract","listWorkspaceAdminSummariesResponseSchema","workspaceAdministrationMutationBodySchema","workspaceAdministrationMutationResponseSchema","workspaceAdminLookupQuerySchema","isInterModuleKnownError","decodeStringIdCursor","encodeStringIdCursor","ClientError","defineRoute","z","listWorkspaceAdministratorSummaries","reactivateWorkspace","suspendWorkspace","WorkspaceAdminIdempotencyKeyReuseError","WorkspaceAlreadySuspendedError","WorkspaceDeletedError","WorkspaceNotFoundError","WorkspaceNotSuspendedError","idempotencyKeyMaxLength","requireActorId","request","client","status","userId","requireIdempotencyKey","value","headers","key","Array","isArray","trim","length","translateAdminRoleError","error","message","methods","requireAdminRole","code","details","required_role","requiredRole","translateAdministrationError","translateObserverAdministrationError","toWorkspaceAdministrationMutationDto","result","workspace_id","workspaceId","correlation_id","correlationId","toWorkspaceAdministratorSummaryDto","summary","id","name","member_summary","memberSummary","project_summary","projectSummary","job_counts","jobCounts","created_at","createdAt","toISOString","updated_at","updatedAt","createAdminWorkspacesRoutes","params","listRoute","method","path","description","auth","schema","querystring","response","errorHandler","handler","minimumRole","search","limit","cursor","query","decodedCursor","projects","runners","workspaces","map","next_cursor","nextCursor","createMutationRoute","routeParams","action","object","string","uuid","body","actorId","actorRole","role","execute","reason","idempotencyKey","suspendRoute","reactivateRoute","prefix","routes"],"mappings":"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"}