@shipfox/api-runners 10.0.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 (118) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/CHANGELOG.md +28 -0
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/config.js +8 -13
  5. package/dist/config.js.map +1 -1
  6. package/dist/core/admin-runner-instances.d.ts +34 -0
  7. package/dist/core/admin-runner-instances.d.ts.map +1 -0
  8. package/dist/core/admin-runner-instances.js +58 -0
  9. package/dist/core/admin-runner-instances.js.map +1 -0
  10. package/dist/core/errors.d.ts +6 -0
  11. package/dist/core/errors.d.ts.map +1 -1
  12. package/dist/core/errors.js +12 -0
  13. package/dist/core/errors.js.map +1 -1
  14. package/dist/core/index.d.ts +3 -2
  15. package/dist/core/index.d.ts.map +1 -1
  16. package/dist/core/index.js +3 -2
  17. package/dist/core/index.js.map +1 -1
  18. package/dist/core/provisioner-tokens.d.ts +33 -0
  19. package/dist/core/provisioner-tokens.d.ts.map +1 -1
  20. package/dist/core/provisioner-tokens.js +107 -2
  21. package/dist/core/provisioner-tokens.js.map +1 -1
  22. package/dist/db/admin-runner-instances.d.ts +34 -0
  23. package/dist/db/admin-runner-instances.d.ts.map +1 -0
  24. package/dist/db/admin-runner-instances.js +62 -0
  25. package/dist/db/admin-runner-instances.js.map +1 -0
  26. package/dist/db/db.d.ts +290 -0
  27. package/dist/db/db.d.ts.map +1 -1
  28. package/dist/db/db.js +2 -0
  29. package/dist/db/db.js.map +1 -1
  30. package/dist/db/index.d.ts +3 -1
  31. package/dist/db/index.d.ts.map +1 -1
  32. package/dist/db/index.js +2 -1
  33. package/dist/db/index.js.map +1 -1
  34. package/dist/db/job-executions.d.ts.map +1 -1
  35. package/dist/db/job-executions.js +11 -0
  36. package/dist/db/job-executions.js.map +1 -1
  37. package/dist/db/provisioner-tokens.d.ts +38 -0
  38. package/dist/db/provisioner-tokens.d.ts.map +1 -1
  39. package/dist/db/provisioner-tokens.js +157 -3
  40. package/dist/db/provisioner-tokens.js.map +1 -1
  41. package/dist/db/reservations.d.ts.map +1 -1
  42. package/dist/db/reservations.js +60 -8
  43. package/dist/db/reservations.js.map +1 -1
  44. package/dist/db/schema/admin-command-results.d.ts +134 -0
  45. package/dist/db/schema/admin-command-results.d.ts.map +1 -0
  46. package/dist/db/schema/admin-command-results.js +18 -0
  47. package/dist/db/schema/admin-command-results.js.map +1 -0
  48. package/dist/db/schema/runner-instances.d.ts +17 -0
  49. package/dist/db/schema/runner-instances.d.ts.map +1 -1
  50. package/dist/db/schema/runner-instances.js +3 -0
  51. package/dist/db/schema/runner-instances.js.map +1 -1
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +6 -1
  54. package/dist/index.js.map +1 -1
  55. package/dist/presentation/dto/admin-provisioner-token.d.ts +16 -0
  56. package/dist/presentation/dto/admin-provisioner-token.d.ts.map +1 -0
  57. package/dist/presentation/dto/admin-provisioner-token.js +22 -0
  58. package/dist/presentation/dto/admin-provisioner-token.js.map +1 -0
  59. package/dist/presentation/dto/admin-runner-instances.d.ts +23 -0
  60. package/dist/presentation/dto/admin-runner-instances.d.ts.map +1 -0
  61. package/dist/presentation/dto/admin-runner-instances.js +19 -0
  62. package/dist/presentation/dto/admin-runner-instances.js.map +1 -0
  63. package/dist/presentation/dto/index.d.ts +2 -0
  64. package/dist/presentation/dto/index.d.ts.map +1 -1
  65. package/dist/presentation/dto/index.js +2 -0
  66. package/dist/presentation/dto/index.js.map +1 -1
  67. package/dist/presentation/routes/admin-provisioner-tokens.d.ts +4 -0
  68. package/dist/presentation/routes/admin-provisioner-tokens.d.ts.map +1 -0
  69. package/dist/presentation/routes/admin-provisioner-tokens.js +167 -0
  70. package/dist/presentation/routes/admin-provisioner-tokens.js.map +1 -0
  71. package/dist/presentation/routes/admin-runner-instances.d.ts +3 -0
  72. package/dist/presentation/routes/admin-runner-instances.d.ts.map +1 -0
  73. package/dist/presentation/routes/admin-runner-instances.js +63 -0
  74. package/dist/presentation/routes/admin-runner-instances.js.map +1 -0
  75. package/dist/presentation/routes/index.d.ts.map +1 -1
  76. package/dist/presentation/routes/index.js +11 -1
  77. package/dist/presentation/routes/index.js.map +1 -1
  78. package/dist/presentation/routes/runner-enrollment.d.ts +1 -0
  79. package/dist/presentation/routes/runner-enrollment.d.ts.map +1 -1
  80. package/dist/presentation/routes/runner-enrollment.js +11 -4
  81. package/dist/presentation/routes/runner-enrollment.js.map +1 -1
  82. package/dist/tsconfig.test.tsbuildinfo +1 -1
  83. package/drizzle/0002_special_shadowcat.sql +1 -0
  84. package/drizzle/0003_flaky_sally_floyd.sql +11 -0
  85. package/drizzle/meta/0002_snapshot.json +2099 -0
  86. package/drizzle/meta/0003_snapshot.json +2178 -0
  87. package/drizzle/meta/_journal.json +14 -0
  88. package/package.json +10 -9
  89. package/src/config.test.ts +11 -0
  90. package/src/config.ts +19 -8
  91. package/src/core/admin-runner-instances.ts +125 -0
  92. package/src/core/errors.ts +14 -0
  93. package/src/core/index.ts +12 -0
  94. package/src/core/provisioner-tokens.ts +157 -1
  95. package/src/db/admin-runner-instances.ts +121 -0
  96. package/src/db/db.ts +2 -0
  97. package/src/db/index.ts +10 -0
  98. package/src/db/job-executions.test.ts +17 -0
  99. package/src/db/job-executions.ts +22 -0
  100. package/src/db/provisioner-tokens.ts +263 -9
  101. package/src/db/reservations.test.ts +253 -0
  102. package/src/db/reservations.ts +170 -15
  103. package/src/db/schema/admin-command-results.ts +29 -0
  104. package/src/db/schema/runner-instances.ts +1 -0
  105. package/src/index.test.ts +1 -1
  106. package/src/index.ts +6 -1
  107. package/src/presentation/dto/admin-provisioner-token.ts +23 -0
  108. package/src/presentation/dto/admin-runner-instances.ts +19 -0
  109. package/src/presentation/dto/index.ts +2 -0
  110. package/src/presentation/routes/admin-provisioner-tokens.test.ts +227 -0
  111. package/src/presentation/routes/admin-provisioner-tokens.ts +177 -0
  112. package/src/presentation/routes/admin-runner-instances.test.ts +671 -0
  113. package/src/presentation/routes/admin-runner-instances.ts +67 -0
  114. package/src/presentation/routes/index.ts +8 -0
  115. package/src/presentation/routes/runner-enrollment.test.ts +60 -2
  116. package/src/presentation/routes/runner-enrollment.ts +24 -4
  117. package/test/globalSetup.ts +1 -0
  118. package/tsconfig.build.tsbuildinfo +1 -1
