@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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +19 -0
- package/README.md +7 -1
- package/dist/core/admin-workspaces.d.ts +16 -0
- package/dist/core/admin-workspaces.d.ts.map +1 -1
- package/dist/core/admin-workspaces.js +52 -0
- package/dist/core/admin-workspaces.js.map +1 -1
- package/dist/core/errors.d.ts +12 -0
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +24 -0
- package/dist/core/errors.js.map +1 -1
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -2
- package/dist/core/index.js.map +1 -1
- package/dist/db/admin-workspace-commands.d.ts +17 -0
- package/dist/db/admin-workspace-commands.d.ts.map +1 -0
- package/dist/db/admin-workspace-commands.js +86 -0
- package/dist/db/admin-workspace-commands.js.map +1 -0
- package/dist/db/db.d.ts +256 -0
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/db.js +2 -0
- package/dist/db/db.js.map +1 -1
- package/dist/db/index.d.ts +1 -0
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +1 -0
- package/dist/db/index.js.map +1 -1
- package/dist/db/schema/admin-command-results.d.ts +135 -0
- package/dist/db/schema/admin-command-results.d.ts.map +1 -0
- package/dist/db/schema/admin-command-results.js +18 -0
- package/dist/db/schema/admin-command-results.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/presentation/routes/admin-workspaces.d.ts.map +1 -1
- package/dist/presentation/routes/admin-workspaces.js +119 -14
- package/dist/presentation/routes/admin-workspaces.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/drizzle/0002_wide_enchantress.sql +11 -0
- package/drizzle/meta/0002_snapshot.json +555 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +5 -5
- package/src/core/admin-workspaces.ts +102 -0
- package/src/core/errors.ts +28 -0
- package/src/core/index.ts +8 -0
- package/src/db/admin-workspace-commands.ts +169 -0
- package/src/db/db.ts +2 -0
- package/src/db/index.ts +1 -0
- package/src/db/schema/admin-command-results.ts +30 -0
- package/src/index.test.ts +3 -1
- package/src/index.ts +17 -2
- package/src/presentation/routes/admin-workspaces.test.ts +273 -3
- package/src/presentation/routes/admin-workspaces.ts +143 -13
- package/test/globalSetup.ts +3 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- 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 {
|
|
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:
|
|
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":"AAAA,SAAQA,SAAS,EAAEC,cAAc,QAAO,4BAA4B;AACpE,SAEEC,uBAAuB,QAClB,qCAAqC;AAG5C,SACEC,yCAAyC,EACzCC,yCAAyC,EACzCC,6CAA6C,EAC7CC,+BAA+B,QAC1B,8BAA8B;AACrC,SAAQC,uBAAuB,QAAO,wBAAwB;AAC9D,SAAQC,oBAAoB,EAAEC,oBAAoB,QAAO,wBAAwB;AACjF,SAAQC,WAAW,EAAEC,WAAW,QAAwB,wBAAwB;AAEhF,SAAQC,CAAC,QAAO,MAAM;AACtB,SACEC,mCAAmC,EACnCC,mBAAmB,EACnBC,gBAAgB,QAGX,4BAA4B;AACnC,SACEC,sCAAsC,EACtCC,8BAA8B,EAC9BC,qBAAqB,EACrBC,sBAAsB,EACtBC,0BAA0B,QACrB,kBAAkB;AAEzB,MAAMC,0BAA0B;AAEhC,SAASC,eAAeC,OAAuB;IAC7C,MAAMC,SAASvB,eAAesB;IAC9B,IAAI,CAACC,QAAQ;QACX,MAAM,IAAId,YAAY,2BAA2B,gBAAgB;YAACe,QAAQ;QAAG;IAC/E;IACA,OAAOD,OAAOE,MAAM;AACtB;AAEA,SAASC,sBAAsBJ,OAAuB;IACpD,MAAMK,QAAQL,QAAQM,OAAO,CAAC,kBAAkB;IAChD,MAAMC,MAAMC,MAAMC,OAAO,CAACJ,SAASA,KAAK,CAAC,EAAE,GAAGA;IAC9C,IAAI,CAACE,OAAOA,IAAIG,IAAI,GAAGC,MAAM,KAAK,KAAKJ,IAAII,MAAM,GAAGb,yBAAyB;QAC3E,MAAM,IAAIX,YAAY,sCAAsC,4BAA4B;YACtFe,QAAQ;QACV;IACF;IACA,OAAOK;AACT;AAEA,SAASK,wBAAwBC,KAAc,EAAEC,OAAe;IAC9D,IACE9B,wBAAwBL,wBAAwBoC,OAAO,CAACC,gBAAgB,EAAEH,UAC1EA,MAAMI,IAAI,KAAK,uBACf;QACA,MAAM,IAAI9B,YAAY2B,SAAS,aAAa;YAC1CZ,QAAQ;YACRgB,SAAS;gBAACC,eAAeN,MAAMK,OAAO,CAACE,YAAY;YAAA;QACrD;IACF;AACF;AAEA,SAASC,6BAA6BR,KAAc;IAClDD,wBAAwBC,OAAO;IAC/B,IAAIA,iBAAiBjB,wBAAwB;QAC3C,MAAM,IAAIT,YAAY,uBAAuB,uBAAuB;YAACe,QAAQ;QAAG;IAClF;IACA,IAAIW,iBAAiBlB,uBAAuB;QAC1C,MAAM,IAAIR,YAAY,wBAAwB,qBAAqB;YAACe,QAAQ;QAAG;IACjF;IACA,IAAIW,iBAAiBnB,gCAAgC;QACnD,MAAM,IAAIP,YAAY,kCAAkC,+BAA+B;YACrFe,QAAQ;QACV;IACF;IACA,IAAIW,iBAAiBhB,4BAA4B;QAC/C,MAAM,IAAIV,YAAY,8BAA8B,2BAA2B;YAC7Ee,QAAQ;QACV;IACF;IACA,IAAIW,iBAAiBpB,wCAAwC;QAC3D,MAAM,IAAIN,YACR,sEACA,0BACA;YAACe,QAAQ;QAAG;IAEhB;IACA,MAAMW;AACR;AAEA,SAASS,qCAAqCT,KAAc;IAC1DD,wBAAwBC,OAAO;IAC/B,MAAMA;AACR;AAEA,SAASU,qCAAqCC,MAI7C;IACC,OAAO;QACLC,cAAcD,OAAOE,WAAW;QAChCxB,QAAQsB,OAAOtB,MAAM;QACrByB,gBAAgBH,OAAOI,aAAa;IACtC;AACF;AAEA,SAASC,mCAAmCC,OAAsC;IAChF,OAAO;QACLC,IAAID,QAAQC,EAAE;QACdC,MAAMF,QAAQE,IAAI;QAClB9B,QAAQ4B,QAAQ5B,MAAM;QACtB+B,gBAAgBH,QAAQI,aAAa;QACrCC,iBAAiBL,QAAQM,cAAc;QACvCC,YAAYP,QAAQQ,SAAS;QAC7BC,YAAYT,QAAQU,SAAS,CAACC,WAAW;QACzCC,YAAYZ,QAAQa,SAAS,CAACF,WAAW;IAC3C;AACF;AAEA,OAAO,SAASG,4BAA4BC,MAI3C;IACC,MAAMC,YAAY1D,YAAY;QAC5B2D,QAAQ;QACRC,MAAM;QACNC,aAAa;QACbC,MAAMzE;QACN0E,QAAQ;YACNC,aAAarE;YACbsE,UAAU;gBAAC,KAAKzE;YAAyC;QAC3D;QACA0E,cAAchC;QACdiC,SAAS,OAAOvD;YACd,MAAMC,SAASvB,eAAesB;YAC9B,IAAI,CAACC,QAAQ;gBACX,MAAM,IAAId,YAAY,2BAA2B,gBAAgB;oBAACe,QAAQ;gBAAG;YAC/E;YACA,MAAM2C,OAAOK,IAAI,CAAClC,gBAAgB,CAAC;gBACjCb,QAAQF,OAAOE,MAAM;gBACrBqD,aAAa;YACf;YAEA,MAAM,EAAC/B,cAAcC,WAAW,EAAE+B,MAAM,EAAEvD,MAAM,EAAEwD,KAAK,EAAEC,MAAM,EAAC,GAAG3D,QAAQ4D,KAAK;YAChF,MAAMC,gBAAgB5E,qBAAqB0E;YAC3C,IAAIA,UAAU,CAACE,eAAe;gBAC5B,MAAM,IAAI1E,YAAY,kBAAkB,kBAAkB;oBAACe,QAAQ;gBAAG;YACxE;YAEA,MAAMsB,SAAS,MAAMlC,oCAAoC;gBACvDoC;gBACA+B;gBACAvD;gBACAwD;gBACAC,QAAQE;gBACRC,UAAUjB,OAAOiB,QAAQ;gBACzBC,SAASlB,OAAOkB,OAAO;YACzB;YAEA,OAAO;gBACLC,YAAYxC,OAAOwC,UAAU,CAACC,GAAG,CAACpC;gBAClCqC,aAAa1C,OAAO2C,UAAU,GAAGjF,qBAAqBsC,OAAO2C,UAAU,IAAI;YAC7E;QACF;IACF;IAEA,MAAMC,sBAAsB,CAACC,cAS3BjF,YAAY;YACV2D,QAAQ;YACRC,MAAM,CAAC,cAAc,EAAEqB,YAAYC,MAAM,EAAE;YAC3CrB,aAAaoB,YAAYpB,WAAW;YACpCC,MAAMzE;YACN0E,QAAQ;gBACNN,QAAQxD,EAAEkF,MAAM,CAAC;oBAAC7C,aAAarC,EAAEmF,MAAM,GAAGC,IAAI;gBAAE;gBAChDC,MAAM7F;gBACNwE,UAAU;oBAAC,KAAKvE;gBAA6C;YAC/D;YACAwE,cAAcjC;YACdkC,SAAS,OAAOvD;gBACd,MAAM2E,UAAU5E,eAAeC;gBAC/B,MAAM4E,YAAY,AAChB,CAAA,MAAM/B,OAAOK,IAAI,CAAClC,gBAAgB,CAAC;oBAACb,QAAQwE;oBAASnB,aAAa;gBAAgB,EAAC,EACnFqB,IAAI;gBACN,OAAOtD,qCACL,MAAM8C,YAAYS,OAAO,CAAC;oBACxBH;oBACAC;oBACAlD,aAAa1B,QAAQ6C,MAAM,CAACnB,WAAW;oBACvCqD,QAAQ/E,QAAQ0E,IAAI,CAACK,MAAM;oBAC3BC,gBAAgB5E,sBAAsBJ;oBACtC4B,eAAe5B,QAAQ+B,EAAE;gBAC3B;YAEJ;QACF;IAEF,MAAMkD,eAAeb,oBAAoB;QACvCE,QAAQ;QACRrB,aAAa;QACb6B,SAAStF;IACX;IACA,MAAM0F,kBAAkBd,oBAAoB;QAC1CE,QAAQ;QACRrB,aAAa;QACb6B,SAASvF;IACX;IAEA,OAAO;QAAC4F,QAAQ;QAAqBC,QAAQ;YAACtC;YAAWmC;YAAcC;SAAgB;IAAA;AACzF"}
|