@syncular/server-hono 0.0.6-126 → 0.0.6-135
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/dist/console/gateway.d.ts.map +1 -1
- package/dist/console/gateway.js +799 -1200
- package/dist/console/gateway.js.map +1 -1
- package/dist/console/live-auth.d.ts +7 -0
- package/dist/console/live-auth.d.ts.map +1 -0
- package/dist/console/live-auth.js +35 -0
- package/dist/console/live-auth.js.map +1 -0
- package/dist/console/routes.d.ts +5 -1
- package/dist/console/routes.d.ts.map +1 -1
- package/dist/console/routes.js +39 -191
- package/dist/console/routes.js.map +1 -1
- package/dist/routes.d.ts +1 -1
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +86 -4
- package/dist/routes.js.map +1 -1
- package/dist/ws.d.ts +5 -12
- package/dist/ws.d.ts.map +1 -1
- package/dist/ws.js +30 -202
- package/dist/ws.js.map +1 -1
- package/package.json +6 -6
- package/src/__tests__/create-server.test.ts +26 -0
- package/src/__tests__/sync-maintenance.test.ts +257 -0
- package/src/console/gateway.ts +985 -1591
- package/src/console/live-auth.ts +39 -0
- package/src/console/routes.ts +59 -211
- package/src/routes.ts +102 -4
- package/src/ws.ts +44 -213
package/dist/console/gateway.js
CHANGED
|
@@ -2,6 +2,7 @@ import { Hono } from 'hono';
|
|
|
2
2
|
import { cors } from 'hono/cors';
|
|
3
3
|
import { describeRoute, resolver, validator as zValidator } from 'hono-openapi';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
+
import { closeUnauthenticatedSocket, parseBearerToken, parseWebSocketAuthToken, } from './live-auth.js';
|
|
5
6
|
import { ApiKeyTypeSchema, ConsoleApiKeyBulkRevokeRequestSchema, ConsoleApiKeyBulkRevokeResponseSchema, ConsoleApiKeyCreateRequestSchema, ConsoleApiKeyCreateResponseSchema, ConsoleApiKeyRevokeResponseSchema, ConsoleApiKeySchema, ConsoleClearEventsResultSchema, ConsoleClientSchema, ConsoleCommitDetailSchema, ConsoleCommitListItemSchema, ConsoleCompactResultSchema, ConsoleEvictResultSchema, ConsoleHandlerSchema, ConsoleOperationEventSchema, ConsoleOperationsQuerySchema, ConsolePaginatedResponseSchema, ConsolePaginationQuerySchema, ConsolePartitionedPaginationQuerySchema, ConsolePartitionQuerySchema, ConsolePruneEventsResultSchema, ConsolePrunePreviewSchema, ConsolePruneResultSchema, ConsoleRequestEventSchema, ConsoleRequestPayloadSchema, ConsoleTimelineItemSchema, ConsoleTimelineQuerySchema, LatencyQuerySchema, LatencyStatsResponseSchema, SyncStatsSchema, TimeseriesQuerySchema, TimeseriesStatsResponseSchema, } from './schemas.js';
|
|
6
7
|
const GatewayFailureSchema = z.object({
|
|
7
8
|
instanceId: z.string(),
|
|
@@ -230,65 +231,35 @@ function parseLocalNumericId(value) {
|
|
|
230
231
|
return null;
|
|
231
232
|
return parsed;
|
|
232
233
|
}
|
|
233
|
-
function
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
status: 404,
|
|
244
|
-
error: 'NOT_FOUND',
|
|
245
|
-
message: 'Instance not found',
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
return { ok: true, instance, localEventId: federated.localId };
|
|
249
|
-
}
|
|
250
|
-
const localEventId = parseLocalNumericId(args.id);
|
|
251
|
-
if (localEventId === null) {
|
|
252
|
-
return {
|
|
253
|
-
ok: false,
|
|
254
|
-
status: 400,
|
|
255
|
-
error: 'INVALID_FEDERATED_ID',
|
|
256
|
-
message: 'Expected either "<instanceId>:<eventId>" or "<eventId>" with an explicit instance filter.',
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
const selectedInstances = selectInstances({
|
|
260
|
-
instances: args.instances,
|
|
261
|
-
query: args.query,
|
|
262
|
-
});
|
|
234
|
+
function noInstancesSelectedResponse() {
|
|
235
|
+
return {
|
|
236
|
+
ok: false,
|
|
237
|
+
status: 400,
|
|
238
|
+
error: 'NO_INSTANCES_SELECTED',
|
|
239
|
+
message: 'No enabled instances matched the provided instance filter.',
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
function resolveSingleSelectedInstance(args) {
|
|
243
|
+
const selectedInstances = selectInstances(args);
|
|
263
244
|
if (selectedInstances.length === 0) {
|
|
264
|
-
return
|
|
265
|
-
ok: false,
|
|
266
|
-
status: 400,
|
|
267
|
-
error: 'NO_INSTANCES_SELECTED',
|
|
268
|
-
message: 'No enabled instances matched the provided instance filter.',
|
|
269
|
-
};
|
|
245
|
+
return noInstancesSelectedResponse();
|
|
270
246
|
}
|
|
271
247
|
if (selectedInstances.length > 1) {
|
|
272
248
|
return {
|
|
273
249
|
ok: false,
|
|
274
250
|
status: 400,
|
|
275
|
-
error:
|
|
276
|
-
message:
|
|
251
|
+
error: args.onMultiple.error,
|
|
252
|
+
message: args.onMultiple.message,
|
|
277
253
|
};
|
|
278
254
|
}
|
|
279
255
|
const instance = selectedInstances[0];
|
|
280
256
|
if (!instance) {
|
|
281
|
-
return
|
|
282
|
-
ok: false,
|
|
283
|
-
status: 400,
|
|
284
|
-
error: 'NO_INSTANCES_SELECTED',
|
|
285
|
-
message: 'No enabled instances matched the provided instance filter.',
|
|
286
|
-
};
|
|
257
|
+
return noInstancesSelectedResponse();
|
|
287
258
|
}
|
|
288
|
-
return { ok: true, instance
|
|
259
|
+
return { ok: true, instance };
|
|
289
260
|
}
|
|
290
|
-
function
|
|
291
|
-
const federated = parseFederatedNumericId(args.
|
|
261
|
+
function resolveFederatedOrLocalNumericTarget(args) {
|
|
262
|
+
const federated = parseFederatedNumericId(args.id);
|
|
292
263
|
if (federated) {
|
|
293
264
|
const instance = findInstanceById({
|
|
294
265
|
instances: args.instances,
|
|
@@ -302,76 +273,71 @@ function resolveCommitTarget(args) {
|
|
|
302
273
|
message: 'Instance not found',
|
|
303
274
|
};
|
|
304
275
|
}
|
|
305
|
-
return { ok: true, instance,
|
|
276
|
+
return { ok: true, instance, localId: federated.localId };
|
|
306
277
|
}
|
|
307
|
-
const
|
|
308
|
-
if (
|
|
278
|
+
const localId = parseLocalNumericId(args.id);
|
|
279
|
+
if (localId === null) {
|
|
309
280
|
return {
|
|
310
281
|
ok: false,
|
|
311
282
|
status: 400,
|
|
312
283
|
error: 'INVALID_FEDERATED_ID',
|
|
313
|
-
message:
|
|
284
|
+
message: args.invalidMessage,
|
|
314
285
|
};
|
|
315
286
|
}
|
|
316
|
-
const
|
|
287
|
+
const selection = resolveSingleSelectedInstance({
|
|
317
288
|
instances: args.instances,
|
|
318
289
|
query: args.query,
|
|
290
|
+
onMultiple: {
|
|
291
|
+
error: args.ambiguousError,
|
|
292
|
+
message: args.ambiguousMessage,
|
|
293
|
+
},
|
|
319
294
|
});
|
|
320
|
-
if (
|
|
321
|
-
return
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
295
|
+
if (!selection.ok)
|
|
296
|
+
return selection;
|
|
297
|
+
return { ok: true, instance: selection.instance, localId };
|
|
298
|
+
}
|
|
299
|
+
function resolveEventTarget(args) {
|
|
300
|
+
const resolved = resolveFederatedOrLocalNumericTarget({
|
|
301
|
+
id: args.id,
|
|
302
|
+
instances: args.instances,
|
|
303
|
+
query: args.query,
|
|
304
|
+
invalidMessage: 'Expected either "<instanceId>:<eventId>" or "<eventId>" with an explicit instance filter.',
|
|
305
|
+
ambiguousError: 'AMBIGUOUS_EVENT_ID',
|
|
306
|
+
ambiguousMessage: 'Local event IDs are ambiguous across multiple instances. Use "<instanceId>:<eventId>" or select one instance.',
|
|
307
|
+
});
|
|
308
|
+
if (!resolved.ok)
|
|
309
|
+
return resolved;
|
|
310
|
+
return {
|
|
311
|
+
ok: true,
|
|
312
|
+
instance: resolved.instance,
|
|
313
|
+
localEventId: resolved.localId,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
function resolveCommitTarget(args) {
|
|
317
|
+
const resolved = resolveFederatedOrLocalNumericTarget({
|
|
318
|
+
id: args.seq,
|
|
319
|
+
instances: args.instances,
|
|
320
|
+
query: args.query,
|
|
321
|
+
invalidMessage: 'Expected either "<instanceId>:<commitSeq>" or "<commitSeq>" with an explicit instance filter.',
|
|
322
|
+
ambiguousError: 'AMBIGUOUS_COMMIT_ID',
|
|
323
|
+
ambiguousMessage: 'Local commit IDs are ambiguous across multiple instances. Use "<instanceId>:<commitSeq>" or select one instance.',
|
|
324
|
+
});
|
|
325
|
+
if (!resolved.ok)
|
|
326
|
+
return resolved;
|
|
327
|
+
return {
|
|
328
|
+
ok: true,
|
|
329
|
+
instance: resolved.instance,
|
|
330
|
+
localCommitSeq: resolved.localId,
|
|
331
|
+
};
|
|
346
332
|
}
|
|
347
333
|
function resolveSingleInstanceTarget(args) {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
ok: false,
|
|
352
|
-
status: 400,
|
|
353
|
-
error: 'NO_INSTANCES_SELECTED',
|
|
354
|
-
message: 'No enabled instances matched the provided instance filter.',
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
if (selectedInstances.length > 1) {
|
|
358
|
-
return {
|
|
359
|
-
ok: false,
|
|
360
|
-
status: 400,
|
|
334
|
+
return resolveSingleSelectedInstance({
|
|
335
|
+
...args,
|
|
336
|
+
onMultiple: {
|
|
361
337
|
error: 'INSTANCE_REQUIRED',
|
|
362
338
|
message: 'This endpoint requires exactly one target instance. Provide `instanceId` or a single-value `instanceIds` filter.',
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
const instance = selectedInstances[0];
|
|
366
|
-
if (!instance) {
|
|
367
|
-
return {
|
|
368
|
-
ok: false,
|
|
369
|
-
status: 400,
|
|
370
|
-
error: 'NO_INSTANCES_SELECTED',
|
|
371
|
-
message: 'No enabled instances matched the provided instance filter.',
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
return { ok: true, instance };
|
|
339
|
+
},
|
|
340
|
+
});
|
|
375
341
|
}
|
|
376
342
|
function minNullable(values) {
|
|
377
343
|
const filtered = values.filter((value) => value !== null);
|
|
@@ -473,14 +439,6 @@ function resolveForwardAuthorization(args) {
|
|
|
473
439
|
}
|
|
474
440
|
return null;
|
|
475
441
|
}
|
|
476
|
-
function parseBearerToken(authHeader) {
|
|
477
|
-
const value = authHeader?.trim();
|
|
478
|
-
if (!value?.startsWith('Bearer ')) {
|
|
479
|
-
return null;
|
|
480
|
-
}
|
|
481
|
-
const token = value.slice(7).trim();
|
|
482
|
-
return token.length > 0 ? token : null;
|
|
483
|
-
}
|
|
484
442
|
async function fetchDownstreamJson(args) {
|
|
485
443
|
const url = buildConsoleEndpointUrl({
|
|
486
444
|
instance: args.instance,
|
|
@@ -741,6 +699,124 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
741
699
|
],
|
|
742
700
|
credentials: true,
|
|
743
701
|
}));
|
|
702
|
+
const withGatewayAuth = async (c, callback) => {
|
|
703
|
+
const auth = await options.authenticate(c);
|
|
704
|
+
if (!auth) {
|
|
705
|
+
return unauthorizedResponse(c);
|
|
706
|
+
}
|
|
707
|
+
return callback();
|
|
708
|
+
};
|
|
709
|
+
const proxySingleInstanceJsonRequest = async (args) => {
|
|
710
|
+
const target = resolveSingleInstanceTarget({
|
|
711
|
+
instances,
|
|
712
|
+
query: args.query,
|
|
713
|
+
});
|
|
714
|
+
if (!target.ok) {
|
|
715
|
+
return args.c.json({
|
|
716
|
+
error: target.error,
|
|
717
|
+
message: target.message,
|
|
718
|
+
}, target.status);
|
|
719
|
+
}
|
|
720
|
+
const forwardQuery = sanitizeForwardQueryParams(new URL(args.c.req.url).searchParams);
|
|
721
|
+
const result = await forwardDownstreamJsonRequest({
|
|
722
|
+
c: args.c,
|
|
723
|
+
instance: target.instance,
|
|
724
|
+
method: args.method,
|
|
725
|
+
path: args.path,
|
|
726
|
+
query: forwardQuery,
|
|
727
|
+
...(args.body === undefined ? {} : { body: args.body }),
|
|
728
|
+
responseSchema: args.responseSchema,
|
|
729
|
+
fetchImpl,
|
|
730
|
+
});
|
|
731
|
+
if (!result.ok) {
|
|
732
|
+
return jsonResponse(result.body, result.status);
|
|
733
|
+
}
|
|
734
|
+
return jsonResponse(result.data, result.status);
|
|
735
|
+
};
|
|
736
|
+
const selectTargetInstances = (c, query) => {
|
|
737
|
+
const selectedInstances = selectInstances({ instances, query });
|
|
738
|
+
if (selectedInstances.length > 0) {
|
|
739
|
+
return { ok: true, selectedInstances };
|
|
740
|
+
}
|
|
741
|
+
const noInstanceError = noInstancesSelectedResponse();
|
|
742
|
+
return {
|
|
743
|
+
ok: false,
|
|
744
|
+
response: c.json({
|
|
745
|
+
error: noInstanceError.error,
|
|
746
|
+
message: noInstanceError.message,
|
|
747
|
+
}, noInstanceError.status),
|
|
748
|
+
};
|
|
749
|
+
};
|
|
750
|
+
const fetchFromSelectedInstances = async (args) => {
|
|
751
|
+
const results = await Promise.all(args.selectedInstances.map((instance) => fetchDownstreamJson({
|
|
752
|
+
c: args.c,
|
|
753
|
+
instance,
|
|
754
|
+
path: args.path,
|
|
755
|
+
query: args.query,
|
|
756
|
+
schema: args.schema,
|
|
757
|
+
fetchImpl,
|
|
758
|
+
})));
|
|
759
|
+
const failedInstances = results
|
|
760
|
+
.filter((result) => !result.ok)
|
|
761
|
+
.map((result) => result.failure);
|
|
762
|
+
const successfulResults = results
|
|
763
|
+
.map((result, index) => ({
|
|
764
|
+
result,
|
|
765
|
+
instance: args.selectedInstances[index],
|
|
766
|
+
}))
|
|
767
|
+
.filter((entry) => Boolean(entry.instance) && entry.result.ok)
|
|
768
|
+
.map((entry) => ({
|
|
769
|
+
instance: entry.instance,
|
|
770
|
+
data: entry.result.data,
|
|
771
|
+
}));
|
|
772
|
+
if (successfulResults.length === 0) {
|
|
773
|
+
return {
|
|
774
|
+
ok: false,
|
|
775
|
+
response: allInstancesFailedResponse(args.c, failedInstances),
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
return {
|
|
779
|
+
ok: true,
|
|
780
|
+
successfulResults,
|
|
781
|
+
failedInstances,
|
|
782
|
+
};
|
|
783
|
+
};
|
|
784
|
+
const fetchPagedFromSelectedInstances = async (args) => {
|
|
785
|
+
const results = await Promise.all(args.selectedInstances.map((instance) => fetchDownstreamPaged({
|
|
786
|
+
c: args.c,
|
|
787
|
+
instance,
|
|
788
|
+
path: args.path,
|
|
789
|
+
query: args.query,
|
|
790
|
+
targetCount: args.targetCount,
|
|
791
|
+
schema: args.schema,
|
|
792
|
+
fetchImpl,
|
|
793
|
+
})));
|
|
794
|
+
const failedInstances = results
|
|
795
|
+
.filter((result) => !result.ok)
|
|
796
|
+
.map((result) => result.failure);
|
|
797
|
+
const successfulResults = results
|
|
798
|
+
.map((result, index) => ({
|
|
799
|
+
result,
|
|
800
|
+
instance: args.selectedInstances[index],
|
|
801
|
+
}))
|
|
802
|
+
.filter((entry) => Boolean(entry.instance) && entry.result.ok)
|
|
803
|
+
.map((entry) => ({
|
|
804
|
+
instance: entry.instance,
|
|
805
|
+
items: entry.result.items,
|
|
806
|
+
total: entry.result.total,
|
|
807
|
+
}));
|
|
808
|
+
if (successfulResults.length === 0) {
|
|
809
|
+
return {
|
|
810
|
+
ok: false,
|
|
811
|
+
response: allInstancesFailedResponse(args.c, failedInstances),
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
return {
|
|
815
|
+
ok: true,
|
|
816
|
+
successfulResults,
|
|
817
|
+
failedInstances,
|
|
818
|
+
};
|
|
819
|
+
};
|
|
744
820
|
routes.get('/instances', describeRoute({
|
|
745
821
|
tags: ['console-gateway'],
|
|
746
822
|
summary: 'List configured downstream console instances',
|
|
@@ -763,17 +839,15 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
763
839
|
},
|
|
764
840
|
},
|
|
765
841
|
}), async (c) => {
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
enabled: instance.enabled ?? true,
|
|
776
|
-
})),
|
|
842
|
+
return withGatewayAuth(c, async () => {
|
|
843
|
+
return c.json({
|
|
844
|
+
items: instances.map((instance) => ({
|
|
845
|
+
instanceId: instance.instanceId,
|
|
846
|
+
label: instance.label ?? instance.instanceId,
|
|
847
|
+
baseUrl: instance.baseUrl,
|
|
848
|
+
enabled: instance.enabled ?? true,
|
|
849
|
+
})),
|
|
850
|
+
});
|
|
777
851
|
});
|
|
778
852
|
});
|
|
779
853
|
routes.get('/instances/health', describeRoute({
|
|
@@ -798,34 +872,29 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
798
872
|
},
|
|
799
873
|
},
|
|
800
874
|
}), zValidator('query', GatewayInstanceFilterSchema), async (c) => {
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
875
|
+
return withGatewayAuth(c, async () => {
|
|
876
|
+
const query = c.req.valid('query');
|
|
877
|
+
const selection = selectTargetInstances(c, query);
|
|
878
|
+
if (!selection.ok) {
|
|
879
|
+
return selection.response;
|
|
880
|
+
}
|
|
881
|
+
const items = await Promise.all(selection.selectedInstances.map((instance) => checkDownstreamInstanceHealth({
|
|
882
|
+
c,
|
|
883
|
+
instance,
|
|
884
|
+
fetchImpl,
|
|
885
|
+
})));
|
|
886
|
+
const failedInstances = items
|
|
887
|
+
.filter((item) => !item.healthy)
|
|
888
|
+
.map((item) => ({
|
|
889
|
+
instanceId: item.instanceId,
|
|
890
|
+
reason: item.reason ?? 'Health probe failed',
|
|
891
|
+
...(item.status !== undefined ? { status: item.status } : {}),
|
|
892
|
+
}));
|
|
808
893
|
return c.json({
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
const items = await Promise.all(selectedInstances.map((instance) => checkDownstreamInstanceHealth({
|
|
814
|
-
c,
|
|
815
|
-
instance,
|
|
816
|
-
fetchImpl,
|
|
817
|
-
})));
|
|
818
|
-
const failedInstances = items
|
|
819
|
-
.filter((item) => !item.healthy)
|
|
820
|
-
.map((item) => ({
|
|
821
|
-
instanceId: item.instanceId,
|
|
822
|
-
reason: item.reason ?? 'Health probe failed',
|
|
823
|
-
...(item.status !== undefined ? { status: item.status } : {}),
|
|
824
|
-
}));
|
|
825
|
-
return c.json({
|
|
826
|
-
items,
|
|
827
|
-
partial: failedInstances.length > 0,
|
|
828
|
-
failedInstances,
|
|
894
|
+
items,
|
|
895
|
+
partial: failedInstances.length > 0,
|
|
896
|
+
failedInstances,
|
|
897
|
+
});
|
|
829
898
|
});
|
|
830
899
|
});
|
|
831
900
|
routes.get('/handlers', describeRoute({
|
|
@@ -842,32 +911,16 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
842
911
|
},
|
|
843
912
|
},
|
|
844
913
|
}), zValidator('query', GatewaySingleInstanceQuerySchema), async (c) => {
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
return
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
message: target.message,
|
|
855
|
-
}, target.status);
|
|
856
|
-
}
|
|
857
|
-
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
858
|
-
const result = await forwardDownstreamJsonRequest({
|
|
859
|
-
c,
|
|
860
|
-
instance: target.instance,
|
|
861
|
-
method: 'GET',
|
|
862
|
-
path: '/handlers',
|
|
863
|
-
query: forwardQuery,
|
|
864
|
-
responseSchema: GatewayHandlersResponseSchema,
|
|
865
|
-
fetchImpl,
|
|
914
|
+
return withGatewayAuth(c, async () => {
|
|
915
|
+
const query = c.req.valid('query');
|
|
916
|
+
return proxySingleInstanceJsonRequest({
|
|
917
|
+
c,
|
|
918
|
+
query,
|
|
919
|
+
method: 'GET',
|
|
920
|
+
path: '/handlers',
|
|
921
|
+
responseSchema: GatewayHandlersResponseSchema,
|
|
922
|
+
});
|
|
866
923
|
});
|
|
867
|
-
if (!result.ok) {
|
|
868
|
-
return jsonResponse(result.body, result.status);
|
|
869
|
-
}
|
|
870
|
-
return jsonResponse(result.data, result.status);
|
|
871
924
|
});
|
|
872
925
|
routes.post('/prune/preview', describeRoute({
|
|
873
926
|
tags: ['console-gateway'],
|
|
@@ -883,32 +936,16 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
883
936
|
},
|
|
884
937
|
},
|
|
885
938
|
}), zValidator('query', GatewaySingleInstanceQuerySchema), async (c) => {
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
return
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
message: target.message,
|
|
896
|
-
}, target.status);
|
|
897
|
-
}
|
|
898
|
-
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
899
|
-
const result = await forwardDownstreamJsonRequest({
|
|
900
|
-
c,
|
|
901
|
-
instance: target.instance,
|
|
902
|
-
method: 'POST',
|
|
903
|
-
path: '/prune/preview',
|
|
904
|
-
query: forwardQuery,
|
|
905
|
-
responseSchema: ConsolePrunePreviewSchema,
|
|
906
|
-
fetchImpl,
|
|
939
|
+
return withGatewayAuth(c, async () => {
|
|
940
|
+
const query = c.req.valid('query');
|
|
941
|
+
return proxySingleInstanceJsonRequest({
|
|
942
|
+
c,
|
|
943
|
+
query,
|
|
944
|
+
method: 'POST',
|
|
945
|
+
path: '/prune/preview',
|
|
946
|
+
responseSchema: ConsolePrunePreviewSchema,
|
|
947
|
+
});
|
|
907
948
|
});
|
|
908
|
-
if (!result.ok) {
|
|
909
|
-
return jsonResponse(result.body, result.status);
|
|
910
|
-
}
|
|
911
|
-
return jsonResponse(result.data, result.status);
|
|
912
949
|
});
|
|
913
950
|
routes.post('/prune', describeRoute({
|
|
914
951
|
tags: ['console-gateway'],
|
|
@@ -924,32 +961,16 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
924
961
|
},
|
|
925
962
|
},
|
|
926
963
|
}), zValidator('query', GatewaySingleInstanceQuerySchema), async (c) => {
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
return
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
message: target.message,
|
|
937
|
-
}, target.status);
|
|
938
|
-
}
|
|
939
|
-
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
940
|
-
const result = await forwardDownstreamJsonRequest({
|
|
941
|
-
c,
|
|
942
|
-
instance: target.instance,
|
|
943
|
-
method: 'POST',
|
|
944
|
-
path: '/prune',
|
|
945
|
-
query: forwardQuery,
|
|
946
|
-
responseSchema: ConsolePruneResultSchema,
|
|
947
|
-
fetchImpl,
|
|
964
|
+
return withGatewayAuth(c, async () => {
|
|
965
|
+
const query = c.req.valid('query');
|
|
966
|
+
return proxySingleInstanceJsonRequest({
|
|
967
|
+
c,
|
|
968
|
+
query,
|
|
969
|
+
method: 'POST',
|
|
970
|
+
path: '/prune',
|
|
971
|
+
responseSchema: ConsolePruneResultSchema,
|
|
972
|
+
});
|
|
948
973
|
});
|
|
949
|
-
if (!result.ok) {
|
|
950
|
-
return jsonResponse(result.body, result.status);
|
|
951
|
-
}
|
|
952
|
-
return jsonResponse(result.data, result.status);
|
|
953
974
|
});
|
|
954
975
|
routes.post('/compact', describeRoute({
|
|
955
976
|
tags: ['console-gateway'],
|
|
@@ -965,32 +986,16 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
965
986
|
},
|
|
966
987
|
},
|
|
967
988
|
}), zValidator('query', GatewaySingleInstanceQuerySchema), async (c) => {
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
return
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
message: target.message,
|
|
978
|
-
}, target.status);
|
|
979
|
-
}
|
|
980
|
-
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
981
|
-
const result = await forwardDownstreamJsonRequest({
|
|
982
|
-
c,
|
|
983
|
-
instance: target.instance,
|
|
984
|
-
method: 'POST',
|
|
985
|
-
path: '/compact',
|
|
986
|
-
query: forwardQuery,
|
|
987
|
-
responseSchema: ConsoleCompactResultSchema,
|
|
988
|
-
fetchImpl,
|
|
989
|
+
return withGatewayAuth(c, async () => {
|
|
990
|
+
const query = c.req.valid('query');
|
|
991
|
+
return proxySingleInstanceJsonRequest({
|
|
992
|
+
c,
|
|
993
|
+
query,
|
|
994
|
+
method: 'POST',
|
|
995
|
+
path: '/compact',
|
|
996
|
+
responseSchema: ConsoleCompactResultSchema,
|
|
997
|
+
});
|
|
989
998
|
});
|
|
990
|
-
if (!result.ok) {
|
|
991
|
-
return jsonResponse(result.body, result.status);
|
|
992
|
-
}
|
|
993
|
-
return jsonResponse(result.data, result.status);
|
|
994
999
|
});
|
|
995
1000
|
routes.post('/notify-data-change', describeRoute({
|
|
996
1001
|
tags: ['console-gateway'],
|
|
@@ -1006,34 +1011,18 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
1006
1011
|
},
|
|
1007
1012
|
},
|
|
1008
1013
|
}), zValidator('query', GatewaySingleInstanceQuerySchema), zValidator('json', GatewayNotifyDataChangeRequestSchema), async (c) => {
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
}, target.status);
|
|
1021
|
-
}
|
|
1022
|
-
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
1023
|
-
const result = await forwardDownstreamJsonRequest({
|
|
1024
|
-
c,
|
|
1025
|
-
instance: target.instance,
|
|
1026
|
-
method: 'POST',
|
|
1027
|
-
path: '/notify-data-change',
|
|
1028
|
-
query: forwardQuery,
|
|
1029
|
-
body,
|
|
1030
|
-
responseSchema: GatewayNotifyDataChangeResponseSchema,
|
|
1031
|
-
fetchImpl,
|
|
1014
|
+
return withGatewayAuth(c, async () => {
|
|
1015
|
+
const query = c.req.valid('query');
|
|
1016
|
+
const body = c.req.valid('json');
|
|
1017
|
+
return proxySingleInstanceJsonRequest({
|
|
1018
|
+
c,
|
|
1019
|
+
query,
|
|
1020
|
+
method: 'POST',
|
|
1021
|
+
path: '/notify-data-change',
|
|
1022
|
+
body,
|
|
1023
|
+
responseSchema: GatewayNotifyDataChangeResponseSchema,
|
|
1024
|
+
});
|
|
1032
1025
|
});
|
|
1033
|
-
if (!result.ok) {
|
|
1034
|
-
return jsonResponse(result.body, result.status);
|
|
1035
|
-
}
|
|
1036
|
-
return jsonResponse(result.data, result.status);
|
|
1037
1026
|
});
|
|
1038
1027
|
routes.delete('/clients/:id', describeRoute({
|
|
1039
1028
|
tags: ['console-gateway'],
|
|
@@ -1049,33 +1038,17 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
1049
1038
|
},
|
|
1050
1039
|
},
|
|
1051
1040
|
}), zValidator('param', GatewayClientPathParamSchema), zValidator('query', GatewaySingleInstancePartitionQuerySchema), async (c) => {
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
message: target.message,
|
|
1063
|
-
}, target.status);
|
|
1064
|
-
}
|
|
1065
|
-
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
1066
|
-
const result = await forwardDownstreamJsonRequest({
|
|
1067
|
-
c,
|
|
1068
|
-
instance: target.instance,
|
|
1069
|
-
method: 'DELETE',
|
|
1070
|
-
path: `/clients/${encodeURIComponent(id)}`,
|
|
1071
|
-
query: forwardQuery,
|
|
1072
|
-
responseSchema: ConsoleEvictResultSchema,
|
|
1073
|
-
fetchImpl,
|
|
1041
|
+
return withGatewayAuth(c, async () => {
|
|
1042
|
+
const { id } = c.req.valid('param');
|
|
1043
|
+
const query = c.req.valid('query');
|
|
1044
|
+
return proxySingleInstanceJsonRequest({
|
|
1045
|
+
c,
|
|
1046
|
+
query,
|
|
1047
|
+
method: 'DELETE',
|
|
1048
|
+
path: `/clients/${encodeURIComponent(id)}`,
|
|
1049
|
+
responseSchema: ConsoleEvictResultSchema,
|
|
1050
|
+
});
|
|
1074
1051
|
});
|
|
1075
|
-
if (!result.ok) {
|
|
1076
|
-
return jsonResponse(result.body, result.status);
|
|
1077
|
-
}
|
|
1078
|
-
return jsonResponse(result.data, result.status);
|
|
1079
1052
|
});
|
|
1080
1053
|
routes.delete('/events', describeRoute({
|
|
1081
1054
|
tags: ['console-gateway'],
|
|
@@ -1091,32 +1064,16 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
1091
1064
|
},
|
|
1092
1065
|
},
|
|
1093
1066
|
}), zValidator('query', GatewaySingleInstanceQuerySchema), async (c) => {
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
return
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
message: target.message,
|
|
1104
|
-
}, target.status);
|
|
1105
|
-
}
|
|
1106
|
-
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
1107
|
-
const result = await forwardDownstreamJsonRequest({
|
|
1108
|
-
c,
|
|
1109
|
-
instance: target.instance,
|
|
1110
|
-
method: 'DELETE',
|
|
1111
|
-
path: '/events',
|
|
1112
|
-
query: forwardQuery,
|
|
1113
|
-
responseSchema: ConsoleClearEventsResultSchema,
|
|
1114
|
-
fetchImpl,
|
|
1067
|
+
return withGatewayAuth(c, async () => {
|
|
1068
|
+
const query = c.req.valid('query');
|
|
1069
|
+
return proxySingleInstanceJsonRequest({
|
|
1070
|
+
c,
|
|
1071
|
+
query,
|
|
1072
|
+
method: 'DELETE',
|
|
1073
|
+
path: '/events',
|
|
1074
|
+
responseSchema: ConsoleClearEventsResultSchema,
|
|
1075
|
+
});
|
|
1115
1076
|
});
|
|
1116
|
-
if (!result.ok) {
|
|
1117
|
-
return jsonResponse(result.body, result.status);
|
|
1118
|
-
}
|
|
1119
|
-
return jsonResponse(result.data, result.status);
|
|
1120
1077
|
});
|
|
1121
1078
|
routes.post('/events/prune', describeRoute({
|
|
1122
1079
|
tags: ['console-gateway'],
|
|
@@ -1132,32 +1089,16 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
1132
1089
|
},
|
|
1133
1090
|
},
|
|
1134
1091
|
}), zValidator('query', GatewaySingleInstanceQuerySchema), async (c) => {
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
return
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
message: target.message,
|
|
1145
|
-
}, target.status);
|
|
1146
|
-
}
|
|
1147
|
-
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
1148
|
-
const result = await forwardDownstreamJsonRequest({
|
|
1149
|
-
c,
|
|
1150
|
-
instance: target.instance,
|
|
1151
|
-
method: 'POST',
|
|
1152
|
-
path: '/events/prune',
|
|
1153
|
-
query: forwardQuery,
|
|
1154
|
-
responseSchema: ConsolePruneEventsResultSchema,
|
|
1155
|
-
fetchImpl,
|
|
1092
|
+
return withGatewayAuth(c, async () => {
|
|
1093
|
+
const query = c.req.valid('query');
|
|
1094
|
+
return proxySingleInstanceJsonRequest({
|
|
1095
|
+
c,
|
|
1096
|
+
query,
|
|
1097
|
+
method: 'POST',
|
|
1098
|
+
path: '/events/prune',
|
|
1099
|
+
responseSchema: ConsolePruneEventsResultSchema,
|
|
1100
|
+
});
|
|
1156
1101
|
});
|
|
1157
|
-
if (!result.ok) {
|
|
1158
|
-
return jsonResponse(result.body, result.status);
|
|
1159
|
-
}
|
|
1160
|
-
return jsonResponse(result.data, result.status);
|
|
1161
1102
|
});
|
|
1162
1103
|
routes.get('/api-keys', describeRoute({
|
|
1163
1104
|
tags: ['console-gateway'],
|
|
@@ -1173,32 +1114,16 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
1173
1114
|
},
|
|
1174
1115
|
},
|
|
1175
1116
|
}), zValidator('query', GatewayApiKeysQuerySchema), async (c) => {
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
return
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
message: target.message,
|
|
1186
|
-
}, target.status);
|
|
1187
|
-
}
|
|
1188
|
-
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
1189
|
-
const result = await forwardDownstreamJsonRequest({
|
|
1190
|
-
c,
|
|
1191
|
-
instance: target.instance,
|
|
1192
|
-
method: 'GET',
|
|
1193
|
-
path: '/api-keys',
|
|
1194
|
-
query: forwardQuery,
|
|
1195
|
-
responseSchema: ConsolePaginatedResponseSchema(ConsoleApiKeySchema),
|
|
1196
|
-
fetchImpl,
|
|
1117
|
+
return withGatewayAuth(c, async () => {
|
|
1118
|
+
const query = c.req.valid('query');
|
|
1119
|
+
return proxySingleInstanceJsonRequest({
|
|
1120
|
+
c,
|
|
1121
|
+
query,
|
|
1122
|
+
method: 'GET',
|
|
1123
|
+
path: '/api-keys',
|
|
1124
|
+
responseSchema: ConsolePaginatedResponseSchema(ConsoleApiKeySchema),
|
|
1125
|
+
});
|
|
1197
1126
|
});
|
|
1198
|
-
if (!result.ok) {
|
|
1199
|
-
return jsonResponse(result.body, result.status);
|
|
1200
|
-
}
|
|
1201
|
-
return jsonResponse(result.data, result.status);
|
|
1202
1127
|
});
|
|
1203
1128
|
routes.post('/api-keys', describeRoute({
|
|
1204
1129
|
tags: ['console-gateway'],
|
|
@@ -1214,34 +1139,18 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
1214
1139
|
},
|
|
1215
1140
|
},
|
|
1216
1141
|
}), zValidator('query', GatewaySingleInstanceQuerySchema), zValidator('json', ConsoleApiKeyCreateRequestSchema), async (c) => {
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
}, target.status);
|
|
1229
|
-
}
|
|
1230
|
-
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
1231
|
-
const result = await forwardDownstreamJsonRequest({
|
|
1232
|
-
c,
|
|
1233
|
-
instance: target.instance,
|
|
1234
|
-
method: 'POST',
|
|
1235
|
-
path: '/api-keys',
|
|
1236
|
-
query: forwardQuery,
|
|
1237
|
-
body,
|
|
1238
|
-
responseSchema: ConsoleApiKeyCreateResponseSchema,
|
|
1239
|
-
fetchImpl,
|
|
1142
|
+
return withGatewayAuth(c, async () => {
|
|
1143
|
+
const query = c.req.valid('query');
|
|
1144
|
+
const body = c.req.valid('json');
|
|
1145
|
+
return proxySingleInstanceJsonRequest({
|
|
1146
|
+
c,
|
|
1147
|
+
query,
|
|
1148
|
+
method: 'POST',
|
|
1149
|
+
path: '/api-keys',
|
|
1150
|
+
body,
|
|
1151
|
+
responseSchema: ConsoleApiKeyCreateResponseSchema,
|
|
1152
|
+
});
|
|
1240
1153
|
});
|
|
1241
|
-
if (!result.ok) {
|
|
1242
|
-
return jsonResponse(result.body, result.status);
|
|
1243
|
-
}
|
|
1244
|
-
return jsonResponse(result.data, result.status);
|
|
1245
1154
|
});
|
|
1246
1155
|
routes.get('/api-keys/:id', describeRoute({
|
|
1247
1156
|
tags: ['console-gateway'],
|
|
@@ -1257,33 +1166,17 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
1257
1166
|
},
|
|
1258
1167
|
},
|
|
1259
1168
|
}), zValidator('param', GatewayApiKeyPathParamSchema), zValidator('query', GatewaySingleInstanceQuerySchema), async (c) => {
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
message: target.message,
|
|
1271
|
-
}, target.status);
|
|
1272
|
-
}
|
|
1273
|
-
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
1274
|
-
const result = await forwardDownstreamJsonRequest({
|
|
1275
|
-
c,
|
|
1276
|
-
instance: target.instance,
|
|
1277
|
-
method: 'GET',
|
|
1278
|
-
path: `/api-keys/${encodeURIComponent(id)}`,
|
|
1279
|
-
query: forwardQuery,
|
|
1280
|
-
responseSchema: ConsoleApiKeySchema,
|
|
1281
|
-
fetchImpl,
|
|
1169
|
+
return withGatewayAuth(c, async () => {
|
|
1170
|
+
const { id } = c.req.valid('param');
|
|
1171
|
+
const query = c.req.valid('query');
|
|
1172
|
+
return proxySingleInstanceJsonRequest({
|
|
1173
|
+
c,
|
|
1174
|
+
query,
|
|
1175
|
+
method: 'GET',
|
|
1176
|
+
path: `/api-keys/${encodeURIComponent(id)}`,
|
|
1177
|
+
responseSchema: ConsoleApiKeySchema,
|
|
1178
|
+
});
|
|
1282
1179
|
});
|
|
1283
|
-
if (!result.ok) {
|
|
1284
|
-
return jsonResponse(result.body, result.status);
|
|
1285
|
-
}
|
|
1286
|
-
return jsonResponse(result.data, result.status);
|
|
1287
1180
|
});
|
|
1288
1181
|
routes.delete('/api-keys/:id', describeRoute({
|
|
1289
1182
|
tags: ['console-gateway'],
|
|
@@ -1299,33 +1192,17 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
1299
1192
|
},
|
|
1300
1193
|
},
|
|
1301
1194
|
}), zValidator('param', GatewayApiKeyPathParamSchema), zValidator('query', GatewaySingleInstanceQuerySchema), async (c) => {
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
message: target.message,
|
|
1313
|
-
}, target.status);
|
|
1314
|
-
}
|
|
1315
|
-
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
1316
|
-
const result = await forwardDownstreamJsonRequest({
|
|
1317
|
-
c,
|
|
1318
|
-
instance: target.instance,
|
|
1319
|
-
method: 'DELETE',
|
|
1320
|
-
path: `/api-keys/${encodeURIComponent(id)}`,
|
|
1321
|
-
query: forwardQuery,
|
|
1322
|
-
responseSchema: ConsoleApiKeyRevokeResponseSchema,
|
|
1323
|
-
fetchImpl,
|
|
1195
|
+
return withGatewayAuth(c, async () => {
|
|
1196
|
+
const { id } = c.req.valid('param');
|
|
1197
|
+
const query = c.req.valid('query');
|
|
1198
|
+
return proxySingleInstanceJsonRequest({
|
|
1199
|
+
c,
|
|
1200
|
+
query,
|
|
1201
|
+
method: 'DELETE',
|
|
1202
|
+
path: `/api-keys/${encodeURIComponent(id)}`,
|
|
1203
|
+
responseSchema: ConsoleApiKeyRevokeResponseSchema,
|
|
1204
|
+
});
|
|
1324
1205
|
});
|
|
1325
|
-
if (!result.ok) {
|
|
1326
|
-
return jsonResponse(result.body, result.status);
|
|
1327
|
-
}
|
|
1328
|
-
return jsonResponse(result.data, result.status);
|
|
1329
1206
|
});
|
|
1330
1207
|
routes.post('/api-keys/bulk-revoke', describeRoute({
|
|
1331
1208
|
tags: ['console-gateway'],
|
|
@@ -1341,34 +1218,18 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
1341
1218
|
},
|
|
1342
1219
|
},
|
|
1343
1220
|
}), zValidator('query', GatewaySingleInstanceQuerySchema), zValidator('json', ConsoleApiKeyBulkRevokeRequestSchema), async (c) => {
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
}, target.status);
|
|
1356
|
-
}
|
|
1357
|
-
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
1358
|
-
const result = await forwardDownstreamJsonRequest({
|
|
1359
|
-
c,
|
|
1360
|
-
instance: target.instance,
|
|
1361
|
-
method: 'POST',
|
|
1362
|
-
path: '/api-keys/bulk-revoke',
|
|
1363
|
-
query: forwardQuery,
|
|
1364
|
-
body,
|
|
1365
|
-
responseSchema: ConsoleApiKeyBulkRevokeResponseSchema,
|
|
1366
|
-
fetchImpl,
|
|
1221
|
+
return withGatewayAuth(c, async () => {
|
|
1222
|
+
const query = c.req.valid('query');
|
|
1223
|
+
const body = c.req.valid('json');
|
|
1224
|
+
return proxySingleInstanceJsonRequest({
|
|
1225
|
+
c,
|
|
1226
|
+
query,
|
|
1227
|
+
method: 'POST',
|
|
1228
|
+
path: '/api-keys/bulk-revoke',
|
|
1229
|
+
body,
|
|
1230
|
+
responseSchema: ConsoleApiKeyBulkRevokeResponseSchema,
|
|
1231
|
+
});
|
|
1367
1232
|
});
|
|
1368
|
-
if (!result.ok) {
|
|
1369
|
-
return jsonResponse(result.body, result.status);
|
|
1370
|
-
}
|
|
1371
|
-
return jsonResponse(result.data, result.status);
|
|
1372
1233
|
});
|
|
1373
1234
|
routes.post('/api-keys/:id/rotate/stage', describeRoute({
|
|
1374
1235
|
tags: ['console-gateway'],
|
|
@@ -1384,33 +1245,17 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
1384
1245
|
},
|
|
1385
1246
|
},
|
|
1386
1247
|
}), zValidator('param', GatewayApiKeyPathParamSchema), zValidator('query', GatewaySingleInstanceQuerySchema), async (c) => {
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
message: target.message,
|
|
1398
|
-
}, target.status);
|
|
1399
|
-
}
|
|
1400
|
-
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
1401
|
-
const result = await forwardDownstreamJsonRequest({
|
|
1402
|
-
c,
|
|
1403
|
-
instance: target.instance,
|
|
1404
|
-
method: 'POST',
|
|
1405
|
-
path: `/api-keys/${encodeURIComponent(id)}/rotate/stage`,
|
|
1406
|
-
query: forwardQuery,
|
|
1407
|
-
responseSchema: ConsoleApiKeyCreateResponseSchema,
|
|
1408
|
-
fetchImpl,
|
|
1248
|
+
return withGatewayAuth(c, async () => {
|
|
1249
|
+
const { id } = c.req.valid('param');
|
|
1250
|
+
const query = c.req.valid('query');
|
|
1251
|
+
return proxySingleInstanceJsonRequest({
|
|
1252
|
+
c,
|
|
1253
|
+
query,
|
|
1254
|
+
method: 'POST',
|
|
1255
|
+
path: `/api-keys/${encodeURIComponent(id)}/rotate/stage`,
|
|
1256
|
+
responseSchema: ConsoleApiKeyCreateResponseSchema,
|
|
1257
|
+
});
|
|
1409
1258
|
});
|
|
1410
|
-
if (!result.ok) {
|
|
1411
|
-
return jsonResponse(result.body, result.status);
|
|
1412
|
-
}
|
|
1413
|
-
return jsonResponse(result.data, result.status);
|
|
1414
1259
|
});
|
|
1415
1260
|
routes.post('/api-keys/:id/rotate', describeRoute({
|
|
1416
1261
|
tags: ['console-gateway'],
|
|
@@ -1426,33 +1271,17 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
1426
1271
|
},
|
|
1427
1272
|
},
|
|
1428
1273
|
}), zValidator('param', GatewayApiKeyPathParamSchema), zValidator('query', GatewaySingleInstanceQuerySchema), async (c) => {
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
message: target.message,
|
|
1440
|
-
}, target.status);
|
|
1441
|
-
}
|
|
1442
|
-
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
1443
|
-
const result = await forwardDownstreamJsonRequest({
|
|
1444
|
-
c,
|
|
1445
|
-
instance: target.instance,
|
|
1446
|
-
method: 'POST',
|
|
1447
|
-
path: `/api-keys/${encodeURIComponent(id)}/rotate`,
|
|
1448
|
-
query: forwardQuery,
|
|
1449
|
-
responseSchema: ConsoleApiKeyCreateResponseSchema,
|
|
1450
|
-
fetchImpl,
|
|
1274
|
+
return withGatewayAuth(c, async () => {
|
|
1275
|
+
const { id } = c.req.valid('param');
|
|
1276
|
+
const query = c.req.valid('query');
|
|
1277
|
+
return proxySingleInstanceJsonRequest({
|
|
1278
|
+
c,
|
|
1279
|
+
query,
|
|
1280
|
+
method: 'POST',
|
|
1281
|
+
path: `/api-keys/${encodeURIComponent(id)}/rotate`,
|
|
1282
|
+
responseSchema: ConsoleApiKeyCreateResponseSchema,
|
|
1283
|
+
});
|
|
1451
1284
|
});
|
|
1452
|
-
if (!result.ok) {
|
|
1453
|
-
return jsonResponse(result.body, result.status);
|
|
1454
|
-
}
|
|
1455
|
-
return jsonResponse(result.data, result.status);
|
|
1456
1285
|
});
|
|
1457
1286
|
routes.get('/stats', describeRoute({
|
|
1458
1287
|
tags: ['console-gateway'],
|
|
@@ -1468,65 +1297,49 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
1468
1297
|
},
|
|
1469
1298
|
},
|
|
1470
1299
|
}), zValidator('query', GatewayStatsQuerySchema), async (c) => {
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1300
|
+
return withGatewayAuth(c, async () => {
|
|
1301
|
+
const query = c.req.valid('query');
|
|
1302
|
+
const selection = selectTargetInstances(c, query);
|
|
1303
|
+
if (!selection.ok) {
|
|
1304
|
+
return selection.response;
|
|
1305
|
+
}
|
|
1306
|
+
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
1307
|
+
const fetched = await fetchFromSelectedInstances({
|
|
1308
|
+
c,
|
|
1309
|
+
selectedInstances: selection.selectedInstances,
|
|
1310
|
+
path: '/stats',
|
|
1311
|
+
query: forwardQuery,
|
|
1312
|
+
schema: SyncStatsSchema,
|
|
1313
|
+
});
|
|
1314
|
+
if (!fetched.ok) {
|
|
1315
|
+
return fetched.response;
|
|
1316
|
+
}
|
|
1317
|
+
const statsByInstance = new Map();
|
|
1318
|
+
for (const result of fetched.successfulResults) {
|
|
1319
|
+
statsByInstance.set(result.instance.instanceId, result.data);
|
|
1320
|
+
}
|
|
1321
|
+
const statsValues = Array.from(statsByInstance.values());
|
|
1322
|
+
const sum = (selector) => statsValues.reduce((acc, stats) => acc + selector(stats), 0);
|
|
1323
|
+
const minCommitSeqByInstance = {};
|
|
1324
|
+
const maxCommitSeqByInstance = {};
|
|
1325
|
+
for (const [instanceId, stats] of statsByInstance.entries()) {
|
|
1326
|
+
minCommitSeqByInstance[instanceId] = stats.minCommitSeq;
|
|
1327
|
+
maxCommitSeqByInstance[instanceId] = stats.maxCommitSeq;
|
|
1328
|
+
}
|
|
1478
1329
|
return c.json({
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
const failedInstances = results
|
|
1493
|
-
.filter((result) => !result.ok)
|
|
1494
|
-
.map((result) => result.failure);
|
|
1495
|
-
const successfulResults = results.filter((result) => result.ok);
|
|
1496
|
-
if (successfulResults.length === 0) {
|
|
1497
|
-
return allInstancesFailedResponse(c, failedInstances);
|
|
1498
|
-
}
|
|
1499
|
-
const statsByInstance = new Map();
|
|
1500
|
-
for (let i = 0; i < selectedInstances.length; i++) {
|
|
1501
|
-
const result = results[i];
|
|
1502
|
-
if (!result || !result.ok)
|
|
1503
|
-
continue;
|
|
1504
|
-
const instance = selectedInstances[i];
|
|
1505
|
-
if (!instance)
|
|
1506
|
-
continue;
|
|
1507
|
-
statsByInstance.set(instance.instanceId, result.data);
|
|
1508
|
-
}
|
|
1509
|
-
const statsValues = Array.from(statsByInstance.values());
|
|
1510
|
-
const sum = (selector) => statsValues.reduce((acc, stats) => acc + selector(stats), 0);
|
|
1511
|
-
const minCommitSeqByInstance = {};
|
|
1512
|
-
const maxCommitSeqByInstance = {};
|
|
1513
|
-
for (const [instanceId, stats] of statsByInstance.entries()) {
|
|
1514
|
-
minCommitSeqByInstance[instanceId] = stats.minCommitSeq;
|
|
1515
|
-
maxCommitSeqByInstance[instanceId] = stats.maxCommitSeq;
|
|
1516
|
-
}
|
|
1517
|
-
return c.json({
|
|
1518
|
-
commitCount: sum((stats) => stats.commitCount),
|
|
1519
|
-
changeCount: sum((stats) => stats.changeCount),
|
|
1520
|
-
minCommitSeq: Math.min(...statsValues.map((stats) => stats.minCommitSeq)),
|
|
1521
|
-
maxCommitSeq: Math.max(...statsValues.map((stats) => stats.maxCommitSeq)),
|
|
1522
|
-
clientCount: sum((stats) => stats.clientCount),
|
|
1523
|
-
activeClientCount: sum((stats) => stats.activeClientCount),
|
|
1524
|
-
minActiveClientCursor: minNullable(statsValues.map((stats) => stats.minActiveClientCursor)),
|
|
1525
|
-
maxActiveClientCursor: maxNullable(statsValues.map((stats) => stats.maxActiveClientCursor)),
|
|
1526
|
-
minCommitSeqByInstance,
|
|
1527
|
-
maxCommitSeqByInstance,
|
|
1528
|
-
partial: failedInstances.length > 0,
|
|
1529
|
-
failedInstances,
|
|
1330
|
+
commitCount: sum((stats) => stats.commitCount),
|
|
1331
|
+
changeCount: sum((stats) => stats.changeCount),
|
|
1332
|
+
minCommitSeq: Math.min(...statsValues.map((stats) => stats.minCommitSeq)),
|
|
1333
|
+
maxCommitSeq: Math.max(...statsValues.map((stats) => stats.maxCommitSeq)),
|
|
1334
|
+
clientCount: sum((stats) => stats.clientCount),
|
|
1335
|
+
activeClientCount: sum((stats) => stats.activeClientCount),
|
|
1336
|
+
minActiveClientCursor: minNullable(statsValues.map((stats) => stats.minActiveClientCursor)),
|
|
1337
|
+
maxActiveClientCursor: maxNullable(statsValues.map((stats) => stats.maxActiveClientCursor)),
|
|
1338
|
+
minCommitSeqByInstance,
|
|
1339
|
+
maxCommitSeqByInstance,
|
|
1340
|
+
partial: fetched.failedInstances.length > 0,
|
|
1341
|
+
failedInstances: fetched.failedInstances,
|
|
1342
|
+
});
|
|
1530
1343
|
});
|
|
1531
1344
|
});
|
|
1532
1345
|
routes.get('/stats/timeseries', describeRoute({
|
|
@@ -1543,40 +1356,30 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
1543
1356
|
},
|
|
1544
1357
|
},
|
|
1545
1358
|
}), zValidator('query', GatewayTimeseriesQuerySchema), async (c) => {
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1359
|
+
return withGatewayAuth(c, async () => {
|
|
1360
|
+
const query = c.req.valid('query');
|
|
1361
|
+
const selection = selectTargetInstances(c, query);
|
|
1362
|
+
if (!selection.ok) {
|
|
1363
|
+
return selection.response;
|
|
1364
|
+
}
|
|
1365
|
+
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
1366
|
+
const fetched = await fetchFromSelectedInstances({
|
|
1367
|
+
c,
|
|
1368
|
+
selectedInstances: selection.selectedInstances,
|
|
1369
|
+
path: '/stats/timeseries',
|
|
1370
|
+
query: forwardQuery,
|
|
1371
|
+
schema: TimeseriesStatsResponseSchema,
|
|
1372
|
+
});
|
|
1373
|
+
if (!fetched.ok) {
|
|
1374
|
+
return fetched.response;
|
|
1375
|
+
}
|
|
1553
1376
|
return c.json({
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
c,
|
|
1561
|
-
instance,
|
|
1562
|
-
path: '/stats/timeseries',
|
|
1563
|
-
query: forwardQuery,
|
|
1564
|
-
schema: TimeseriesStatsResponseSchema,
|
|
1565
|
-
fetchImpl,
|
|
1566
|
-
})));
|
|
1567
|
-
const failedInstances = results
|
|
1568
|
-
.filter((result) => !result.ok)
|
|
1569
|
-
.map((result) => result.failure);
|
|
1570
|
-
const successfulResults = results.filter((result) => result.ok);
|
|
1571
|
-
if (successfulResults.length === 0) {
|
|
1572
|
-
return allInstancesFailedResponse(c, failedInstances);
|
|
1573
|
-
}
|
|
1574
|
-
return c.json({
|
|
1575
|
-
buckets: mergeTimeseriesBuckets(successfulResults.map((result) => result.data)),
|
|
1576
|
-
interval: query.interval,
|
|
1577
|
-
range: query.range,
|
|
1578
|
-
partial: failedInstances.length > 0,
|
|
1579
|
-
failedInstances,
|
|
1377
|
+
buckets: mergeTimeseriesBuckets(fetched.successfulResults.map((result) => result.data)),
|
|
1378
|
+
interval: query.interval,
|
|
1379
|
+
range: query.range,
|
|
1380
|
+
partial: fetched.failedInstances.length > 0,
|
|
1381
|
+
failedInstances: fetched.failedInstances,
|
|
1382
|
+
});
|
|
1580
1383
|
});
|
|
1581
1384
|
});
|
|
1582
1385
|
routes.get('/stats/latency', describeRoute({
|
|
@@ -1593,40 +1396,30 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
1593
1396
|
},
|
|
1594
1397
|
},
|
|
1595
1398
|
}), zValidator('query', GatewayLatencyQuerySchema), async (c) => {
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1399
|
+
return withGatewayAuth(c, async () => {
|
|
1400
|
+
const query = c.req.valid('query');
|
|
1401
|
+
const selection = selectTargetInstances(c, query);
|
|
1402
|
+
if (!selection.ok) {
|
|
1403
|
+
return selection.response;
|
|
1404
|
+
}
|
|
1405
|
+
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
1406
|
+
const fetched = await fetchFromSelectedInstances({
|
|
1407
|
+
c,
|
|
1408
|
+
selectedInstances: selection.selectedInstances,
|
|
1409
|
+
path: '/stats/latency',
|
|
1410
|
+
query: forwardQuery,
|
|
1411
|
+
schema: LatencyStatsResponseSchema,
|
|
1412
|
+
});
|
|
1413
|
+
if (!fetched.ok) {
|
|
1414
|
+
return fetched.response;
|
|
1415
|
+
}
|
|
1603
1416
|
return c.json({
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
c,
|
|
1611
|
-
instance,
|
|
1612
|
-
path: '/stats/latency',
|
|
1613
|
-
query: forwardQuery,
|
|
1614
|
-
schema: LatencyStatsResponseSchema,
|
|
1615
|
-
fetchImpl,
|
|
1616
|
-
})));
|
|
1617
|
-
const failedInstances = results
|
|
1618
|
-
.filter((result) => !result.ok)
|
|
1619
|
-
.map((result) => result.failure);
|
|
1620
|
-
const successfulResults = results.filter((result) => result.ok);
|
|
1621
|
-
if (successfulResults.length === 0) {
|
|
1622
|
-
return allInstancesFailedResponse(c, failedInstances);
|
|
1623
|
-
}
|
|
1624
|
-
return c.json({
|
|
1625
|
-
push: averagePercentiles(successfulResults.map((result) => result.data.push)),
|
|
1626
|
-
pull: averagePercentiles(successfulResults.map((result) => result.data.pull)),
|
|
1627
|
-
range: query.range,
|
|
1628
|
-
partial: failedInstances.length > 0,
|
|
1629
|
-
failedInstances,
|
|
1417
|
+
push: averagePercentiles(fetched.successfulResults.map((result) => result.data.push)),
|
|
1418
|
+
pull: averagePercentiles(fetched.successfulResults.map((result) => result.data.pull)),
|
|
1419
|
+
range: query.range,
|
|
1420
|
+
partial: fetched.failedInstances.length > 0,
|
|
1421
|
+
failedInstances: fetched.failedInstances,
|
|
1422
|
+
});
|
|
1630
1423
|
});
|
|
1631
1424
|
});
|
|
1632
1425
|
routes.get('/commits', describeRoute({
|
|
@@ -1643,66 +1436,51 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
1643
1436
|
},
|
|
1644
1437
|
},
|
|
1645
1438
|
}), zValidator('query', GatewayPaginatedQuerySchema), async (c) => {
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1439
|
+
return withGatewayAuth(c, async () => {
|
|
1440
|
+
const query = c.req.valid('query');
|
|
1441
|
+
const selection = selectTargetInstances(c, query);
|
|
1442
|
+
if (!selection.ok) {
|
|
1443
|
+
return selection.response;
|
|
1444
|
+
}
|
|
1445
|
+
const targetCount = query.offset + query.limit;
|
|
1446
|
+
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
1447
|
+
forwardQuery.delete('limit');
|
|
1448
|
+
forwardQuery.delete('offset');
|
|
1449
|
+
const pageSchema = ConsolePaginatedResponseSchema(ConsoleCommitListItemSchema);
|
|
1450
|
+
const fetched = await fetchPagedFromSelectedInstances({
|
|
1451
|
+
c,
|
|
1452
|
+
selectedInstances: selection.selectedInstances,
|
|
1453
|
+
path: '/commits',
|
|
1454
|
+
query: forwardQuery,
|
|
1455
|
+
targetCount,
|
|
1456
|
+
schema: pageSchema,
|
|
1457
|
+
});
|
|
1458
|
+
if (!fetched.ok) {
|
|
1459
|
+
return fetched.response;
|
|
1460
|
+
}
|
|
1461
|
+
const merged = fetched.successfulResults
|
|
1462
|
+
.flatMap(({ items, instance }) => items.map((commit) => ({
|
|
1463
|
+
...commit,
|
|
1464
|
+
instanceId: instance.instanceId,
|
|
1465
|
+
federatedCommitId: `${instance.instanceId}:${commit.commitSeq}`,
|
|
1466
|
+
})))
|
|
1467
|
+
.sort((a, b) => {
|
|
1468
|
+
const byTime = compareIsoDesc(a.createdAt, b.createdAt);
|
|
1469
|
+
if (byTime !== 0)
|
|
1470
|
+
return byTime;
|
|
1471
|
+
const byInstance = a.instanceId.localeCompare(b.instanceId);
|
|
1472
|
+
if (byInstance !== 0)
|
|
1473
|
+
return byInstance;
|
|
1474
|
+
return b.commitSeq - a.commitSeq;
|
|
1475
|
+
});
|
|
1653
1476
|
return c.json({
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
forwardQuery.delete('offset');
|
|
1662
|
-
const pageSchema = ConsolePaginatedResponseSchema(ConsoleCommitListItemSchema);
|
|
1663
|
-
const results = await Promise.all(selectedInstances.map((instance) => fetchDownstreamPaged({
|
|
1664
|
-
c,
|
|
1665
|
-
instance,
|
|
1666
|
-
path: '/commits',
|
|
1667
|
-
query: forwardQuery,
|
|
1668
|
-
targetCount,
|
|
1669
|
-
schema: pageSchema,
|
|
1670
|
-
fetchImpl,
|
|
1671
|
-
})));
|
|
1672
|
-
const failedInstances = results
|
|
1673
|
-
.filter((result) => !result.ok)
|
|
1674
|
-
.map((result) => result.failure);
|
|
1675
|
-
const successful = results
|
|
1676
|
-
.map((result, index) => ({
|
|
1677
|
-
result,
|
|
1678
|
-
instance: selectedInstances[index],
|
|
1679
|
-
}))
|
|
1680
|
-
.filter((entry) => Boolean(entry.instance) && entry.result.ok);
|
|
1681
|
-
if (successful.length === 0) {
|
|
1682
|
-
return allInstancesFailedResponse(c, failedInstances);
|
|
1683
|
-
}
|
|
1684
|
-
const merged = successful
|
|
1685
|
-
.flatMap(({ result, instance }) => result.items.map((commit) => ({
|
|
1686
|
-
...commit,
|
|
1687
|
-
instanceId: instance.instanceId,
|
|
1688
|
-
federatedCommitId: `${instance.instanceId}:${commit.commitSeq}`,
|
|
1689
|
-
})))
|
|
1690
|
-
.sort((a, b) => {
|
|
1691
|
-
const byTime = compareIsoDesc(a.createdAt, b.createdAt);
|
|
1692
|
-
if (byTime !== 0)
|
|
1693
|
-
return byTime;
|
|
1694
|
-
const byInstance = a.instanceId.localeCompare(b.instanceId);
|
|
1695
|
-
if (byInstance !== 0)
|
|
1696
|
-
return byInstance;
|
|
1697
|
-
return b.commitSeq - a.commitSeq;
|
|
1698
|
-
});
|
|
1699
|
-
return c.json({
|
|
1700
|
-
items: merged.slice(query.offset, query.offset + query.limit),
|
|
1701
|
-
total: successful.reduce((acc, entry) => acc + entry.result.total, 0),
|
|
1702
|
-
offset: query.offset,
|
|
1703
|
-
limit: query.limit,
|
|
1704
|
-
partial: failedInstances.length > 0,
|
|
1705
|
-
failedInstances,
|
|
1477
|
+
items: merged.slice(query.offset, query.offset + query.limit),
|
|
1478
|
+
total: fetched.successfulResults.reduce((acc, entry) => acc + entry.total, 0),
|
|
1479
|
+
offset: query.offset,
|
|
1480
|
+
limit: query.limit,
|
|
1481
|
+
partial: fetched.failedInstances.length > 0,
|
|
1482
|
+
failedInstances: fetched.failedInstances,
|
|
1483
|
+
});
|
|
1706
1484
|
});
|
|
1707
1485
|
});
|
|
1708
1486
|
routes.get('/commits/:seq', describeRoute({
|
|
@@ -1719,42 +1497,40 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
1719
1497
|
},
|
|
1720
1498
|
},
|
|
1721
1499
|
}), zValidator('param', GatewayCommitPathParamSchema), zValidator('query', ConsolePartitionQuerySchema.extend(GatewayInstanceFilterSchema.shape)), async (c) => {
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1500
|
+
return withGatewayAuth(c, async () => {
|
|
1501
|
+
const { seq } = c.req.valid('param');
|
|
1502
|
+
const query = c.req.valid('query');
|
|
1503
|
+
const target = resolveCommitTarget({ seq, instances, query });
|
|
1504
|
+
if (!target.ok) {
|
|
1505
|
+
return c.json({
|
|
1506
|
+
error: target.error,
|
|
1507
|
+
...(target.message ? { message: target.message } : {}),
|
|
1508
|
+
}, target.status);
|
|
1509
|
+
}
|
|
1510
|
+
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
1511
|
+
const result = await fetchDownstreamJson({
|
|
1512
|
+
c,
|
|
1513
|
+
instance: target.instance,
|
|
1514
|
+
path: `/commits/${target.localCommitSeq}`,
|
|
1515
|
+
query: forwardQuery,
|
|
1516
|
+
schema: ConsoleCommitDetailSchema,
|
|
1517
|
+
fetchImpl,
|
|
1518
|
+
});
|
|
1519
|
+
if (!result.ok) {
|
|
1520
|
+
if (result.failure.status === 404) {
|
|
1521
|
+
return c.json({ error: 'NOT_FOUND' }, 404);
|
|
1522
|
+
}
|
|
1523
|
+
return c.json({
|
|
1524
|
+
error: 'DOWNSTREAM_UNAVAILABLE',
|
|
1525
|
+
failedInstances: [result.failure],
|
|
1526
|
+
}, 502);
|
|
1747
1527
|
}
|
|
1748
1528
|
return c.json({
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
...result.data,
|
|
1755
|
-
instanceId: target.instance.instanceId,
|
|
1756
|
-
federatedCommitId: `${target.instance.instanceId}:${result.data.commitSeq}`,
|
|
1757
|
-
localCommitSeq: result.data.commitSeq,
|
|
1529
|
+
...result.data,
|
|
1530
|
+
instanceId: target.instance.instanceId,
|
|
1531
|
+
federatedCommitId: `${target.instance.instanceId}:${result.data.commitSeq}`,
|
|
1532
|
+
localCommitSeq: result.data.commitSeq,
|
|
1533
|
+
});
|
|
1758
1534
|
});
|
|
1759
1535
|
});
|
|
1760
1536
|
routes.get('/clients', describeRoute({
|
|
@@ -1771,66 +1547,51 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
1771
1547
|
},
|
|
1772
1548
|
},
|
|
1773
1549
|
}), zValidator('query', GatewayPaginatedQuerySchema), async (c) => {
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1550
|
+
return withGatewayAuth(c, async () => {
|
|
1551
|
+
const query = c.req.valid('query');
|
|
1552
|
+
const selection = selectTargetInstances(c, query);
|
|
1553
|
+
if (!selection.ok) {
|
|
1554
|
+
return selection.response;
|
|
1555
|
+
}
|
|
1556
|
+
const targetCount = query.offset + query.limit;
|
|
1557
|
+
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
1558
|
+
forwardQuery.delete('limit');
|
|
1559
|
+
forwardQuery.delete('offset');
|
|
1560
|
+
const pageSchema = ConsolePaginatedResponseSchema(ConsoleClientSchema);
|
|
1561
|
+
const fetched = await fetchPagedFromSelectedInstances({
|
|
1562
|
+
c,
|
|
1563
|
+
selectedInstances: selection.selectedInstances,
|
|
1564
|
+
path: '/clients',
|
|
1565
|
+
query: forwardQuery,
|
|
1566
|
+
targetCount,
|
|
1567
|
+
schema: pageSchema,
|
|
1568
|
+
});
|
|
1569
|
+
if (!fetched.ok) {
|
|
1570
|
+
return fetched.response;
|
|
1571
|
+
}
|
|
1572
|
+
const merged = fetched.successfulResults
|
|
1573
|
+
.flatMap(({ items, instance }) => items.map((client) => ({
|
|
1574
|
+
...client,
|
|
1575
|
+
instanceId: instance.instanceId,
|
|
1576
|
+
federatedClientId: `${instance.instanceId}:${client.clientId}`,
|
|
1577
|
+
})))
|
|
1578
|
+
.sort((a, b) => {
|
|
1579
|
+
const byTime = compareIsoDesc(a.updatedAt, b.updatedAt);
|
|
1580
|
+
if (byTime !== 0)
|
|
1581
|
+
return byTime;
|
|
1582
|
+
const byInstance = a.instanceId.localeCompare(b.instanceId);
|
|
1583
|
+
if (byInstance !== 0)
|
|
1584
|
+
return byInstance;
|
|
1585
|
+
return a.clientId.localeCompare(b.clientId);
|
|
1586
|
+
});
|
|
1781
1587
|
return c.json({
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
forwardQuery.delete('offset');
|
|
1790
|
-
const pageSchema = ConsolePaginatedResponseSchema(ConsoleClientSchema);
|
|
1791
|
-
const results = await Promise.all(selectedInstances.map((instance) => fetchDownstreamPaged({
|
|
1792
|
-
c,
|
|
1793
|
-
instance,
|
|
1794
|
-
path: '/clients',
|
|
1795
|
-
query: forwardQuery,
|
|
1796
|
-
targetCount,
|
|
1797
|
-
schema: pageSchema,
|
|
1798
|
-
fetchImpl,
|
|
1799
|
-
})));
|
|
1800
|
-
const failedInstances = results
|
|
1801
|
-
.filter((result) => !result.ok)
|
|
1802
|
-
.map((result) => result.failure);
|
|
1803
|
-
const successful = results
|
|
1804
|
-
.map((result, index) => ({
|
|
1805
|
-
result,
|
|
1806
|
-
instance: selectedInstances[index],
|
|
1807
|
-
}))
|
|
1808
|
-
.filter((entry) => Boolean(entry.instance) && entry.result.ok);
|
|
1809
|
-
if (successful.length === 0) {
|
|
1810
|
-
return allInstancesFailedResponse(c, failedInstances);
|
|
1811
|
-
}
|
|
1812
|
-
const merged = successful
|
|
1813
|
-
.flatMap(({ result, instance }) => result.items.map((client) => ({
|
|
1814
|
-
...client,
|
|
1815
|
-
instanceId: instance.instanceId,
|
|
1816
|
-
federatedClientId: `${instance.instanceId}:${client.clientId}`,
|
|
1817
|
-
})))
|
|
1818
|
-
.sort((a, b) => {
|
|
1819
|
-
const byTime = compareIsoDesc(a.updatedAt, b.updatedAt);
|
|
1820
|
-
if (byTime !== 0)
|
|
1821
|
-
return byTime;
|
|
1822
|
-
const byInstance = a.instanceId.localeCompare(b.instanceId);
|
|
1823
|
-
if (byInstance !== 0)
|
|
1824
|
-
return byInstance;
|
|
1825
|
-
return a.clientId.localeCompare(b.clientId);
|
|
1826
|
-
});
|
|
1827
|
-
return c.json({
|
|
1828
|
-
items: merged.slice(query.offset, query.offset + query.limit),
|
|
1829
|
-
total: successful.reduce((acc, entry) => acc + entry.result.total, 0),
|
|
1830
|
-
offset: query.offset,
|
|
1831
|
-
limit: query.limit,
|
|
1832
|
-
partial: failedInstances.length > 0,
|
|
1833
|
-
failedInstances,
|
|
1588
|
+
items: merged.slice(query.offset, query.offset + query.limit),
|
|
1589
|
+
total: fetched.successfulResults.reduce((acc, entry) => acc + entry.total, 0),
|
|
1590
|
+
offset: query.offset,
|
|
1591
|
+
limit: query.limit,
|
|
1592
|
+
partial: fetched.failedInstances.length > 0,
|
|
1593
|
+
failedInstances: fetched.failedInstances,
|
|
1594
|
+
});
|
|
1834
1595
|
});
|
|
1835
1596
|
});
|
|
1836
1597
|
routes.get('/timeline', describeRoute({
|
|
@@ -1847,77 +1608,64 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
1847
1608
|
},
|
|
1848
1609
|
},
|
|
1849
1610
|
}), zValidator('query', GatewayTimelineQuerySchema), async (c) => {
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1611
|
+
return withGatewayAuth(c, async () => {
|
|
1612
|
+
const query = c.req.valid('query');
|
|
1613
|
+
const selection = selectTargetInstances(c, query);
|
|
1614
|
+
if (!selection.ok) {
|
|
1615
|
+
return selection.response;
|
|
1616
|
+
}
|
|
1617
|
+
const targetCount = query.offset + query.limit;
|
|
1618
|
+
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
1619
|
+
forwardQuery.delete('limit');
|
|
1620
|
+
forwardQuery.delete('offset');
|
|
1621
|
+
const pageSchema = ConsolePaginatedResponseSchema(ConsoleTimelineItemSchema);
|
|
1622
|
+
const fetched = await fetchPagedFromSelectedInstances({
|
|
1623
|
+
c,
|
|
1624
|
+
selectedInstances: selection.selectedInstances,
|
|
1625
|
+
path: '/timeline',
|
|
1626
|
+
query: forwardQuery,
|
|
1627
|
+
targetCount,
|
|
1628
|
+
schema: pageSchema,
|
|
1629
|
+
});
|
|
1630
|
+
if (!fetched.ok) {
|
|
1631
|
+
return fetched.response;
|
|
1632
|
+
}
|
|
1633
|
+
const merged = fetched.successfulResults
|
|
1634
|
+
.flatMap(({ items, instance }) => items.map((item) => {
|
|
1635
|
+
const localCommitSeq = item.type === 'commit'
|
|
1636
|
+
? (item.commit?.commitSeq ?? null)
|
|
1637
|
+
: null;
|
|
1638
|
+
const localEventId = item.type === 'event' ? (item.event?.eventId ?? null) : null;
|
|
1639
|
+
const localIdSegment = item.type === 'commit'
|
|
1640
|
+
? String(localCommitSeq ?? 'unknown')
|
|
1641
|
+
: String(localEventId ?? 'unknown');
|
|
1642
|
+
return {
|
|
1643
|
+
...item,
|
|
1644
|
+
instanceId: instance.instanceId,
|
|
1645
|
+
federatedTimelineId: `${instance.instanceId}:${item.type}:${localIdSegment}`,
|
|
1646
|
+
localCommitSeq,
|
|
1647
|
+
localEventId,
|
|
1648
|
+
};
|
|
1649
|
+
}))
|
|
1650
|
+
.sort((a, b) => {
|
|
1651
|
+
const byTime = compareIsoDesc(a.timestamp, b.timestamp);
|
|
1652
|
+
if (byTime !== 0)
|
|
1653
|
+
return byTime;
|
|
1654
|
+
const byInstance = a.instanceId.localeCompare(b.instanceId);
|
|
1655
|
+
if (byInstance !== 0)
|
|
1656
|
+
return byInstance;
|
|
1657
|
+
const aLocalId = a.localCommitSeq ?? a.localEventId ?? 0;
|
|
1658
|
+
const bLocalId = b.localCommitSeq ?? b.localEventId ?? 0;
|
|
1659
|
+
return bLocalId - aLocalId;
|
|
1660
|
+
});
|
|
1857
1661
|
return c.json({
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
forwardQuery.delete('offset');
|
|
1866
|
-
const pageSchema = ConsolePaginatedResponseSchema(ConsoleTimelineItemSchema);
|
|
1867
|
-
const results = await Promise.all(selectedInstances.map((instance) => fetchDownstreamPaged({
|
|
1868
|
-
c,
|
|
1869
|
-
instance,
|
|
1870
|
-
path: '/timeline',
|
|
1871
|
-
query: forwardQuery,
|
|
1872
|
-
targetCount,
|
|
1873
|
-
schema: pageSchema,
|
|
1874
|
-
fetchImpl,
|
|
1875
|
-
})));
|
|
1876
|
-
const failedInstances = results
|
|
1877
|
-
.filter((result) => !result.ok)
|
|
1878
|
-
.map((result) => result.failure);
|
|
1879
|
-
const successful = results
|
|
1880
|
-
.map((result, index) => ({
|
|
1881
|
-
result,
|
|
1882
|
-
instance: selectedInstances[index],
|
|
1883
|
-
}))
|
|
1884
|
-
.filter((entry) => Boolean(entry.instance) && entry.result.ok);
|
|
1885
|
-
if (successful.length === 0) {
|
|
1886
|
-
return allInstancesFailedResponse(c, failedInstances);
|
|
1887
|
-
}
|
|
1888
|
-
const merged = successful
|
|
1889
|
-
.flatMap(({ result, instance }) => result.items.map((item) => {
|
|
1890
|
-
const localCommitSeq = item.type === 'commit' ? (item.commit?.commitSeq ?? null) : null;
|
|
1891
|
-
const localEventId = item.type === 'event' ? (item.event?.eventId ?? null) : null;
|
|
1892
|
-
const localIdSegment = item.type === 'commit'
|
|
1893
|
-
? String(localCommitSeq ?? 'unknown')
|
|
1894
|
-
: String(localEventId ?? 'unknown');
|
|
1895
|
-
return {
|
|
1896
|
-
...item,
|
|
1897
|
-
instanceId: instance.instanceId,
|
|
1898
|
-
federatedTimelineId: `${instance.instanceId}:${item.type}:${localIdSegment}`,
|
|
1899
|
-
localCommitSeq,
|
|
1900
|
-
localEventId,
|
|
1901
|
-
};
|
|
1902
|
-
}))
|
|
1903
|
-
.sort((a, b) => {
|
|
1904
|
-
const byTime = compareIsoDesc(a.timestamp, b.timestamp);
|
|
1905
|
-
if (byTime !== 0)
|
|
1906
|
-
return byTime;
|
|
1907
|
-
const byInstance = a.instanceId.localeCompare(b.instanceId);
|
|
1908
|
-
if (byInstance !== 0)
|
|
1909
|
-
return byInstance;
|
|
1910
|
-
const aLocalId = a.localCommitSeq ?? a.localEventId ?? 0;
|
|
1911
|
-
const bLocalId = b.localCommitSeq ?? b.localEventId ?? 0;
|
|
1912
|
-
return bLocalId - aLocalId;
|
|
1913
|
-
});
|
|
1914
|
-
return c.json({
|
|
1915
|
-
items: merged.slice(query.offset, query.offset + query.limit),
|
|
1916
|
-
total: successful.reduce((acc, entry) => acc + entry.result.total, 0),
|
|
1917
|
-
offset: query.offset,
|
|
1918
|
-
limit: query.limit,
|
|
1919
|
-
partial: failedInstances.length > 0,
|
|
1920
|
-
failedInstances,
|
|
1662
|
+
items: merged.slice(query.offset, query.offset + query.limit),
|
|
1663
|
+
total: fetched.successfulResults.reduce((acc, entry) => acc + entry.total, 0),
|
|
1664
|
+
offset: query.offset,
|
|
1665
|
+
limit: query.limit,
|
|
1666
|
+
partial: fetched.failedInstances.length > 0,
|
|
1667
|
+
failedInstances: fetched.failedInstances,
|
|
1668
|
+
});
|
|
1921
1669
|
});
|
|
1922
1670
|
});
|
|
1923
1671
|
routes.get('/operations', describeRoute({
|
|
@@ -1934,67 +1682,52 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
1934
1682
|
},
|
|
1935
1683
|
},
|
|
1936
1684
|
}), zValidator('query', GatewayOperationsQuerySchema), async (c) => {
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1685
|
+
return withGatewayAuth(c, async () => {
|
|
1686
|
+
const query = c.req.valid('query');
|
|
1687
|
+
const selection = selectTargetInstances(c, query);
|
|
1688
|
+
if (!selection.ok) {
|
|
1689
|
+
return selection.response;
|
|
1690
|
+
}
|
|
1691
|
+
const targetCount = query.offset + query.limit;
|
|
1692
|
+
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
1693
|
+
forwardQuery.delete('limit');
|
|
1694
|
+
forwardQuery.delete('offset');
|
|
1695
|
+
const pageSchema = ConsolePaginatedResponseSchema(ConsoleOperationEventSchema);
|
|
1696
|
+
const fetched = await fetchPagedFromSelectedInstances({
|
|
1697
|
+
c,
|
|
1698
|
+
selectedInstances: selection.selectedInstances,
|
|
1699
|
+
path: '/operations',
|
|
1700
|
+
query: forwardQuery,
|
|
1701
|
+
targetCount,
|
|
1702
|
+
schema: pageSchema,
|
|
1703
|
+
});
|
|
1704
|
+
if (!fetched.ok) {
|
|
1705
|
+
return fetched.response;
|
|
1706
|
+
}
|
|
1707
|
+
const merged = fetched.successfulResults
|
|
1708
|
+
.flatMap(({ items, instance }) => items.map((operation) => ({
|
|
1709
|
+
...operation,
|
|
1710
|
+
instanceId: instance.instanceId,
|
|
1711
|
+
federatedOperationId: `${instance.instanceId}:${operation.operationId}`,
|
|
1712
|
+
localOperationId: operation.operationId,
|
|
1713
|
+
})))
|
|
1714
|
+
.sort((a, b) => {
|
|
1715
|
+
const byTime = compareIsoDesc(a.createdAt, b.createdAt);
|
|
1716
|
+
if (byTime !== 0)
|
|
1717
|
+
return byTime;
|
|
1718
|
+
const byInstance = a.instanceId.localeCompare(b.instanceId);
|
|
1719
|
+
if (byInstance !== 0)
|
|
1720
|
+
return byInstance;
|
|
1721
|
+
return b.localOperationId - a.localOperationId;
|
|
1722
|
+
});
|
|
1944
1723
|
return c.json({
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
forwardQuery.delete('offset');
|
|
1953
|
-
const pageSchema = ConsolePaginatedResponseSchema(ConsoleOperationEventSchema);
|
|
1954
|
-
const results = await Promise.all(selectedInstances.map((instance) => fetchDownstreamPaged({
|
|
1955
|
-
c,
|
|
1956
|
-
instance,
|
|
1957
|
-
path: '/operations',
|
|
1958
|
-
query: forwardQuery,
|
|
1959
|
-
targetCount,
|
|
1960
|
-
schema: pageSchema,
|
|
1961
|
-
fetchImpl,
|
|
1962
|
-
})));
|
|
1963
|
-
const failedInstances = results
|
|
1964
|
-
.filter((result) => !result.ok)
|
|
1965
|
-
.map((result) => result.failure);
|
|
1966
|
-
const successful = results
|
|
1967
|
-
.map((result, index) => ({
|
|
1968
|
-
result,
|
|
1969
|
-
instance: selectedInstances[index],
|
|
1970
|
-
}))
|
|
1971
|
-
.filter((entry) => Boolean(entry.instance) && entry.result.ok);
|
|
1972
|
-
if (successful.length === 0) {
|
|
1973
|
-
return allInstancesFailedResponse(c, failedInstances);
|
|
1974
|
-
}
|
|
1975
|
-
const merged = successful
|
|
1976
|
-
.flatMap(({ result, instance }) => result.items.map((operation) => ({
|
|
1977
|
-
...operation,
|
|
1978
|
-
instanceId: instance.instanceId,
|
|
1979
|
-
federatedOperationId: `${instance.instanceId}:${operation.operationId}`,
|
|
1980
|
-
localOperationId: operation.operationId,
|
|
1981
|
-
})))
|
|
1982
|
-
.sort((a, b) => {
|
|
1983
|
-
const byTime = compareIsoDesc(a.createdAt, b.createdAt);
|
|
1984
|
-
if (byTime !== 0)
|
|
1985
|
-
return byTime;
|
|
1986
|
-
const byInstance = a.instanceId.localeCompare(b.instanceId);
|
|
1987
|
-
if (byInstance !== 0)
|
|
1988
|
-
return byInstance;
|
|
1989
|
-
return b.localOperationId - a.localOperationId;
|
|
1990
|
-
});
|
|
1991
|
-
return c.json({
|
|
1992
|
-
items: merged.slice(query.offset, query.offset + query.limit),
|
|
1993
|
-
total: successful.reduce((acc, entry) => acc + entry.result.total, 0),
|
|
1994
|
-
offset: query.offset,
|
|
1995
|
-
limit: query.limit,
|
|
1996
|
-
partial: failedInstances.length > 0,
|
|
1997
|
-
failedInstances,
|
|
1724
|
+
items: merged.slice(query.offset, query.offset + query.limit),
|
|
1725
|
+
total: fetched.successfulResults.reduce((acc, entry) => acc + entry.total, 0),
|
|
1726
|
+
offset: query.offset,
|
|
1727
|
+
limit: query.limit,
|
|
1728
|
+
partial: fetched.failedInstances.length > 0,
|
|
1729
|
+
failedInstances: fetched.failedInstances,
|
|
1730
|
+
});
|
|
1998
1731
|
});
|
|
1999
1732
|
});
|
|
2000
1733
|
routes.get('/events', describeRoute({
|
|
@@ -2011,67 +1744,52 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
2011
1744
|
},
|
|
2012
1745
|
},
|
|
2013
1746
|
}), zValidator('query', GatewayEventsQuerySchema), async (c) => {
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
1747
|
+
return withGatewayAuth(c, async () => {
|
|
1748
|
+
const query = c.req.valid('query');
|
|
1749
|
+
const selection = selectTargetInstances(c, query);
|
|
1750
|
+
if (!selection.ok) {
|
|
1751
|
+
return selection.response;
|
|
1752
|
+
}
|
|
1753
|
+
const targetCount = query.offset + query.limit;
|
|
1754
|
+
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
1755
|
+
forwardQuery.delete('limit');
|
|
1756
|
+
forwardQuery.delete('offset');
|
|
1757
|
+
const pageSchema = ConsolePaginatedResponseSchema(ConsoleRequestEventSchema);
|
|
1758
|
+
const fetched = await fetchPagedFromSelectedInstances({
|
|
1759
|
+
c,
|
|
1760
|
+
selectedInstances: selection.selectedInstances,
|
|
1761
|
+
path: '/events',
|
|
1762
|
+
query: forwardQuery,
|
|
1763
|
+
targetCount,
|
|
1764
|
+
schema: pageSchema,
|
|
1765
|
+
});
|
|
1766
|
+
if (!fetched.ok) {
|
|
1767
|
+
return fetched.response;
|
|
1768
|
+
}
|
|
1769
|
+
const merged = fetched.successfulResults
|
|
1770
|
+
.flatMap(({ items, instance }) => items.map((event) => ({
|
|
1771
|
+
...event,
|
|
1772
|
+
instanceId: instance.instanceId,
|
|
1773
|
+
federatedEventId: `${instance.instanceId}:${event.eventId}`,
|
|
1774
|
+
localEventId: event.eventId,
|
|
1775
|
+
})))
|
|
1776
|
+
.sort((a, b) => {
|
|
1777
|
+
const byTime = compareIsoDesc(a.createdAt, b.createdAt);
|
|
1778
|
+
if (byTime !== 0)
|
|
1779
|
+
return byTime;
|
|
1780
|
+
const byInstance = a.instanceId.localeCompare(b.instanceId);
|
|
1781
|
+
if (byInstance !== 0)
|
|
1782
|
+
return byInstance;
|
|
1783
|
+
return b.localEventId - a.localEventId;
|
|
1784
|
+
});
|
|
2021
1785
|
return c.json({
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
forwardQuery.delete('offset');
|
|
2030
|
-
const pageSchema = ConsolePaginatedResponseSchema(ConsoleRequestEventSchema);
|
|
2031
|
-
const results = await Promise.all(selectedInstances.map((instance) => fetchDownstreamPaged({
|
|
2032
|
-
c,
|
|
2033
|
-
instance,
|
|
2034
|
-
path: '/events',
|
|
2035
|
-
query: forwardQuery,
|
|
2036
|
-
targetCount,
|
|
2037
|
-
schema: pageSchema,
|
|
2038
|
-
fetchImpl,
|
|
2039
|
-
})));
|
|
2040
|
-
const failedInstances = results
|
|
2041
|
-
.filter((result) => !result.ok)
|
|
2042
|
-
.map((result) => result.failure);
|
|
2043
|
-
const successful = results
|
|
2044
|
-
.map((result, index) => ({
|
|
2045
|
-
result,
|
|
2046
|
-
instance: selectedInstances[index],
|
|
2047
|
-
}))
|
|
2048
|
-
.filter((entry) => Boolean(entry.instance) && entry.result.ok);
|
|
2049
|
-
if (successful.length === 0) {
|
|
2050
|
-
return allInstancesFailedResponse(c, failedInstances);
|
|
2051
|
-
}
|
|
2052
|
-
const merged = successful
|
|
2053
|
-
.flatMap(({ result, instance }) => result.items.map((event) => ({
|
|
2054
|
-
...event,
|
|
2055
|
-
instanceId: instance.instanceId,
|
|
2056
|
-
federatedEventId: `${instance.instanceId}:${event.eventId}`,
|
|
2057
|
-
localEventId: event.eventId,
|
|
2058
|
-
})))
|
|
2059
|
-
.sort((a, b) => {
|
|
2060
|
-
const byTime = compareIsoDesc(a.createdAt, b.createdAt);
|
|
2061
|
-
if (byTime !== 0)
|
|
2062
|
-
return byTime;
|
|
2063
|
-
const byInstance = a.instanceId.localeCompare(b.instanceId);
|
|
2064
|
-
if (byInstance !== 0)
|
|
2065
|
-
return byInstance;
|
|
2066
|
-
return b.localEventId - a.localEventId;
|
|
2067
|
-
});
|
|
2068
|
-
return c.json({
|
|
2069
|
-
items: merged.slice(query.offset, query.offset + query.limit),
|
|
2070
|
-
total: successful.reduce((acc, entry) => acc + entry.result.total, 0),
|
|
2071
|
-
offset: query.offset,
|
|
2072
|
-
limit: query.limit,
|
|
2073
|
-
partial: failedInstances.length > 0,
|
|
2074
|
-
failedInstances,
|
|
1786
|
+
items: merged.slice(query.offset, query.offset + query.limit),
|
|
1787
|
+
total: fetched.successfulResults.reduce((acc, entry) => acc + entry.total, 0),
|
|
1788
|
+
offset: query.offset,
|
|
1789
|
+
limit: query.limit,
|
|
1790
|
+
partial: fetched.failedInstances.length > 0,
|
|
1791
|
+
failedInstances: fetched.failedInstances,
|
|
1792
|
+
});
|
|
2075
1793
|
});
|
|
2076
1794
|
});
|
|
2077
1795
|
if (options.websocket?.enabled &&
|
|
@@ -2112,15 +1830,6 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
2112
1830
|
};
|
|
2113
1831
|
return options.authenticate(authContext);
|
|
2114
1832
|
};
|
|
2115
|
-
const closeUnauthenticated = (ws) => {
|
|
2116
|
-
try {
|
|
2117
|
-
ws.send(JSON.stringify({ type: 'error', message: 'UNAUTHENTICATED' }));
|
|
2118
|
-
}
|
|
2119
|
-
catch {
|
|
2120
|
-
// no-op
|
|
2121
|
-
}
|
|
2122
|
-
ws.close(4001, 'Unauthenticated');
|
|
2123
|
-
};
|
|
2124
1833
|
const cleanup = (ws) => {
|
|
2125
1834
|
const state = liveState.get(ws);
|
|
2126
1835
|
if (!state)
|
|
@@ -2156,6 +1865,7 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
2156
1865
|
heartbeatInterval: null,
|
|
2157
1866
|
authTimeout: null,
|
|
2158
1867
|
isAuthenticated: false,
|
|
1868
|
+
startAuthenticatedSession: null,
|
|
2159
1869
|
};
|
|
2160
1870
|
liveState.set(ws, state);
|
|
2161
1871
|
const startAuthenticatedSession = (upstreamBearerToken) => {
|
|
@@ -2259,6 +1969,7 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
2259
1969
|
}, heartbeatIntervalMs);
|
|
2260
1970
|
state.heartbeatInterval = heartbeatInterval;
|
|
2261
1971
|
};
|
|
1972
|
+
state.startAuthenticatedSession = startAuthenticatedSession;
|
|
2262
1973
|
if (initialAuth) {
|
|
2263
1974
|
startAuthenticatedSession(parseBearerToken(c.req.header('Authorization')));
|
|
2264
1975
|
return;
|
|
@@ -2268,7 +1979,7 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
2268
1979
|
if (!current || current.isAuthenticated) {
|
|
2269
1980
|
return;
|
|
2270
1981
|
}
|
|
2271
|
-
|
|
1982
|
+
closeUnauthenticatedSocket(ws);
|
|
2272
1983
|
cleanup(ws);
|
|
2273
1984
|
}, 5_000);
|
|
2274
1985
|
},
|
|
@@ -2278,24 +1989,13 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
2278
1989
|
return;
|
|
2279
1990
|
}
|
|
2280
1991
|
if (typeof event.data !== 'string') {
|
|
2281
|
-
|
|
1992
|
+
closeUnauthenticatedSocket(ws);
|
|
2282
1993
|
cleanup(ws);
|
|
2283
1994
|
return;
|
|
2284
1995
|
}
|
|
2285
|
-
|
|
2286
|
-
try {
|
|
2287
|
-
const parsed = JSON.parse(event.data);
|
|
2288
|
-
if (parsed.type === 'auth' &&
|
|
2289
|
-
typeof parsed.token === 'string' &&
|
|
2290
|
-
parsed.token.trim().length > 0) {
|
|
2291
|
-
token = parsed.token;
|
|
2292
|
-
}
|
|
2293
|
-
}
|
|
2294
|
-
catch {
|
|
2295
|
-
// Invalid auth message will be handled below.
|
|
2296
|
-
}
|
|
1996
|
+
const token = parseWebSocketAuthToken(event.data);
|
|
2297
1997
|
if (!token) {
|
|
2298
|
-
|
|
1998
|
+
closeUnauthenticatedSocket(ws);
|
|
2299
1999
|
cleanup(ws);
|
|
2300
2000
|
return;
|
|
2301
2001
|
}
|
|
@@ -2305,108 +2005,11 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
2305
2005
|
return;
|
|
2306
2006
|
}
|
|
2307
2007
|
if (!auth) {
|
|
2308
|
-
|
|
2008
|
+
closeUnauthenticatedSocket(ws);
|
|
2309
2009
|
cleanup(ws);
|
|
2310
2010
|
return;
|
|
2311
2011
|
}
|
|
2312
|
-
current.
|
|
2313
|
-
if (current.authTimeout) {
|
|
2314
|
-
clearTimeout(current.authTimeout);
|
|
2315
|
-
current.authTimeout = null;
|
|
2316
|
-
}
|
|
2317
|
-
for (const instance of selectedInstances) {
|
|
2318
|
-
const downstreamQuery = new URLSearchParams();
|
|
2319
|
-
if (partitionId) {
|
|
2320
|
-
downstreamQuery.set('partitionId', partitionId);
|
|
2321
|
-
}
|
|
2322
|
-
if (replaySince) {
|
|
2323
|
-
downstreamQuery.set('since', replaySince);
|
|
2324
|
-
}
|
|
2325
|
-
downstreamQuery.set('replayLimit', String(replayLimit));
|
|
2326
|
-
const downstreamUrl = buildConsoleEndpointUrl({
|
|
2327
|
-
instance,
|
|
2328
|
-
requestUrl: c.req.url,
|
|
2329
|
-
path: '/events/live',
|
|
2330
|
-
query: downstreamQuery,
|
|
2331
|
-
});
|
|
2332
|
-
const downstreamSocket = createDownstreamSocket(downstreamUrl);
|
|
2333
|
-
const upstreamToken = token.trim();
|
|
2334
|
-
const downstreamToken = instance.token?.trim() ||
|
|
2335
|
-
(upstreamToken.length > 0 ? upstreamToken : null);
|
|
2336
|
-
if (downstreamToken && downstreamSocket.send) {
|
|
2337
|
-
downstreamSocket.onopen = () => {
|
|
2338
|
-
try {
|
|
2339
|
-
downstreamSocket.send?.(JSON.stringify({
|
|
2340
|
-
type: 'auth',
|
|
2341
|
-
token: downstreamToken,
|
|
2342
|
-
}));
|
|
2343
|
-
}
|
|
2344
|
-
catch {
|
|
2345
|
-
// no-op
|
|
2346
|
-
}
|
|
2347
|
-
};
|
|
2348
|
-
}
|
|
2349
|
-
downstreamSocket.onmessage = (message) => {
|
|
2350
|
-
if (typeof message.data !== 'string') {
|
|
2351
|
-
return;
|
|
2352
|
-
}
|
|
2353
|
-
try {
|
|
2354
|
-
const payload = JSON.parse(message.data);
|
|
2355
|
-
if (typeof payload.type === 'string' &&
|
|
2356
|
-
(payload.type === 'connected' ||
|
|
2357
|
-
payload.type === 'heartbeat')) {
|
|
2358
|
-
return;
|
|
2359
|
-
}
|
|
2360
|
-
const payloadData = payload.data &&
|
|
2361
|
-
typeof payload.data === 'object' &&
|
|
2362
|
-
!Array.isArray(payload.data)
|
|
2363
|
-
? { ...payload.data, instanceId: instance.instanceId }
|
|
2364
|
-
: { instanceId: instance.instanceId };
|
|
2365
|
-
const liveEvent = {
|
|
2366
|
-
...payload,
|
|
2367
|
-
data: payloadData,
|
|
2368
|
-
instanceId: instance.instanceId,
|
|
2369
|
-
timestamp: typeof payload.timestamp === 'string'
|
|
2370
|
-
? payload.timestamp
|
|
2371
|
-
: new Date().toISOString(),
|
|
2372
|
-
};
|
|
2373
|
-
ws.send(JSON.stringify(liveEvent));
|
|
2374
|
-
}
|
|
2375
|
-
catch {
|
|
2376
|
-
// Ignore malformed downstream events
|
|
2377
|
-
}
|
|
2378
|
-
};
|
|
2379
|
-
downstreamSocket.onerror = () => {
|
|
2380
|
-
try {
|
|
2381
|
-
ws.send(JSON.stringify({
|
|
2382
|
-
type: 'instance_error',
|
|
2383
|
-
instanceId: instance.instanceId,
|
|
2384
|
-
timestamp: new Date().toISOString(),
|
|
2385
|
-
}));
|
|
2386
|
-
}
|
|
2387
|
-
catch {
|
|
2388
|
-
// ignore send errors
|
|
2389
|
-
}
|
|
2390
|
-
};
|
|
2391
|
-
current.downstreamSockets.push(downstreamSocket);
|
|
2392
|
-
}
|
|
2393
|
-
ws.send(JSON.stringify({
|
|
2394
|
-
type: 'connected',
|
|
2395
|
-
timestamp: new Date().toISOString(),
|
|
2396
|
-
instanceCount: selectedInstances.length,
|
|
2397
|
-
}));
|
|
2398
|
-
const heartbeatInterval = setInterval(() => {
|
|
2399
|
-
try {
|
|
2400
|
-
ws.send(JSON.stringify({
|
|
2401
|
-
type: 'heartbeat',
|
|
2402
|
-
timestamp: new Date().toISOString(),
|
|
2403
|
-
}));
|
|
2404
|
-
}
|
|
2405
|
-
catch {
|
|
2406
|
-
clearInterval(heartbeatInterval);
|
|
2407
|
-
}
|
|
2408
|
-
}, heartbeatIntervalMs);
|
|
2409
|
-
current.heartbeatInterval = heartbeatInterval;
|
|
2012
|
+
current.startAuthenticatedSession?.(token);
|
|
2410
2013
|
},
|
|
2411
2014
|
onClose(_event, ws) {
|
|
2412
2015
|
cleanup(ws);
|
|
@@ -2431,46 +2034,44 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
2431
2034
|
},
|
|
2432
2035
|
},
|
|
2433
2036
|
}), zValidator('param', GatewayEventPathParamSchema), zValidator('query', ConsolePartitionQuerySchema.extend(GatewayInstanceFilterSchema.shape)), async (c) => {
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2037
|
+
return withGatewayAuth(c, async () => {
|
|
2038
|
+
const { id } = c.req.valid('param');
|
|
2039
|
+
const query = c.req.valid('query');
|
|
2040
|
+
const target = resolveEventTarget({
|
|
2041
|
+
id,
|
|
2042
|
+
instances,
|
|
2043
|
+
query,
|
|
2044
|
+
});
|
|
2045
|
+
if (!target.ok) {
|
|
2046
|
+
return c.json({
|
|
2047
|
+
error: target.error,
|
|
2048
|
+
...(target.message ? { message: target.message } : {}),
|
|
2049
|
+
}, target.status);
|
|
2050
|
+
}
|
|
2051
|
+
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
2052
|
+
const result = await fetchDownstreamJson({
|
|
2053
|
+
c,
|
|
2054
|
+
instance: target.instance,
|
|
2055
|
+
path: `/events/${target.localEventId}`,
|
|
2056
|
+
query: forwardQuery,
|
|
2057
|
+
schema: ConsoleRequestEventSchema,
|
|
2058
|
+
fetchImpl,
|
|
2059
|
+
});
|
|
2060
|
+
if (!result.ok) {
|
|
2061
|
+
if (result.failure.status === 404) {
|
|
2062
|
+
return c.json({ error: 'NOT_FOUND' }, 404);
|
|
2063
|
+
}
|
|
2064
|
+
return c.json({
|
|
2065
|
+
error: 'DOWNSTREAM_UNAVAILABLE',
|
|
2066
|
+
failedInstances: [result.failure],
|
|
2067
|
+
}, 502);
|
|
2463
2068
|
}
|
|
2464
2069
|
return c.json({
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
...result.data,
|
|
2471
|
-
instanceId: target.instance.instanceId,
|
|
2472
|
-
federatedEventId: `${target.instance.instanceId}:${result.data.eventId}`,
|
|
2473
|
-
localEventId: result.data.eventId,
|
|
2070
|
+
...result.data,
|
|
2071
|
+
instanceId: target.instance.instanceId,
|
|
2072
|
+
federatedEventId: `${target.instance.instanceId}:${result.data.eventId}`,
|
|
2073
|
+
localEventId: result.data.eventId,
|
|
2074
|
+
});
|
|
2474
2075
|
});
|
|
2475
2076
|
});
|
|
2476
2077
|
routes.get('/events/:id/payload', describeRoute({
|
|
@@ -2487,46 +2088,44 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
2487
2088
|
},
|
|
2488
2089
|
},
|
|
2489
2090
|
}), zValidator('param', GatewayEventPathParamSchema), zValidator('query', ConsolePartitionQuerySchema.extend(GatewayInstanceFilterSchema.shape)), async (c) => {
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2091
|
+
return withGatewayAuth(c, async () => {
|
|
2092
|
+
const { id } = c.req.valid('param');
|
|
2093
|
+
const query = c.req.valid('query');
|
|
2094
|
+
const target = resolveEventTarget({
|
|
2095
|
+
id,
|
|
2096
|
+
instances,
|
|
2097
|
+
query,
|
|
2098
|
+
});
|
|
2099
|
+
if (!target.ok) {
|
|
2100
|
+
return c.json({
|
|
2101
|
+
error: target.error,
|
|
2102
|
+
...(target.message ? { message: target.message } : {}),
|
|
2103
|
+
}, target.status);
|
|
2104
|
+
}
|
|
2105
|
+
const forwardQuery = sanitizeForwardQueryParams(new URL(c.req.url).searchParams);
|
|
2106
|
+
const result = await fetchDownstreamJson({
|
|
2107
|
+
c,
|
|
2108
|
+
instance: target.instance,
|
|
2109
|
+
path: `/events/${target.localEventId}/payload`,
|
|
2110
|
+
query: forwardQuery,
|
|
2111
|
+
schema: ConsoleRequestPayloadSchema,
|
|
2112
|
+
fetchImpl,
|
|
2113
|
+
});
|
|
2114
|
+
if (!result.ok) {
|
|
2115
|
+
if (result.failure.status === 404) {
|
|
2116
|
+
return c.json({ error: 'NOT_FOUND' }, 404);
|
|
2117
|
+
}
|
|
2118
|
+
return c.json({
|
|
2119
|
+
error: 'DOWNSTREAM_UNAVAILABLE',
|
|
2120
|
+
failedInstances: [result.failure],
|
|
2121
|
+
}, 502);
|
|
2519
2122
|
}
|
|
2520
2123
|
return c.json({
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
...result.data,
|
|
2527
|
-
instanceId: target.instance.instanceId,
|
|
2528
|
-
federatedEventId: `${target.instance.instanceId}:${target.localEventId}`,
|
|
2529
|
-
localEventId: target.localEventId,
|
|
2124
|
+
...result.data,
|
|
2125
|
+
instanceId: target.instance.instanceId,
|
|
2126
|
+
federatedEventId: `${target.instance.instanceId}:${target.localEventId}`,
|
|
2127
|
+
localEventId: target.localEventId,
|
|
2128
|
+
});
|
|
2530
2129
|
});
|
|
2531
2130
|
});
|
|
2532
2131
|
return routes;
|