@@ -0,0 +1,177 @@
1
+ import {AUTH_USER, requireUserContext} from '@shipfox/api-auth-context';
2
+ import {
3
+ type AuthInterModuleClient,
4
+ authInterModuleContract,
5
+ } from '@shipfox/api-auth-dto/inter-module';
6
+ import {
7
+ createAdministratorProvisionerTokenBodySchema,
8
+ createAdministratorProvisionerTokenResponseSchema,
9
+ listAdministratorProvisionerTokensQuerySchema,
10
+ listAdministratorProvisionerTokensResponseSchema,
11
+ revokeAdministratorProvisionerTokenBodySchema,
12
+ revokeAdministratorProvisionerTokenResponseSchema,
13
+ } from '@shipfox/api-runners-dto';
14
+ import {isInterModuleKnownError} from '@shipfox/inter-module';
15
+ import {decodeTimestampIdCursor, encodeTimestampIdCursor} from '@shipfox/node-drizzle';
16
+ import {ClientError, defineRoute, type RouteGroup} from '@shipfox/node-fastify';
17
+ import {z} from 'zod';
18
+ import {
19
+ createAdministratorInstallationProvisionerToken,
20
+ listAdministratorInstallationProvisionerTokens,
21
+ ProvisionerAdminIdempotencyKeyReuseError,
22
+ ProvisionerAdminIdempotencyReplayUnavailableError,
23
+ ProvisionerTokenNotFoundError,
24
+ revokeAdministratorInstallationProvisionerToken,
25
+ } from '#core/index.js';
26
+ import {toAdministratorProvisionerTokenDto} from '#presentation/dto/index.js';
27
+
28
+ const idempotencyKeyMaxLength = 256;
29
+
30
+ function requireIdempotencyKey(request: {headers: Record<string, string | string[] | undefined>}) {
31
+ const value = request.headers['idempotency-key'];
32
+ const key = Array.isArray(value) ? value[0] : value;
33
+ if (!key || key.trim().length === 0 || key.length > idempotencyKeyMaxLength) {
34
+ throw new ClientError('Idempotency-Key header is required', 'idempotency-key-required', {
35
+ status: 400,
36
+ });
37
+ }
38
+ return key;
39
+ }
40
+
41
+ function translateAdministrationError(error: unknown): never {
42
+ if (
43
+ isInterModuleKnownError(authInterModuleContract.methods.requireAdminRole, error) &&
44
+ error.code === 'admin-role-required'
45
+ ) {
46
+ throw new ClientError('Administrator role required', 'forbidden', {
47
+ status: 403,
48
+ details: {required_role: error.details.requiredRole},
49
+ });
50
+ }
51
+ if (error instanceof ProvisionerAdminIdempotencyKeyReuseError) {
52
+ throw new ClientError(
53
+ 'Idempotency-Key was already used for a different provisioner token command',
54
+ 'idempotency-key-reused',
55
+ {status: 409},
56
+ );
57
+ }
58
+ if (error instanceof ProvisionerAdminIdempotencyReplayUnavailableError) {
59
+ throw new ClientError(
60
+ 'Idempotency-Key replay cannot reproduce the original provisioner token',
61
+ 'idempotency-replay-unavailable',
62
+ {status: 409},
63
+ );
64
+ }
65
+ if (error instanceof ProvisionerTokenNotFoundError) {
66
+ throw new ClientError('Installation provisioner token not found', 'not-found', {
67
+ status: 404,
68
+ });
69
+ }
70
+ throw error;
71
+ }
72
+
73
+ export function createAdminProvisionerTokenRoutes(
74
+ auth: Pick<AuthInterModuleClient, 'requireAdminRole'>,
75
+ ): RouteGroup {
76
+ const listRoute = defineRoute({
77
+ method: 'GET',
78
+ path: '/',
79
+ auth: AUTH_USER,
80
+ description: 'List bounded installation provisioner token metadata for administrators.',
81
+ schema: {
82
+ querystring: listAdministratorProvisionerTokensQuerySchema,
83
+ response: {200: listAdministratorProvisionerTokensResponseSchema},
84
+ },
85
+ errorHandler: translateAdministrationError,
86
+ handler: async (request) => {
87
+ const actor = requireUserContext(request);
88
+ await auth.requireAdminRole({userId: actor.userId, minimumRole: 'admin-observer'});
89
+ const {status, limit, cursor} = request.query;
90
+ const decodedCursor = decodeTimestampIdCursor(cursor);
91
+ if (cursor !== undefined && !decodedCursor) {
92
+ throw new ClientError('Invalid cursor', 'invalid-cursor', {status: 400});
93
+ }
94
+ const result = await listAdministratorInstallationProvisionerTokens({
95
+ status,
96
+ limit,
97
+ ...(decodedCursor ? {cursor: decodedCursor} : {}),
98
+ });
99
+ return {
100
+ tokens: result.tokens.map(toAdministratorProvisionerTokenDto),
101
+ next_cursor: result.nextCursor ? encodeTimestampIdCursor(result.nextCursor) : null,
102
+ };
103
+ },
104
+ });
105
+
106
+ const createRoute = defineRoute({
107
+ method: 'POST',
108
+ path: '/',
109
+ auth: AUTH_USER,
110
+ description: 'Create an installation-wide provisioner token for administrators.',
111
+ schema: {
112
+ body: createAdministratorProvisionerTokenBodySchema,
113
+ response: {201: createAdministratorProvisionerTokenResponseSchema},
114
+ },
115
+ errorHandler: translateAdministrationError,
116
+ handler: async (request, reply) => {
117
+ const actor = requireUserContext(request);
118
+ const role = await auth.requireAdminRole({
119
+ userId: actor.userId,
120
+ minimumRole: 'admin-owner',
121
+ });
122
+ const result = await createAdministratorInstallationProvisionerToken({
123
+ actorId: actor.userId,
124
+ actorRole: role.role,
125
+ idempotencyKey: requireIdempotencyKey(request),
126
+ correlationId: request.id,
127
+ reason: request.body.reason,
128
+ name: request.body.name,
129
+ ttlSeconds: request.body.ttl_seconds,
130
+ });
131
+ reply.code(201);
132
+ return {
133
+ ...toAdministratorProvisionerTokenDto(result.token),
134
+ raw_token: result.rawToken,
135
+ correlation_id: result.correlationId,
136
+ };
137
+ },
138
+ });
139
+
140
+ const revokeRoute = defineRoute({
141
+ method: 'POST',
142
+ path: '/:tokenId/revoke',
143
+ auth: AUTH_USER,
144
+ description: 'Revoke an installation provisioner token and its unclaimed descendants.',
145
+ schema: {
146
+ params: z.object({tokenId: z.string().uuid()}),
147
+ body: revokeAdministratorProvisionerTokenBodySchema,
148
+ response: {200: revokeAdministratorProvisionerTokenResponseSchema},
149
+ },
150
+ errorHandler: translateAdministrationError,
151
+ handler: async (request) => {
152
+ const actor = requireUserContext(request);
153
+ const role = await auth.requireAdminRole({
154
+ userId: actor.userId,
155
+ minimumRole: 'admin-operator',
156
+ });
157
+ const result = await revokeAdministratorInstallationProvisionerToken({
158
+ actorId: actor.userId,
159
+ actorRole: role.role,
160
+ tokenId: request.params.tokenId,
161
+ idempotencyKey: requireIdempotencyKey(request),
162
+ correlationId: request.id,
163
+ reason: request.body.reason,
164
+ });
165
+ return {
166
+ ...toAdministratorProvisionerTokenDto(result.token),
167
+ correlation_id: result.correlationId,
168
+ };
169
+ },
170
+ });
171
+
172
+ return {
173
+ prefix: '/admin/runners/provisioner-tokens',
174
+ auth: AUTH_USER,
175
+ routes: [listRoute, createRoute, revokeRoute],
176
+ };
177
+ }