@shipfox/api-secrets 5.0.0 → 7.1.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 +39 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +29 -15
- package/dist/index.js.map +1 -1
- package/dist/presentation/inter-module.d.ts +4 -0
- package/dist/presentation/inter-module.d.ts.map +1 -0
- package/dist/presentation/inter-module.js +73 -0
- package/dist/presentation/inter-module.js.map +1 -0
- package/dist/presentation/routes/auth.d.ts +14 -10
- package/dist/presentation/routes/auth.d.ts.map +1 -1
- package/dist/presentation/routes/auth.js +27 -11
- package/dist/presentation/routes/auth.js.map +1 -1
- package/dist/presentation/routes/batch-secrets.d.ts +2 -1
- package/dist/presentation/routes/batch-secrets.d.ts.map +1 -1
- package/dist/presentation/routes/batch-secrets.js +35 -34
- package/dist/presentation/routes/batch-secrets.js.map +1 -1
- package/dist/presentation/routes/batch-variables.d.ts +2 -1
- package/dist/presentation/routes/batch-variables.d.ts.map +1 -1
- package/dist/presentation/routes/batch-variables.js +35 -34
- package/dist/presentation/routes/batch-variables.js.map +1 -1
- package/dist/presentation/routes/delete-secret.d.ts +2 -1
- package/dist/presentation/routes/delete-secret.d.ts.map +1 -1
- package/dist/presentation/routes/delete-secret.js +33 -32
- package/dist/presentation/routes/delete-secret.js.map +1 -1
- package/dist/presentation/routes/delete-variable.d.ts +2 -1
- package/dist/presentation/routes/delete-variable.d.ts.map +1 -1
- package/dist/presentation/routes/delete-variable.js +33 -32
- package/dist/presentation/routes/delete-variable.js.map +1 -1
- package/dist/presentation/routes/errors.d.ts.map +1 -1
- package/dist/presentation/routes/errors.js +0 -6
- package/dist/presentation/routes/errors.js.map +1 -1
- package/dist/presentation/routes/get-variable.d.ts +2 -1
- package/dist/presentation/routes/get-variable.d.ts.map +1 -1
- package/dist/presentation/routes/get-variable.js +34 -33
- package/dist/presentation/routes/get-variable.js.map +1 -1
- package/dist/presentation/routes/index.d.ts +2 -1
- package/dist/presentation/routes/index.d.ts.map +1 -1
- package/dist/presentation/routes/index.js +21 -17
- package/dist/presentation/routes/index.js.map +1 -1
- package/dist/presentation/routes/list-secrets.d.ts +2 -1
- package/dist/presentation/routes/list-secrets.d.ts.map +1 -1
- package/dist/presentation/routes/list-secrets.js +40 -39
- package/dist/presentation/routes/list-secrets.js.map +1 -1
- package/dist/presentation/routes/list-variables.d.ts +2 -1
- package/dist/presentation/routes/list-variables.d.ts.map +1 -1
- package/dist/presentation/routes/list-variables.js +40 -39
- package/dist/presentation/routes/list-variables.js.map +1 -1
- package/dist/presentation/routes/put-secret.d.ts +2 -1
- package/dist/presentation/routes/put-secret.d.ts.map +1 -1
- package/dist/presentation/routes/put-secret.js +43 -42
- package/dist/presentation/routes/put-secret.js.map +1 -1
- package/dist/presentation/routes/put-variable.d.ts +2 -1
- package/dist/presentation/routes/put-variable.d.ts.map +1 -1
- package/dist/presentation/routes/put-variable.js +43 -42
- package/dist/presentation/routes/put-variable.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +11 -19
- package/src/index.ts +16 -5
- package/src/presentation/inter-module.test.ts +32 -0
- package/src/presentation/inter-module.ts +78 -0
- package/src/presentation/routes/auth.ts +65 -19
- package/src/presentation/routes/batch-secrets.ts +26 -24
- package/src/presentation/routes/batch-variables.ts +26 -24
- package/src/presentation/routes/delete-secret.ts +21 -19
- package/src/presentation/routes/delete-variable.ts +21 -19
- package/src/presentation/routes/errors.ts +0 -5
- package/src/presentation/routes/get-variable.ts +21 -19
- package/src/presentation/routes/index.ts +22 -17
- package/src/presentation/routes/list-secrets.ts +33 -31
- package/src/presentation/routes/list-variables.ts +33 -31
- package/src/presentation/routes/management.test.ts +32 -18
- package/src/presentation/routes/put-secret.ts +28 -26
- package/src/presentation/routes/put-variable.ts +28 -26
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -3,39 +3,41 @@ import {ClientError, defineRoute} from '@shipfox/node-fastify';
|
|
|
3
3
|
import {z} from 'zod';
|
|
4
4
|
import {listManagedSecrets} from '#core/index.js';
|
|
5
5
|
import {toSecretDto} from '#presentation/dto/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import type {ManagementAccessHelpers} from './auth.js';
|
|
7
7
|
import {decodeManagementCursor, encodeManagementCursor} from './cursor.js';
|
|
8
8
|
import {translateManagementError} from './errors.js';
|
|
9
9
|
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
10
|
+
export function listSecretsRoute(accessControl: ManagementAccessHelpers) {
|
|
11
|
+
return defineRoute({
|
|
12
|
+
method: 'GET',
|
|
13
|
+
path: '/secrets',
|
|
14
|
+
description: 'List user-managed secrets for a workspace or project scope.',
|
|
15
|
+
schema: {
|
|
16
|
+
params: z.object({workspaceId: z.string().uuid()}),
|
|
17
|
+
querystring: listSecretsQuerySchema,
|
|
18
|
+
response: {200: listSecretsResponseSchema},
|
|
19
|
+
},
|
|
20
|
+
errorHandler: translateManagementError,
|
|
21
|
+
handler: async (request) => {
|
|
22
|
+
const {workspaceId} = request.params;
|
|
23
|
+
const {project_id: projectId, limit, cursor} = request.query;
|
|
24
|
+
const decodedCursor = decodeManagementCursor(cursor);
|
|
25
|
+
if (cursor && !decodedCursor) {
|
|
26
|
+
throw new ClientError('Invalid cursor', 'invalid-cursor', {status: 400});
|
|
27
|
+
}
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
await accessControl.requireManagementRead({request, workspaceId, projectId});
|
|
30
|
+
const result = await listManagedSecrets({
|
|
31
|
+
workspaceId,
|
|
32
|
+
projectId,
|
|
33
|
+
limit,
|
|
34
|
+
cursor: decodedCursor ?? undefined,
|
|
35
|
+
});
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
});
|
|
37
|
+
return {
|
|
38
|
+
secrets: result.secrets.map(toSecretDto),
|
|
39
|
+
next_cursor: result.nextCursor ? encodeManagementCursor(result.nextCursor) : null,
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -3,39 +3,41 @@ import {ClientError, defineRoute} from '@shipfox/node-fastify';
|
|
|
3
3
|
import {z} from 'zod';
|
|
4
4
|
import {listManagedVariables} from '#core/index.js';
|
|
5
5
|
import {toVariableListItemDto} from '#presentation/dto/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import type {ManagementAccessHelpers} from './auth.js';
|
|
7
7
|
import {decodeManagementCursor, encodeManagementCursor} from './cursor.js';
|
|
8
8
|
import {translateManagementError} from './errors.js';
|
|
9
9
|
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
10
|
+
export function listVariablesRoute(accessControl: ManagementAccessHelpers) {
|
|
11
|
+
return defineRoute({
|
|
12
|
+
method: 'GET',
|
|
13
|
+
path: '/variables',
|
|
14
|
+
description: 'List user-managed variables for a workspace or project scope.',
|
|
15
|
+
schema: {
|
|
16
|
+
params: z.object({workspaceId: z.string().uuid()}),
|
|
17
|
+
querystring: listVariablesQuerySchema,
|
|
18
|
+
response: {200: listVariablesResponseSchema},
|
|
19
|
+
},
|
|
20
|
+
errorHandler: translateManagementError,
|
|
21
|
+
handler: async (request) => {
|
|
22
|
+
const {workspaceId} = request.params;
|
|
23
|
+
const {project_id: projectId, limit, cursor} = request.query;
|
|
24
|
+
const decodedCursor = decodeManagementCursor(cursor);
|
|
25
|
+
if (cursor && !decodedCursor) {
|
|
26
|
+
throw new ClientError('Invalid cursor', 'invalid-cursor', {status: 400});
|
|
27
|
+
}
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
await accessControl.requireManagementRead({request, workspaceId, projectId});
|
|
30
|
+
const result = await listManagedVariables({
|
|
31
|
+
workspaceId,
|
|
32
|
+
projectId,
|
|
33
|
+
limit,
|
|
34
|
+
cursor: decodedCursor ?? undefined,
|
|
35
|
+
});
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
});
|
|
37
|
+
return {
|
|
38
|
+
variables: result.variables.map(toVariableListItemDto),
|
|
39
|
+
next_cursor: result.nextCursor ? encodeManagementCursor(result.nextCursor) : null,
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
setUserContext,
|
|
5
5
|
type UserContextMembership,
|
|
6
6
|
} from '@shipfox/api-auth-context';
|
|
7
|
-
import {
|
|
7
|
+
import {projectsInterModuleContract} from '@shipfox/api-projects-dto';
|
|
8
8
|
import {
|
|
9
9
|
SECRET_CREATED,
|
|
10
10
|
SECRET_DELETED,
|
|
@@ -12,17 +12,22 @@ import {
|
|
|
12
12
|
VARIABLE_CREATED,
|
|
13
13
|
VARIABLE_DELETED,
|
|
14
14
|
} from '@shipfox/api-secrets-dto';
|
|
15
|
+
import {createInterModuleKnownError, defineInterModulePresentation} from '@shipfox/inter-module';
|
|
15
16
|
import type {AuthMethod, FastifyRequest} from '@shipfox/node-fastify';
|
|
16
17
|
import {ClientError, closeApp, createApp} from '@shipfox/node-fastify';
|
|
18
|
+
import {createFakeInterModuleClients} from '@shipfox/node-module/inter-module/testing';
|
|
17
19
|
import {and, eq, isNull, sql} from 'drizzle-orm';
|
|
18
20
|
import {setSecrets} from '#core/index.js';
|
|
19
21
|
import {db, secretsOutbox, secretValues, secretVariables} from '#db/index.js';
|
|
20
|
-
import {
|
|
22
|
+
import {createSecretsRoutes} from './index.js';
|
|
21
23
|
|
|
22
|
-
vi.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
})
|
|
24
|
+
const requireProjectForWorkspace = vi.fn();
|
|
25
|
+
const projects = createFakeInterModuleClients({
|
|
26
|
+
projects: defineInterModulePresentation(projectsInterModuleContract, {
|
|
27
|
+
getProjectById: () => ({project: null}),
|
|
28
|
+
requireProjectForWorkspace: async (input) => await requireProjectForWorkspace(input),
|
|
29
|
+
}),
|
|
30
|
+
}).projects;
|
|
26
31
|
|
|
27
32
|
const USER_ID = '11111111-1111-4111-8111-111111111111';
|
|
28
33
|
let authenticatedMemberships: UserContextMembership[] = [];
|
|
@@ -56,16 +61,21 @@ describe('secrets management routes', () => {
|
|
|
56
61
|
workspaceId = crypto.randomUUID();
|
|
57
62
|
projectId = crypto.randomUUID();
|
|
58
63
|
authenticatedMemberships = [{workspaceId, role: 'admin'}];
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
requireProjectForWorkspace.mockResolvedValue({
|
|
65
|
+
project: {
|
|
66
|
+
id: projectId,
|
|
67
|
+
workspaceId,
|
|
68
|
+
name: 'Project',
|
|
69
|
+
sourceConnectionId: crypto.randomUUID(),
|
|
70
|
+
sourceExternalRepositoryId: 'repo-1',
|
|
71
|
+
createdAt: new Date(),
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
app = await createApp({
|
|
75
|
+
auth: [fakeUserAuth],
|
|
76
|
+
routes: createSecretsRoutes(projects),
|
|
77
|
+
swagger: false,
|
|
67
78
|
});
|
|
68
|
-
app = await createApp({auth: [fakeUserAuth], routes: secretsRoutes, swagger: false});
|
|
69
79
|
await app.ready();
|
|
70
80
|
});
|
|
71
81
|
|
|
@@ -76,7 +86,7 @@ describe('secrets management routes', () => {
|
|
|
76
86
|
});
|
|
77
87
|
|
|
78
88
|
it('registers management routes under user auth', () => {
|
|
79
|
-
expect(
|
|
89
|
+
expect(createSecretsRoutes(projects)[0]?.auth).toBe(AUTH_USER);
|
|
80
90
|
});
|
|
81
91
|
|
|
82
92
|
it('returns 401 unauthenticated and 403 for non-members', async () => {
|
|
@@ -423,8 +433,12 @@ describe('secrets management routes', () => {
|
|
|
423
433
|
});
|
|
424
434
|
|
|
425
435
|
it('returns 404 when project_id does not belong to the workspace', async () => {
|
|
426
|
-
|
|
427
|
-
|
|
436
|
+
requireProjectForWorkspace.mockRejectedValueOnce(
|
|
437
|
+
createInterModuleKnownError(
|
|
438
|
+
projectsInterModuleContract.methods.requireProjectForWorkspace,
|
|
439
|
+
'project-not-found',
|
|
440
|
+
{projectId},
|
|
441
|
+
),
|
|
428
442
|
);
|
|
429
443
|
|
|
430
444
|
const res = await app.inject({
|
|
@@ -7,33 +7,35 @@ import {defineRoute} from '@shipfox/node-fastify';
|
|
|
7
7
|
import {z} from 'zod';
|
|
8
8
|
import {setManagedSecrets} from '#core/index.js';
|
|
9
9
|
import {toSecretDto} from '#presentation/dto/index.js';
|
|
10
|
-
import {
|
|
10
|
+
import type {ManagementAccessHelpers} from './auth.js';
|
|
11
11
|
import {translateManagementError} from './errors.js';
|
|
12
12
|
import {secretWarnings} from './warnings.js';
|
|
13
13
|
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
14
|
+
export function putSecretRoute(accessControl: ManagementAccessHelpers) {
|
|
15
|
+
return defineRoute({
|
|
16
|
+
method: 'PUT',
|
|
17
|
+
path: '/secrets/:key',
|
|
18
|
+
description: 'Create or update a user-managed secret.',
|
|
19
|
+
schema: {
|
|
20
|
+
params: z.object({workspaceId: z.string().uuid(), key: secretKeySchema}),
|
|
21
|
+
body: putSecretBodySchema,
|
|
22
|
+
response: {200: putSecretResponseSchema},
|
|
23
|
+
},
|
|
24
|
+
errorHandler: translateManagementError,
|
|
25
|
+
handler: async (request) => {
|
|
26
|
+
const {workspaceId, key} = request.params;
|
|
27
|
+
const {project_id: projectId, value} = request.body;
|
|
28
|
+
const access = await accessControl.requireManagementWrite({request, workspaceId, projectId});
|
|
29
|
+
const entries = [{key, value}];
|
|
30
|
+
const [secret] = await setManagedSecrets({
|
|
31
|
+
workspaceId,
|
|
32
|
+
projectId,
|
|
33
|
+
actorId: access.userId,
|
|
34
|
+
entries,
|
|
35
|
+
});
|
|
36
|
+
if (!secret) throw new Error('Secret write returned no row');
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
});
|
|
38
|
+
return {secret: toSecretDto(secret), warnings: secretWarnings(entries)};
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
@@ -7,33 +7,35 @@ import {defineRoute} from '@shipfox/node-fastify';
|
|
|
7
7
|
import {z} from 'zod';
|
|
8
8
|
import {setManagedVariables} from '#core/index.js';
|
|
9
9
|
import {toVariableDto} from '#presentation/dto/index.js';
|
|
10
|
-
import {
|
|
10
|
+
import type {ManagementAccessHelpers} from './auth.js';
|
|
11
11
|
import {translateManagementError} from './errors.js';
|
|
12
12
|
import {variableWarnings} from './warnings.js';
|
|
13
13
|
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
14
|
+
export function putVariableRoute(accessControl: ManagementAccessHelpers) {
|
|
15
|
+
return defineRoute({
|
|
16
|
+
method: 'PUT',
|
|
17
|
+
path: '/variables/:key',
|
|
18
|
+
description: 'Create or update a user-managed variable.',
|
|
19
|
+
schema: {
|
|
20
|
+
params: z.object({workspaceId: z.string().uuid(), key: secretKeySchema}),
|
|
21
|
+
body: putVariableBodySchema,
|
|
22
|
+
response: {200: putVariableResponseSchema},
|
|
23
|
+
},
|
|
24
|
+
errorHandler: translateManagementError,
|
|
25
|
+
handler: async (request) => {
|
|
26
|
+
const {workspaceId, key} = request.params;
|
|
27
|
+
const {project_id: projectId, value} = request.body;
|
|
28
|
+
const access = await accessControl.requireManagementWrite({request, workspaceId, projectId});
|
|
29
|
+
const entries = [{key, value}];
|
|
30
|
+
const [variable] = await setManagedVariables({
|
|
31
|
+
workspaceId,
|
|
32
|
+
projectId,
|
|
33
|
+
actorId: access.userId,
|
|
34
|
+
entries,
|
|
35
|
+
});
|
|
36
|
+
if (!variable) throw new Error('Variable write returned no row');
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
});
|
|
38
|
+
return {variable: toVariableDto(variable), warnings: variableWarnings(entries)};
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|