@shipfox/api-logs 10.2.0 → 11.0.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 +4 -4
- package/CHANGELOG.md +13 -0
- package/dist/presentation/routes/read-logs.d.ts.map +1 -1
- package/dist/presentation/routes/read-logs.js +11 -5
- package/dist/presentation/routes/read-logs.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/presentation/routes/read-logs.test.ts +18 -4
- package/src/presentation/routes/read-logs.ts +9 -5
- package/tsconfig.build.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipfox/api-logs",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "11.0.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/ShipfoxHQ/shipfox.git",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"@temporalio/workflow": "1.18.1",
|
|
26
26
|
"drizzle-orm": "^0.45.2",
|
|
27
27
|
"zod": "^4.4.3",
|
|
28
|
-
"@shipfox/api-auth-context": "
|
|
28
|
+
"@shipfox/api-auth-context": "11.0.0",
|
|
29
29
|
"@shipfox/api-logs-dto": "10.2.0",
|
|
30
30
|
"@shipfox/api-workflows-dto": "10.0.0",
|
|
31
31
|
"@shipfox/workflow-document": "2.1.3",
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"@shipfox/node-error-monitoring": "0.3.0",
|
|
35
35
|
"@shipfox/node-fastify": "0.4.0",
|
|
36
36
|
"@shipfox/node-module": "1.0.4",
|
|
37
|
+
"@shipfox/node-opentelemetry": "0.6.3",
|
|
37
38
|
"@shipfox/node-outbox": "0.2.6",
|
|
38
39
|
"@shipfox/node-postgres": "0.4.4",
|
|
39
|
-
"@shipfox/node-opentelemetry": "0.6.3",
|
|
40
40
|
"@shipfox/node-temporal": "0.4.4"
|
|
41
41
|
},
|
|
42
42
|
"imports": {
|
|
@@ -50,13 +50,15 @@ const fakeUserAuth: AuthMethod = {
|
|
|
50
50
|
throw new ClientError('Invalid user token', 'unauthorized', {status: 401});
|
|
51
51
|
}
|
|
52
52
|
const header = request.headers['x-test-workspace'];
|
|
53
|
-
const
|
|
53
|
+
const value = Array.isArray(header) ? header[0] : header;
|
|
54
|
+
const [workspaceId, status] = value?.split('|') ?? [];
|
|
55
|
+
const workspaceStatus = status === 'suspended' || status === 'deleted' ? status : 'active';
|
|
54
56
|
setUserContext(
|
|
55
57
|
request,
|
|
56
58
|
buildUserContext({
|
|
57
59
|
userId: 'user-1',
|
|
58
60
|
email: 'user@example.com',
|
|
59
|
-
memberships: workspaceId ? [{workspaceId, role: 'admin'}] : [],
|
|
61
|
+
memberships: workspaceId ? [{workspaceId, role: 'admin', workspaceStatus}] : [],
|
|
60
62
|
}),
|
|
61
63
|
);
|
|
62
64
|
return Promise.resolve();
|
|
@@ -174,11 +176,11 @@ describe('GET /steps/:stepId/attempts/:attempt/logs', () => {
|
|
|
174
176
|
return `/steps/${stepId}/attempts/${attempt}/logs?cursor=${cursor}`;
|
|
175
177
|
}
|
|
176
178
|
|
|
177
|
-
function authedGet(stream: AttemptStream, cursor: number,
|
|
179
|
+
function authedGet(stream: AttemptStream, cursor: number, workspaceClaim = stream.workspaceId) {
|
|
178
180
|
return app.inject({
|
|
179
181
|
method: 'GET',
|
|
180
182
|
url: readUrl(stream.stepId, stream.attempt, cursor),
|
|
181
|
-
headers: {authorization: 'Bearer user', 'x-test-workspace':
|
|
183
|
+
headers: {authorization: 'Bearer user', 'x-test-workspace': workspaceClaim},
|
|
182
184
|
});
|
|
183
185
|
}
|
|
184
186
|
|
|
@@ -288,6 +290,18 @@ describe('GET /steps/:stepId/attempts/:attempt/logs', () => {
|
|
|
288
290
|
expect(res.json().code).toBe('not-found');
|
|
289
291
|
});
|
|
290
292
|
|
|
293
|
+
it('returns workspace-suspended for a suspended membership claim', async () => {
|
|
294
|
+
const stream = await arrangeStream({
|
|
295
|
+
workspaceId: crypto.randomUUID(),
|
|
296
|
+
chunks: [ndjsonBody(outputLine('secret\n'))],
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
const res = await authedGet(stream, 0, `${stream.workspaceId}|suspended`);
|
|
300
|
+
|
|
301
|
+
expect(res.statusCode).toBe(409);
|
|
302
|
+
expect(res.json().code).toBe('workspace-suspended');
|
|
303
|
+
});
|
|
304
|
+
|
|
291
305
|
it('serves an open stream inline with the next cursor and stream state', async () => {
|
|
292
306
|
const workspaceId = crypto.randomUUID();
|
|
293
307
|
const lineA = outputLine('alpha\n');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {requireWorkspaceResourceAccess} from '@shipfox/api-auth-context';
|
|
2
2
|
import {readLogsQuerySchema, readLogsResponseSchema} from '@shipfox/api-logs-dto';
|
|
3
3
|
import {ClientError, defineRoute} from '@shipfox/node-fastify';
|
|
4
4
|
import {z} from 'zod';
|
|
@@ -22,16 +22,20 @@ export const readLogsRoute = defineRoute({
|
|
|
22
22
|
},
|
|
23
23
|
},
|
|
24
24
|
handler: async (request) => {
|
|
25
|
-
const user = requireUserContext(request);
|
|
26
25
|
const {stepId, attempt} = request.params;
|
|
27
26
|
const {cursor} = request.query;
|
|
28
27
|
|
|
29
28
|
const stream = await getStreamByStepAttempt({stepId, attempt});
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
if (!stream
|
|
29
|
+
// Missing streams and memberships remain 404 so the endpoint never leaks another
|
|
30
|
+
// workspace's step; lifecycle claims propagate their stable access errors.
|
|
31
|
+
if (!stream) {
|
|
33
32
|
throw new ClientError('Logs not found', 'not-found', {status: 404});
|
|
34
33
|
}
|
|
34
|
+
requireWorkspaceResourceAccess({
|
|
35
|
+
request,
|
|
36
|
+
workspaceId: stream.workspaceId,
|
|
37
|
+
notFoundError: new ClientError('Logs not found', 'not-found', {status: 404}),
|
|
38
|
+
});
|
|
35
39
|
|
|
36
40
|
return toReadLogsDto(await buildLogReadResult(stream, cursor));
|
|
37
41
|
},
|