@syncular/server-hono 0.0.6-125 → 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
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export function parseBearerToken(
|
|
2
|
+
authHeader: string | null | undefined
|
|
3
|
+
): string | null {
|
|
4
|
+
const value = authHeader?.trim();
|
|
5
|
+
if (!value?.startsWith('Bearer ')) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
const token = value.slice(7).trim();
|
|
9
|
+
return token.length > 0 ? token : null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function parseWebSocketAuthToken(data: string): string | null {
|
|
13
|
+
try {
|
|
14
|
+
const parsed = JSON.parse(data);
|
|
15
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const payload = parsed as Record<string, unknown>;
|
|
19
|
+
if (payload.type !== 'auth' || typeof payload.token !== 'string') {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const token = payload.token.trim();
|
|
23
|
+
return token.length > 0 ? token : null;
|
|
24
|
+
} catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function closeUnauthenticatedSocket(ws: {
|
|
30
|
+
send: (data: string) => void;
|
|
31
|
+
close: (code?: number, reason?: string) => void;
|
|
32
|
+
}): void {
|
|
33
|
+
try {
|
|
34
|
+
ws.send(JSON.stringify({ type: 'error', message: 'UNAUTHENTICATED' }));
|
|
35
|
+
} catch {
|
|
36
|
+
// ignore send errors
|
|
37
|
+
}
|
|
38
|
+
ws.close(4001, 'Unauthenticated');
|
|
39
|
+
}
|
package/src/console/routes.ts
CHANGED
|
@@ -30,6 +30,11 @@ import { cors } from 'hono/cors';
|
|
|
30
30
|
import { describeRoute, resolver, validator as zValidator } from 'hono-openapi';
|
|
31
31
|
import { type Generated, type Kysely, type Selectable, sql } from 'kysely';
|
|
32
32
|
import { z } from 'zod';
|
|
33
|
+
import {
|
|
34
|
+
closeUnauthenticatedSocket,
|
|
35
|
+
parseBearerToken,
|
|
36
|
+
parseWebSocketAuthToken,
|
|
37
|
+
} from './live-auth';
|
|
33
38
|
import {
|
|
34
39
|
type ApiKeyType,
|
|
35
40
|
ApiKeyTypeSchema,
|
|
@@ -414,8 +419,12 @@ export function createConsoleRoutes<
|
|
|
414
419
|
DB extends SyncCoreDb,
|
|
415
420
|
Auth extends SyncServerAuth,
|
|
416
421
|
F extends SqlFamily = SqlFamily,
|
|
417
|
-
>(
|
|
418
|
-
|
|
422
|
+
>(
|
|
423
|
+
options: CreateConsoleRoutesOptions<DB, Auth, F>
|
|
424
|
+
): Hono<{ Variables: { consoleAuth: ConsoleAuthResult } }> {
|
|
425
|
+
const routes = new Hono<{
|
|
426
|
+
Variables: { consoleAuth: ConsoleAuthResult };
|
|
427
|
+
}>();
|
|
419
428
|
|
|
420
429
|
routes.onError((error, context) => {
|
|
421
430
|
const message =
|
|
@@ -592,14 +601,22 @@ export function createConsoleRoutes<
|
|
|
592
601
|
await next();
|
|
593
602
|
});
|
|
594
603
|
|
|
595
|
-
//
|
|
596
|
-
|
|
604
|
+
// Route auth middleware. Keep /events/live exempt to preserve websocket
|
|
605
|
+
// message-based auth handshake fallback when no Authorization header is sent.
|
|
606
|
+
routes.use('*', async (c, next) => {
|
|
607
|
+
if (c.req.method === 'OPTIONS' || c.req.path.endsWith('/events/live')) {
|
|
608
|
+
await next();
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
|
|
597
612
|
const auth = await options.authenticate(c);
|
|
598
613
|
if (!auth) {
|
|
599
|
-
return
|
|
614
|
+
return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
600
615
|
}
|
|
601
|
-
|
|
602
|
-
|
|
616
|
+
|
|
617
|
+
c.set('consoleAuth', auth);
|
|
618
|
+
await next();
|
|
619
|
+
});
|
|
603
620
|
|
|
604
621
|
const requestEventSelectColumns = [
|
|
605
622
|
'event_id',
|
|
@@ -961,8 +978,6 @@ export function createConsoleRoutes<
|
|
|
961
978
|
}),
|
|
962
979
|
zValidator('query', ConsolePartitionQuerySchema),
|
|
963
980
|
async (c) => {
|
|
964
|
-
const auth = await requireAuth(c);
|
|
965
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
966
981
|
const { partitionId } = c.req.valid('query');
|
|
967
982
|
|
|
968
983
|
const stats: SyncStats = await readSyncStats(options.db, {
|
|
@@ -971,7 +986,7 @@ export function createConsoleRoutes<
|
|
|
971
986
|
|
|
972
987
|
logSyncEvent({
|
|
973
988
|
event: 'console.stats',
|
|
974
|
-
consoleUserId:
|
|
989
|
+
consoleUserId: c.var.consoleAuth.consoleUserId,
|
|
975
990
|
});
|
|
976
991
|
|
|
977
992
|
return c.json(stats, 200);
|
|
@@ -1006,9 +1021,6 @@ export function createConsoleRoutes<
|
|
|
1006
1021
|
}),
|
|
1007
1022
|
zValidator('query', TimeseriesQuerySchema),
|
|
1008
1023
|
async (c) => {
|
|
1009
|
-
const auth = await requireAuth(c);
|
|
1010
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
1011
|
-
|
|
1012
1024
|
const { interval, range, partitionId } = c.req.valid('query');
|
|
1013
1025
|
|
|
1014
1026
|
const rangeMs = rangeToMs(range);
|
|
@@ -1205,9 +1217,6 @@ export function createConsoleRoutes<
|
|
|
1205
1217
|
}),
|
|
1206
1218
|
zValidator('query', LatencyQuerySchema),
|
|
1207
1219
|
async (c) => {
|
|
1208
|
-
const auth = await requireAuth(c);
|
|
1209
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
1210
|
-
|
|
1211
1220
|
const { range, partitionId } = c.req.valid('query');
|
|
1212
1221
|
|
|
1213
1222
|
const rangeMs = rangeToMs(range);
|
|
@@ -1321,9 +1330,6 @@ export function createConsoleRoutes<
|
|
|
1321
1330
|
}),
|
|
1322
1331
|
zValidator('query', ConsoleTimelineQuerySchema),
|
|
1323
1332
|
async (c) => {
|
|
1324
|
-
const auth = await requireAuth(c);
|
|
1325
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
1326
|
-
|
|
1327
1333
|
const {
|
|
1328
1334
|
limit,
|
|
1329
1335
|
offset,
|
|
@@ -1553,9 +1559,6 @@ export function createConsoleRoutes<
|
|
|
1553
1559
|
}),
|
|
1554
1560
|
zValidator('query', ConsolePartitionedPaginationQuerySchema),
|
|
1555
1561
|
async (c) => {
|
|
1556
|
-
const auth = await requireAuth(c);
|
|
1557
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
1558
|
-
|
|
1559
1562
|
const { limit, offset, partitionId } = c.req.valid('query');
|
|
1560
1563
|
|
|
1561
1564
|
let query = db
|
|
@@ -1651,9 +1654,6 @@ export function createConsoleRoutes<
|
|
|
1651
1654
|
zValidator('param', commitSeqParamSchema),
|
|
1652
1655
|
zValidator('query', commitDetailQuerySchema),
|
|
1653
1656
|
async (c) => {
|
|
1654
|
-
const auth = await requireAuth(c);
|
|
1655
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
1656
|
-
|
|
1657
1657
|
const { seq } = c.req.valid('param');
|
|
1658
1658
|
const { partitionId } = c.req.valid('query');
|
|
1659
1659
|
|
|
@@ -1772,9 +1772,6 @@ export function createConsoleRoutes<
|
|
|
1772
1772
|
}),
|
|
1773
1773
|
zValidator('query', ConsolePartitionedPaginationQuerySchema),
|
|
1774
1774
|
async (c) => {
|
|
1775
|
-
const auth = await requireAuth(c);
|
|
1776
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
1777
|
-
|
|
1778
1775
|
const { limit, offset, partitionId } = c.req.valid('query');
|
|
1779
1776
|
|
|
1780
1777
|
let clientsQuery = db
|
|
@@ -1940,9 +1937,6 @@ export function createConsoleRoutes<
|
|
|
1940
1937
|
},
|
|
1941
1938
|
}),
|
|
1942
1939
|
async (c) => {
|
|
1943
|
-
const auth = await requireAuth(c);
|
|
1944
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
1945
|
-
|
|
1946
1940
|
const items: ConsoleHandler[] = options.handlers.map((handler) => ({
|
|
1947
1941
|
table: handler.table,
|
|
1948
1942
|
dependsOn: handler.dependsOn,
|
|
@@ -1983,9 +1977,6 @@ export function createConsoleRoutes<
|
|
|
1983
1977
|
}),
|
|
1984
1978
|
zValidator('query', ConsoleOperationsQuerySchema),
|
|
1985
1979
|
async (c) => {
|
|
1986
|
-
const auth = await requireAuth(c);
|
|
1987
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
1988
|
-
|
|
1989
1980
|
const { limit, offset, operationType, partitionId } =
|
|
1990
1981
|
c.req.valid('query');
|
|
1991
1982
|
|
|
@@ -2055,9 +2046,6 @@ export function createConsoleRoutes<
|
|
|
2055
2046
|
},
|
|
2056
2047
|
}),
|
|
2057
2048
|
async (c) => {
|
|
2058
|
-
const auth = await requireAuth(c);
|
|
2059
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
2060
|
-
|
|
2061
2049
|
const watermarkCommitSeq = await computePruneWatermarkCommitSeq(
|
|
2062
2050
|
options.db,
|
|
2063
2051
|
options.prune
|
|
@@ -2106,9 +2094,6 @@ export function createConsoleRoutes<
|
|
|
2106
2094
|
},
|
|
2107
2095
|
}),
|
|
2108
2096
|
async (c) => {
|
|
2109
|
-
const auth = await requireAuth(c);
|
|
2110
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
2111
|
-
|
|
2112
2097
|
const watermarkCommitSeq = await computePruneWatermarkCommitSeq(
|
|
2113
2098
|
options.db,
|
|
2114
2099
|
options.prune
|
|
@@ -2121,13 +2106,13 @@ export function createConsoleRoutes<
|
|
|
2121
2106
|
|
|
2122
2107
|
logSyncEvent({
|
|
2123
2108
|
event: 'console.prune',
|
|
2124
|
-
consoleUserId:
|
|
2109
|
+
consoleUserId: c.var.consoleAuth.consoleUserId,
|
|
2125
2110
|
deletedCommits,
|
|
2126
2111
|
watermarkCommitSeq,
|
|
2127
2112
|
});
|
|
2128
2113
|
await recordOperationEvent({
|
|
2129
2114
|
operationType: 'prune',
|
|
2130
|
-
consoleUserId:
|
|
2115
|
+
consoleUserId: c.var.consoleAuth.consoleUserId,
|
|
2131
2116
|
requestPayload: {
|
|
2132
2117
|
watermarkCommitSeq,
|
|
2133
2118
|
keepNewestCommits: options.prune?.keepNewestCommits ?? null,
|
|
@@ -2167,9 +2152,6 @@ export function createConsoleRoutes<
|
|
|
2167
2152
|
},
|
|
2168
2153
|
}),
|
|
2169
2154
|
async (c) => {
|
|
2170
|
-
const auth = await requireAuth(c);
|
|
2171
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
2172
|
-
|
|
2173
2155
|
const fullHistoryHours = options.compact?.fullHistoryHours ?? 24 * 7;
|
|
2174
2156
|
|
|
2175
2157
|
const deletedChanges = await compactChanges(options.db, {
|
|
@@ -2179,13 +2161,13 @@ export function createConsoleRoutes<
|
|
|
2179
2161
|
|
|
2180
2162
|
logSyncEvent({
|
|
2181
2163
|
event: 'console.compact',
|
|
2182
|
-
consoleUserId:
|
|
2164
|
+
consoleUserId: c.var.consoleAuth.consoleUserId,
|
|
2183
2165
|
deletedChanges,
|
|
2184
2166
|
fullHistoryHours,
|
|
2185
2167
|
});
|
|
2186
2168
|
await recordOperationEvent({
|
|
2187
2169
|
operationType: 'compact',
|
|
2188
|
-
consoleUserId:
|
|
2170
|
+
consoleUserId: c.var.consoleAuth.consoleUserId,
|
|
2189
2171
|
requestPayload: { fullHistoryHours },
|
|
2190
2172
|
resultPayload: { deletedChanges },
|
|
2191
2173
|
});
|
|
@@ -2243,9 +2225,6 @@ export function createConsoleRoutes<
|
|
|
2243
2225
|
}),
|
|
2244
2226
|
zValidator('json', NotifyDataChangeRequestSchema),
|
|
2245
2227
|
async (c) => {
|
|
2246
|
-
const auth = await requireAuth(c);
|
|
2247
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
2248
|
-
|
|
2249
2228
|
const body = c.req.valid('json');
|
|
2250
2229
|
|
|
2251
2230
|
const result = await notifyExternalDataChange({
|
|
@@ -2257,14 +2236,14 @@ export function createConsoleRoutes<
|
|
|
2257
2236
|
|
|
2258
2237
|
logSyncEvent({
|
|
2259
2238
|
event: 'console.notify_data_change',
|
|
2260
|
-
consoleUserId:
|
|
2239
|
+
consoleUserId: c.var.consoleAuth.consoleUserId,
|
|
2261
2240
|
tables: body.tables,
|
|
2262
2241
|
commitSeq: result.commitSeq,
|
|
2263
2242
|
deletedChunks: result.deletedChunks,
|
|
2264
2243
|
});
|
|
2265
2244
|
await recordOperationEvent({
|
|
2266
2245
|
operationType: 'notify_data_change',
|
|
2267
|
-
consoleUserId:
|
|
2246
|
+
consoleUserId: c.var.consoleAuth.consoleUserId,
|
|
2268
2247
|
partitionId: body.partitionId ?? null,
|
|
2269
2248
|
requestPayload: {
|
|
2270
2249
|
tables: body.tables,
|
|
@@ -2315,9 +2294,6 @@ export function createConsoleRoutes<
|
|
|
2315
2294
|
zValidator('param', clientIdParamSchema),
|
|
2316
2295
|
zValidator('query', evictClientQuerySchema),
|
|
2317
2296
|
async (c) => {
|
|
2318
|
-
const auth = await requireAuth(c);
|
|
2319
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
2320
|
-
|
|
2321
2297
|
const { id: clientId } = c.req.valid('param');
|
|
2322
2298
|
const { partitionId } = c.req.valid('query');
|
|
2323
2299
|
|
|
@@ -2335,13 +2311,13 @@ export function createConsoleRoutes<
|
|
|
2335
2311
|
|
|
2336
2312
|
logSyncEvent({
|
|
2337
2313
|
event: 'console.evict_client',
|
|
2338
|
-
consoleUserId:
|
|
2314
|
+
consoleUserId: c.var.consoleAuth.consoleUserId,
|
|
2339
2315
|
clientId,
|
|
2340
2316
|
evicted,
|
|
2341
2317
|
});
|
|
2342
2318
|
await recordOperationEvent({
|
|
2343
2319
|
operationType: 'evict_client',
|
|
2344
|
-
consoleUserId:
|
|
2320
|
+
consoleUserId: c.var.consoleAuth.consoleUserId,
|
|
2345
2321
|
partitionId: partitionId ?? null,
|
|
2346
2322
|
targetClientId: clientId,
|
|
2347
2323
|
requestPayload: { clientId, partitionId: partitionId ?? null },
|
|
@@ -2383,9 +2359,6 @@ export function createConsoleRoutes<
|
|
|
2383
2359
|
}),
|
|
2384
2360
|
zValidator('query', eventsQuerySchema),
|
|
2385
2361
|
async (c) => {
|
|
2386
|
-
const auth = await requireAuth(c);
|
|
2387
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
2388
|
-
|
|
2389
2362
|
const {
|
|
2390
2363
|
limit,
|
|
2391
2364
|
offset,
|
|
@@ -2488,18 +2461,10 @@ export function createConsoleRoutes<
|
|
|
2488
2461
|
heartbeatInterval: ReturnType<typeof setInterval> | null;
|
|
2489
2462
|
authTimeout: ReturnType<typeof setTimeout> | null;
|
|
2490
2463
|
isAuthenticated: boolean;
|
|
2464
|
+
startAuthenticatedSession: (() => void) | null;
|
|
2491
2465
|
}
|
|
2492
2466
|
>();
|
|
2493
2467
|
|
|
2494
|
-
const closeUnauthenticated = (ws: WebSocketLike) => {
|
|
2495
|
-
try {
|
|
2496
|
-
ws.send(JSON.stringify({ type: 'error', message: 'UNAUTHENTICATED' }));
|
|
2497
|
-
} catch {
|
|
2498
|
-
// ignore send errors
|
|
2499
|
-
}
|
|
2500
|
-
ws.close(4001, 'Unauthenticated');
|
|
2501
|
-
};
|
|
2502
|
-
|
|
2503
2468
|
const cleanup = (ws: WebSocketLike) => {
|
|
2504
2469
|
const state = wsState.get(ws);
|
|
2505
2470
|
if (!state) return;
|
|
@@ -2553,16 +2518,18 @@ export function createConsoleRoutes<
|
|
|
2553
2518
|
|
|
2554
2519
|
return {
|
|
2555
2520
|
onOpen(_event, ws) {
|
|
2556
|
-
const state
|
|
2557
|
-
listener: null,
|
|
2558
|
-
heartbeatInterval: null,
|
|
2559
|
-
authTimeout: null,
|
|
2560
|
-
isAuthenticated: false,
|
|
2561
|
-
} as {
|
|
2521
|
+
const state: {
|
|
2562
2522
|
listener: ConsoleEventListener | null;
|
|
2563
2523
|
heartbeatInterval: ReturnType<typeof setInterval> | null;
|
|
2564
2524
|
authTimeout: ReturnType<typeof setTimeout> | null;
|
|
2565
2525
|
isAuthenticated: boolean;
|
|
2526
|
+
startAuthenticatedSession: (() => void) | null;
|
|
2527
|
+
} = {
|
|
2528
|
+
listener: null,
|
|
2529
|
+
heartbeatInterval: null,
|
|
2530
|
+
authTimeout: null,
|
|
2531
|
+
isAuthenticated: false,
|
|
2532
|
+
startAuthenticatedSession: null,
|
|
2566
2533
|
};
|
|
2567
2534
|
wsState.set(ws, state);
|
|
2568
2535
|
|
|
@@ -2629,6 +2596,7 @@ export function createConsoleRoutes<
|
|
|
2629
2596
|
}, heartbeatIntervalMs);
|
|
2630
2597
|
state.heartbeatInterval = heartbeatInterval;
|
|
2631
2598
|
};
|
|
2599
|
+
state.startAuthenticatedSession = startAuthenticatedSession;
|
|
2632
2600
|
|
|
2633
2601
|
if (initialAuth) {
|
|
2634
2602
|
startAuthenticatedSession();
|
|
@@ -2640,7 +2608,7 @@ export function createConsoleRoutes<
|
|
|
2640
2608
|
if (!current || current.isAuthenticated) {
|
|
2641
2609
|
return;
|
|
2642
2610
|
}
|
|
2643
|
-
|
|
2611
|
+
closeUnauthenticatedSocket(ws);
|
|
2644
2612
|
cleanup(ws);
|
|
2645
2613
|
}, 5_000);
|
|
2646
2614
|
},
|
|
@@ -2651,30 +2619,15 @@ export function createConsoleRoutes<
|
|
|
2651
2619
|
}
|
|
2652
2620
|
|
|
2653
2621
|
if (typeof event.data !== 'string') {
|
|
2654
|
-
|
|
2622
|
+
closeUnauthenticatedSocket(ws);
|
|
2655
2623
|
cleanup(ws);
|
|
2656
2624
|
return;
|
|
2657
2625
|
}
|
|
2658
2626
|
|
|
2659
|
-
|
|
2660
|
-
try {
|
|
2661
|
-
const parsed = JSON.parse(event.data) as {
|
|
2662
|
-
type?: unknown;
|
|
2663
|
-
token?: unknown;
|
|
2664
|
-
};
|
|
2665
|
-
if (
|
|
2666
|
-
parsed.type === 'auth' &&
|
|
2667
|
-
typeof parsed.token === 'string' &&
|
|
2668
|
-
parsed.token.trim().length > 0
|
|
2669
|
-
) {
|
|
2670
|
-
token = parsed.token;
|
|
2671
|
-
}
|
|
2672
|
-
} catch {
|
|
2673
|
-
// Ignore parse errors and close as unauthenticated below.
|
|
2674
|
-
}
|
|
2627
|
+
const token = parseWebSocketAuthToken(event.data);
|
|
2675
2628
|
|
|
2676
2629
|
if (!token) {
|
|
2677
|
-
|
|
2630
|
+
closeUnauthenticatedSocket(ws);
|
|
2678
2631
|
cleanup(ws);
|
|
2679
2632
|
return;
|
|
2680
2633
|
}
|
|
@@ -2685,71 +2638,11 @@ export function createConsoleRoutes<
|
|
|
2685
2638
|
return;
|
|
2686
2639
|
}
|
|
2687
2640
|
if (!auth) {
|
|
2688
|
-
|
|
2641
|
+
closeUnauthenticatedSocket(ws);
|
|
2689
2642
|
cleanup(ws);
|
|
2690
2643
|
return;
|
|
2691
2644
|
}
|
|
2692
|
-
|
|
2693
|
-
currentState.isAuthenticated = true;
|
|
2694
|
-
if (currentState.authTimeout) {
|
|
2695
|
-
clearTimeout(currentState.authTimeout);
|
|
2696
|
-
currentState.authTimeout = null;
|
|
2697
|
-
}
|
|
2698
|
-
|
|
2699
|
-
const listener: ConsoleEventListener = (liveEvent) => {
|
|
2700
|
-
if (partitionId) {
|
|
2701
|
-
const eventPartitionId = liveEvent.data.partitionId;
|
|
2702
|
-
if (
|
|
2703
|
-
typeof eventPartitionId !== 'string' ||
|
|
2704
|
-
eventPartitionId !== partitionId
|
|
2705
|
-
) {
|
|
2706
|
-
return;
|
|
2707
|
-
}
|
|
2708
|
-
}
|
|
2709
|
-
try {
|
|
2710
|
-
ws.send(JSON.stringify(liveEvent));
|
|
2711
|
-
} catch {
|
|
2712
|
-
// Connection closed
|
|
2713
|
-
}
|
|
2714
|
-
};
|
|
2715
|
-
|
|
2716
|
-
emitter.addListener(listener);
|
|
2717
|
-
currentState.listener = listener;
|
|
2718
|
-
|
|
2719
|
-
ws.send(
|
|
2720
|
-
JSON.stringify({
|
|
2721
|
-
type: 'connected',
|
|
2722
|
-
timestamp: new Date().toISOString(),
|
|
2723
|
-
})
|
|
2724
|
-
);
|
|
2725
|
-
|
|
2726
|
-
const replayEvents = emitter.replay({
|
|
2727
|
-
since: replaySince,
|
|
2728
|
-
limit: replayLimit,
|
|
2729
|
-
partitionId,
|
|
2730
|
-
});
|
|
2731
|
-
for (const replayEvent of replayEvents) {
|
|
2732
|
-
try {
|
|
2733
|
-
ws.send(JSON.stringify(replayEvent));
|
|
2734
|
-
} catch {
|
|
2735
|
-
// Connection closed
|
|
2736
|
-
break;
|
|
2737
|
-
}
|
|
2738
|
-
}
|
|
2739
|
-
|
|
2740
|
-
const heartbeatInterval = setInterval(() => {
|
|
2741
|
-
try {
|
|
2742
|
-
ws.send(
|
|
2743
|
-
JSON.stringify({
|
|
2744
|
-
type: 'heartbeat',
|
|
2745
|
-
timestamp: new Date().toISOString(),
|
|
2746
|
-
})
|
|
2747
|
-
);
|
|
2748
|
-
} catch {
|
|
2749
|
-
clearInterval(heartbeatInterval);
|
|
2750
|
-
}
|
|
2751
|
-
}, heartbeatIntervalMs);
|
|
2752
|
-
currentState.heartbeatInterval = heartbeatInterval;
|
|
2645
|
+
currentState.startAuthenticatedSession?.();
|
|
2753
2646
|
},
|
|
2754
2647
|
onClose(_event, ws) {
|
|
2755
2648
|
cleanup(ws);
|
|
@@ -2803,9 +2696,6 @@ export function createConsoleRoutes<
|
|
|
2803
2696
|
zValidator('param', eventIdParamSchema),
|
|
2804
2697
|
zValidator('query', eventDetailQuerySchema),
|
|
2805
2698
|
async (c) => {
|
|
2806
|
-
const auth = await requireAuth(c);
|
|
2807
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
2808
|
-
|
|
2809
2699
|
const { id: eventId } = c.req.valid('param');
|
|
2810
2700
|
const { partitionId } = c.req.valid('query');
|
|
2811
2701
|
|
|
@@ -2869,9 +2759,6 @@ export function createConsoleRoutes<
|
|
|
2869
2759
|
zValidator('param', eventIdParamSchema),
|
|
2870
2760
|
zValidator('query', eventDetailQuerySchema),
|
|
2871
2761
|
async (c) => {
|
|
2872
|
-
const auth = await requireAuth(c);
|
|
2873
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
2874
|
-
|
|
2875
2762
|
const { id: eventId } = c.req.valid('param');
|
|
2876
2763
|
const { partitionId } = c.req.valid('query');
|
|
2877
2764
|
|
|
@@ -2957,9 +2844,6 @@ export function createConsoleRoutes<
|
|
|
2957
2844
|
},
|
|
2958
2845
|
}),
|
|
2959
2846
|
async (c) => {
|
|
2960
|
-
const auth = await requireAuth(c);
|
|
2961
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
2962
|
-
|
|
2963
2847
|
const res = await db.deleteFrom('sync_request_events').executeTakeFirst();
|
|
2964
2848
|
|
|
2965
2849
|
const deletedCount = Number(res?.numDeletedRows ?? 0);
|
|
@@ -2967,7 +2851,7 @@ export function createConsoleRoutes<
|
|
|
2967
2851
|
|
|
2968
2852
|
logSyncEvent({
|
|
2969
2853
|
event: 'console.clear_events',
|
|
2970
|
-
consoleUserId:
|
|
2854
|
+
consoleUserId: c.var.consoleAuth.consoleUserId,
|
|
2971
2855
|
deletedCount,
|
|
2972
2856
|
payloadDeletedCount,
|
|
2973
2857
|
});
|
|
@@ -3004,15 +2888,12 @@ export function createConsoleRoutes<
|
|
|
3004
2888
|
},
|
|
3005
2889
|
}),
|
|
3006
2890
|
async (c) => {
|
|
3007
|
-
const auth = await requireAuth(c);
|
|
3008
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
3009
|
-
|
|
3010
2891
|
const pruneResult = await runEventsPrune();
|
|
3011
2892
|
const deletedCount = pruneResult.totalDeleted;
|
|
3012
2893
|
|
|
3013
2894
|
logSyncEvent({
|
|
3014
2895
|
event: 'console.prune_events',
|
|
3015
|
-
consoleUserId:
|
|
2896
|
+
consoleUserId: c.var.consoleAuth.consoleUserId,
|
|
3016
2897
|
deletedCount,
|
|
3017
2898
|
requestEventsDeleted: pruneResult.requestEventsDeleted,
|
|
3018
2899
|
operationEventsDeleted: pruneResult.operationEventsDeleted,
|
|
@@ -3054,9 +2935,6 @@ export function createConsoleRoutes<
|
|
|
3054
2935
|
}),
|
|
3055
2936
|
zValidator('query', apiKeysQuerySchema),
|
|
3056
2937
|
async (c) => {
|
|
3057
|
-
const auth = await requireAuth(c);
|
|
3058
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
3059
|
-
|
|
3060
2938
|
const {
|
|
3061
2939
|
limit,
|
|
3062
2940
|
offset,
|
|
@@ -3190,9 +3068,6 @@ export function createConsoleRoutes<
|
|
|
3190
3068
|
}),
|
|
3191
3069
|
zValidator('json', ConsoleApiKeyCreateRequestSchema),
|
|
3192
3070
|
async (c) => {
|
|
3193
|
-
const auth = await requireAuth(c);
|
|
3194
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
3195
|
-
|
|
3196
3071
|
const body = c.req.valid('json');
|
|
3197
3072
|
|
|
3198
3073
|
// Generate key components
|
|
@@ -3232,7 +3107,7 @@ export function createConsoleRoutes<
|
|
|
3232
3107
|
|
|
3233
3108
|
logSyncEvent({
|
|
3234
3109
|
event: 'console.create_api_key',
|
|
3235
|
-
consoleUserId:
|
|
3110
|
+
consoleUserId: c.var.consoleAuth.consoleUserId,
|
|
3236
3111
|
keyId,
|
|
3237
3112
|
keyType: body.keyType,
|
|
3238
3113
|
});
|
|
@@ -3291,9 +3166,6 @@ export function createConsoleRoutes<
|
|
|
3291
3166
|
}),
|
|
3292
3167
|
zValidator('param', apiKeyIdParamSchema),
|
|
3293
3168
|
async (c) => {
|
|
3294
|
-
const auth = await requireAuth(c);
|
|
3295
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
3296
|
-
|
|
3297
3169
|
const { id: keyId } = c.req.valid('param');
|
|
3298
3170
|
|
|
3299
3171
|
const row = await db
|
|
@@ -3362,9 +3234,6 @@ export function createConsoleRoutes<
|
|
|
3362
3234
|
}),
|
|
3363
3235
|
zValidator('param', apiKeyIdParamSchema),
|
|
3364
3236
|
async (c) => {
|
|
3365
|
-
const auth = await requireAuth(c);
|
|
3366
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
3367
|
-
|
|
3368
3237
|
const { id: keyId } = c.req.valid('param');
|
|
3369
3238
|
const now = new Date().toISOString();
|
|
3370
3239
|
|
|
@@ -3379,7 +3248,7 @@ export function createConsoleRoutes<
|
|
|
3379
3248
|
|
|
3380
3249
|
logSyncEvent({
|
|
3381
3250
|
event: 'console.revoke_api_key',
|
|
3382
|
-
consoleUserId:
|
|
3251
|
+
consoleUserId: c.var.consoleAuth.consoleUserId,
|
|
3383
3252
|
keyId,
|
|
3384
3253
|
revoked,
|
|
3385
3254
|
});
|
|
@@ -3422,9 +3291,6 @@ export function createConsoleRoutes<
|
|
|
3422
3291
|
}),
|
|
3423
3292
|
zValidator('json', ConsoleApiKeyBulkRevokeRequestSchema),
|
|
3424
3293
|
async (c) => {
|
|
3425
|
-
const auth = await requireAuth(c);
|
|
3426
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
3427
|
-
|
|
3428
3294
|
const body = c.req.valid('json');
|
|
3429
3295
|
const keyIds = [...new Set(body.keyIds.map((keyId) => keyId.trim()))]
|
|
3430
3296
|
.filter((keyId) => keyId.length > 0)
|
|
@@ -3487,7 +3353,7 @@ export function createConsoleRoutes<
|
|
|
3487
3353
|
|
|
3488
3354
|
logSyncEvent({
|
|
3489
3355
|
event: 'console.bulk_revoke_api_keys',
|
|
3490
|
-
consoleUserId:
|
|
3356
|
+
consoleUserId: c.var.consoleAuth.consoleUserId,
|
|
3491
3357
|
requestedCount: response.requestedCount,
|
|
3492
3358
|
revokedCount: response.revokedCount,
|
|
3493
3359
|
alreadyRevokedCount: response.alreadyRevokedCount,
|
|
@@ -3532,9 +3398,6 @@ export function createConsoleRoutes<
|
|
|
3532
3398
|
}),
|
|
3533
3399
|
zValidator('param', apiKeyIdParamSchema),
|
|
3534
3400
|
async (c) => {
|
|
3535
|
-
const auth = await requireAuth(c);
|
|
3536
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
3537
|
-
|
|
3538
3401
|
const { id: keyId } = c.req.valid('param');
|
|
3539
3402
|
const now = new Date().toISOString();
|
|
3540
3403
|
|
|
@@ -3582,7 +3445,7 @@ export function createConsoleRoutes<
|
|
|
3582
3445
|
|
|
3583
3446
|
logSyncEvent({
|
|
3584
3447
|
event: 'console.stage_rotate_api_key',
|
|
3585
|
-
consoleUserId:
|
|
3448
|
+
consoleUserId: c.var.consoleAuth.consoleUserId,
|
|
3586
3449
|
oldKeyId: keyId,
|
|
3587
3450
|
newKeyId,
|
|
3588
3451
|
});
|
|
@@ -3643,9 +3506,6 @@ export function createConsoleRoutes<
|
|
|
3643
3506
|
}),
|
|
3644
3507
|
zValidator('param', apiKeyIdParamSchema),
|
|
3645
3508
|
async (c) => {
|
|
3646
|
-
const auth = await requireAuth(c);
|
|
3647
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
3648
|
-
|
|
3649
3509
|
const { id: keyId } = c.req.valid('param');
|
|
3650
3510
|
const now = new Date().toISOString();
|
|
3651
3511
|
|
|
@@ -3703,7 +3563,7 @@ export function createConsoleRoutes<
|
|
|
3703
3563
|
|
|
3704
3564
|
logSyncEvent({
|
|
3705
3565
|
event: 'console.rotate_api_key',
|
|
3706
|
-
consoleUserId:
|
|
3566
|
+
consoleUserId: c.var.consoleAuth.consoleUserId,
|
|
3707
3567
|
oldKeyId: keyId,
|
|
3708
3568
|
newKeyId,
|
|
3709
3569
|
});
|
|
@@ -3759,9 +3619,6 @@ export function createConsoleRoutes<
|
|
|
3759
3619
|
}),
|
|
3760
3620
|
zValidator('query', ConsoleBlobListQuerySchema),
|
|
3761
3621
|
async (c) => {
|
|
3762
|
-
const auth = await requireAuth(c);
|
|
3763
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
3764
|
-
|
|
3765
3622
|
if (!bucket) {
|
|
3766
3623
|
return c.json({ error: 'BLOB_STORAGE_NOT_CONFIGURED' }, 501);
|
|
3767
3624
|
}
|
|
@@ -3813,9 +3670,6 @@ export function createConsoleRoutes<
|
|
|
3813
3670
|
},
|
|
3814
3671
|
}),
|
|
3815
3672
|
async (c) => {
|
|
3816
|
-
const auth = await requireAuth(c);
|
|
3817
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
3818
|
-
|
|
3819
3673
|
if (!bucket) {
|
|
3820
3674
|
return c.json({ error: 'BLOB_STORAGE_NOT_CONFIGURED' }, 501);
|
|
3821
3675
|
}
|
|
@@ -3868,9 +3722,6 @@ export function createConsoleRoutes<
|
|
|
3868
3722
|
},
|
|
3869
3723
|
}),
|
|
3870
3724
|
async (c) => {
|
|
3871
|
-
const auth = await requireAuth(c);
|
|
3872
|
-
if (!auth) return c.json({ error: 'UNAUTHENTICATED' }, 401);
|
|
3873
|
-
|
|
3874
3725
|
if (!bucket) {
|
|
3875
3726
|
return c.json({ error: 'BLOB_STORAGE_NOT_CONFIGURED' }, 501);
|
|
3876
3727
|
}
|
|
@@ -3923,12 +3774,9 @@ export function createTokenAuthenticator(
|
|
|
3923
3774
|
return async (c: Context) => {
|
|
3924
3775
|
if (!expectedToken) return null;
|
|
3925
3776
|
|
|
3926
|
-
const
|
|
3927
|
-
if (
|
|
3928
|
-
|
|
3929
|
-
if (bearerToken === expectedToken) {
|
|
3930
|
-
return { consoleUserId: 'token' };
|
|
3931
|
-
}
|
|
3777
|
+
const bearerToken = parseBearerToken(c.req.header('Authorization'));
|
|
3778
|
+
if (bearerToken === expectedToken) {
|
|
3779
|
+
return { consoleUserId: 'token' };
|
|
3932
3780
|
}
|
|
3933
3781
|
|
|
3934
3782
|
return null;
|