@syncular/server-hono 0.0.4-25 → 0.0.6-100
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/README.md +6 -1
- package/dist/console/gateway.d.ts +3 -1
- package/dist/console/gateway.d.ts.map +1 -1
- package/dist/console/gateway.js +227 -42
- package/dist/console/gateway.js.map +1 -1
- package/dist/console/index.d.ts +2 -0
- package/dist/console/index.d.ts.map +1 -1
- package/dist/console/index.js +2 -0
- package/dist/console/index.js.map +1 -1
- package/dist/console/routes.d.ts +3 -97
- package/dist/console/routes.d.ts.map +1 -1
- package/dist/console/routes.js +516 -81
- package/dist/console/routes.js.map +1 -1
- package/dist/console/schemas.d.ts +29 -0
- package/dist/console/schemas.d.ts.map +1 -1
- package/dist/console/schemas.js +22 -0
- package/dist/console/schemas.js.map +1 -1
- package/dist/console/types.d.ts +175 -0
- package/dist/console/types.d.ts.map +1 -0
- package/dist/console/types.js +2 -0
- package/dist/console/types.js.map +1 -0
- package/dist/console/ui.d.ts +38 -0
- package/dist/console/ui.d.ts.map +1 -0
- package/dist/console/ui.js +43 -0
- package/dist/console/ui.js.map +1 -0
- package/dist/create-server.d.ts +17 -34
- package/dist/create-server.d.ts.map +1 -1
- package/dist/create-server.js +26 -26
- package/dist/create-server.js.map +1 -1
- package/dist/proxy/connection-manager.d.ts +3 -3
- package/dist/proxy/connection-manager.d.ts.map +1 -1
- package/dist/proxy/routes.d.ts +4 -4
- package/dist/proxy/routes.d.ts.map +1 -1
- package/dist/proxy/routes.js +1 -1
- package/dist/routes.d.ts +33 -9
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +153 -70
- package/dist/routes.js.map +1 -1
- package/package.json +21 -6
- package/src/__tests__/blob-routes.test.ts +424 -0
- package/src/__tests__/console-gateway-live-routes.test.ts +54 -3
- package/src/__tests__/console-routes.test.ts +161 -7
- package/src/__tests__/console-ui.test.ts +114 -0
- package/src/__tests__/create-server.test.ts +233 -10
- package/src/__tests__/pull-chunk-storage.test.ts +6 -2
- package/src/__tests__/realtime-bridge.test.ts +6 -2
- package/src/__tests__/sync-rate-limit-routing.test.ts +6 -2
- package/src/console/gateway.ts +286 -54
- package/src/console/index.ts +2 -0
- package/src/console/routes.ts +663 -199
- package/src/console/schemas.ts +29 -0
- package/src/console/types.ts +185 -0
- package/src/console/ui.ts +100 -0
- package/src/create-server.ts +56 -53
- package/src/proxy/connection-manager.ts +3 -3
- package/src/proxy/routes.ts +4 -4
- package/src/routes.ts +225 -96
package/README.md
CHANGED
|
@@ -8,6 +8,12 @@ Hono adapter for `@syncular/server`. Provides push/pull routes, WebSocket wake-u
|
|
|
8
8
|
npm install @syncular/server-hono hono
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
If you want to serve the Console UI with `mountConsoleUi`, also install:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @syncular/console
|
|
15
|
+
```
|
|
16
|
+
|
|
11
17
|
## Documentation
|
|
12
18
|
|
|
13
19
|
- Hono adapter: https://syncular.dev/docs/server/hono-adapter
|
|
@@ -20,4 +26,3 @@ npm install @syncular/server-hono hono
|
|
|
20
26
|
- Issues: https://github.com/syncular/syncular/issues
|
|
21
27
|
|
|
22
28
|
> Status: Alpha. APIs and storage layouts may change between releases.
|
|
23
|
-
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Context } from 'hono';
|
|
2
2
|
import { Hono } from 'hono';
|
|
3
3
|
import type { UpgradeWebSocket } from 'hono/ws';
|
|
4
|
-
import type { ConsoleAuthResult } from './
|
|
4
|
+
import type { ConsoleAuthResult } from './types';
|
|
5
5
|
export interface ConsoleGatewayInstance {
|
|
6
6
|
instanceId: string;
|
|
7
7
|
label?: string;
|
|
@@ -10,9 +10,11 @@ export interface ConsoleGatewayInstance {
|
|
|
10
10
|
enabled?: boolean;
|
|
11
11
|
}
|
|
12
12
|
interface ConsoleGatewayDownstreamSocket {
|
|
13
|
+
onopen?: ((event: Event) => void) | null;
|
|
13
14
|
onmessage: ((event: MessageEvent) => void) | null;
|
|
14
15
|
onerror: ((event: Event) => void) | null;
|
|
15
16
|
close: () => void;
|
|
17
|
+
send?: (data: string) => void;
|
|
16
18
|
}
|
|
17
19
|
export interface CreateConsoleGatewayRoutesOptions {
|
|
18
20
|
instances: ConsoleGatewayInstance[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../../src/console/gateway.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../../src/console/gateway.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AA2DhD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,8BAA8B;IACtC,MAAM,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACzC,SAAS,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IAClD,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACzC,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,iCAAiC;IAChD,SAAS,EAAE,sBAAsB,EAAE,CAAC;IACpC,YAAY,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IAChE,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC;IAC7B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,SAAS,CAAC,EAAE;QACV,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;QACpC,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,8BAA8B,CAAC;KACnE,CAAC;CACH;AAo8BD,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,iCAAiC,GACzC,IAAI,CAi/EN"}
|
package/dist/console/gateway.js
CHANGED
|
@@ -471,28 +471,15 @@ function resolveForwardAuthorization(args) {
|
|
|
471
471
|
if (header) {
|
|
472
472
|
return header;
|
|
473
473
|
}
|
|
474
|
-
const queryToken = args.c.req.query('token')?.trim();
|
|
475
|
-
if (queryToken) {
|
|
476
|
-
return `Bearer ${queryToken}`;
|
|
477
|
-
}
|
|
478
474
|
return null;
|
|
479
475
|
}
|
|
480
|
-
function
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
const authHeader = args.c.req.header('Authorization')?.trim();
|
|
485
|
-
if (authHeader?.startsWith('Bearer ')) {
|
|
486
|
-
const token = authHeader.slice(7).trim();
|
|
487
|
-
if (token.length > 0) {
|
|
488
|
-
return token;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
const queryToken = args.c.req.query('token')?.trim();
|
|
492
|
-
if (queryToken) {
|
|
493
|
-
return queryToken;
|
|
476
|
+
function parseBearerToken(authHeader) {
|
|
477
|
+
const value = authHeader?.trim();
|
|
478
|
+
if (!value?.startsWith('Bearer ')) {
|
|
479
|
+
return null;
|
|
494
480
|
}
|
|
495
|
-
|
|
481
|
+
const token = value.slice(7).trim();
|
|
482
|
+
return token.length > 0 ? token : null;
|
|
496
483
|
}
|
|
497
484
|
async function fetchDownstreamJson(args) {
|
|
498
485
|
const url = buildConsoleEndpointUrl({
|
|
@@ -743,7 +730,15 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
743
730
|
routes.use('*', cors({
|
|
744
731
|
origin: corsOrigins === '*' ? '*' : corsOrigins,
|
|
745
732
|
allowMethods: ['GET', 'POST', 'DELETE', 'OPTIONS'],
|
|
746
|
-
allowHeaders: [
|
|
733
|
+
allowHeaders: [
|
|
734
|
+
'Content-Type',
|
|
735
|
+
'Authorization',
|
|
736
|
+
'X-Syncular-Transport-Path',
|
|
737
|
+
'Baggage',
|
|
738
|
+
'Sentry-Trace',
|
|
739
|
+
'Traceparent',
|
|
740
|
+
'Tracestate',
|
|
741
|
+
],
|
|
747
742
|
credentials: true,
|
|
748
743
|
}));
|
|
749
744
|
routes.get('/instances', describeRoute({
|
|
@@ -2087,7 +2082,7 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
2087
2082
|
((url) => new WebSocket(url));
|
|
2088
2083
|
const liveState = new WeakMap();
|
|
2089
2084
|
routes.get('/events/live', upgradeWebSocket(async (c) => {
|
|
2090
|
-
const
|
|
2085
|
+
const initialAuth = await options.authenticate(c);
|
|
2091
2086
|
const partitionId = c.req.query('partitionId')?.trim() || undefined;
|
|
2092
2087
|
const replaySince = c.req.query('since')?.trim() || undefined;
|
|
2093
2088
|
const replayLimitRaw = c.req.query('replayLimit');
|
|
@@ -2104,11 +2099,38 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
2104
2099
|
instanceIds: c.req.query('instanceIds') ?? undefined,
|
|
2105
2100
|
},
|
|
2106
2101
|
});
|
|
2102
|
+
const authenticateWithBearer = async (token) => {
|
|
2103
|
+
const trimmedToken = token.trim();
|
|
2104
|
+
if (!trimmedToken) {
|
|
2105
|
+
return null;
|
|
2106
|
+
}
|
|
2107
|
+
const authContext = {
|
|
2108
|
+
req: {
|
|
2109
|
+
header: (name) => name === 'Authorization' ? `Bearer ${trimmedToken}` : undefined,
|
|
2110
|
+
query: () => undefined,
|
|
2111
|
+
},
|
|
2112
|
+
};
|
|
2113
|
+
return options.authenticate(authContext);
|
|
2114
|
+
};
|
|
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
|
+
};
|
|
2107
2124
|
const cleanup = (ws) => {
|
|
2108
2125
|
const state = liveState.get(ws);
|
|
2109
2126
|
if (!state)
|
|
2110
2127
|
return;
|
|
2111
|
-
|
|
2128
|
+
if (state.heartbeatInterval) {
|
|
2129
|
+
clearInterval(state.heartbeatInterval);
|
|
2130
|
+
}
|
|
2131
|
+
if (state.authTimeout) {
|
|
2132
|
+
clearTimeout(state.authTimeout);
|
|
2133
|
+
}
|
|
2112
2134
|
for (const downstream of state.downstreamSockets) {
|
|
2113
2135
|
try {
|
|
2114
2136
|
downstream.close();
|
|
@@ -2121,11 +2143,6 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
2121
2143
|
};
|
|
2122
2144
|
return {
|
|
2123
2145
|
onOpen(_event, ws) {
|
|
2124
|
-
if (!auth) {
|
|
2125
|
-
ws.send(JSON.stringify({ type: 'error', message: 'UNAUTHENTICATED' }));
|
|
2126
|
-
ws.close(4001, 'Unauthenticated');
|
|
2127
|
-
return;
|
|
2128
|
-
}
|
|
2129
2146
|
if (selectedInstances.length === 0) {
|
|
2130
2147
|
ws.send(JSON.stringify({
|
|
2131
2148
|
type: 'error',
|
|
@@ -2134,16 +2151,171 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
2134
2151
|
ws.close(4004, 'No instances selected');
|
|
2135
2152
|
return;
|
|
2136
2153
|
}
|
|
2137
|
-
const
|
|
2154
|
+
const state = {
|
|
2155
|
+
downstreamSockets: [],
|
|
2156
|
+
heartbeatInterval: null,
|
|
2157
|
+
authTimeout: null,
|
|
2158
|
+
isAuthenticated: false,
|
|
2159
|
+
};
|
|
2160
|
+
liveState.set(ws, state);
|
|
2161
|
+
const startAuthenticatedSession = (upstreamBearerToken) => {
|
|
2162
|
+
if (state.isAuthenticated) {
|
|
2163
|
+
return;
|
|
2164
|
+
}
|
|
2165
|
+
state.isAuthenticated = true;
|
|
2166
|
+
if (state.authTimeout) {
|
|
2167
|
+
clearTimeout(state.authTimeout);
|
|
2168
|
+
state.authTimeout = null;
|
|
2169
|
+
}
|
|
2170
|
+
for (const instance of selectedInstances) {
|
|
2171
|
+
const downstreamQuery = new URLSearchParams();
|
|
2172
|
+
if (partitionId) {
|
|
2173
|
+
downstreamQuery.set('partitionId', partitionId);
|
|
2174
|
+
}
|
|
2175
|
+
if (replaySince) {
|
|
2176
|
+
downstreamQuery.set('since', replaySince);
|
|
2177
|
+
}
|
|
2178
|
+
downstreamQuery.set('replayLimit', String(replayLimit));
|
|
2179
|
+
const downstreamUrl = buildConsoleEndpointUrl({
|
|
2180
|
+
instance,
|
|
2181
|
+
requestUrl: c.req.url,
|
|
2182
|
+
path: '/events/live',
|
|
2183
|
+
query: downstreamQuery,
|
|
2184
|
+
});
|
|
2185
|
+
const downstreamSocket = createDownstreamSocket(downstreamUrl);
|
|
2186
|
+
const downstreamToken = instance.token?.trim() ?? upstreamBearerToken?.trim() ?? null;
|
|
2187
|
+
if (downstreamToken && downstreamSocket.send) {
|
|
2188
|
+
downstreamSocket.onopen = () => {
|
|
2189
|
+
try {
|
|
2190
|
+
downstreamSocket.send?.(JSON.stringify({
|
|
2191
|
+
type: 'auth',
|
|
2192
|
+
token: downstreamToken,
|
|
2193
|
+
}));
|
|
2194
|
+
}
|
|
2195
|
+
catch {
|
|
2196
|
+
// no-op
|
|
2197
|
+
}
|
|
2198
|
+
};
|
|
2199
|
+
}
|
|
2200
|
+
downstreamSocket.onmessage = (message) => {
|
|
2201
|
+
if (typeof message.data !== 'string') {
|
|
2202
|
+
return;
|
|
2203
|
+
}
|
|
2204
|
+
try {
|
|
2205
|
+
const payload = JSON.parse(message.data);
|
|
2206
|
+
if (typeof payload.type === 'string' &&
|
|
2207
|
+
(payload.type === 'connected' ||
|
|
2208
|
+
payload.type === 'heartbeat')) {
|
|
2209
|
+
return;
|
|
2210
|
+
}
|
|
2211
|
+
const payloadData = payload.data &&
|
|
2212
|
+
typeof payload.data === 'object' &&
|
|
2213
|
+
!Array.isArray(payload.data)
|
|
2214
|
+
? { ...payload.data, instanceId: instance.instanceId }
|
|
2215
|
+
: { instanceId: instance.instanceId };
|
|
2216
|
+
const event = {
|
|
2217
|
+
...payload,
|
|
2218
|
+
data: payloadData,
|
|
2219
|
+
instanceId: instance.instanceId,
|
|
2220
|
+
timestamp: typeof payload.timestamp === 'string'
|
|
2221
|
+
? payload.timestamp
|
|
2222
|
+
: new Date().toISOString(),
|
|
2223
|
+
};
|
|
2224
|
+
ws.send(JSON.stringify(event));
|
|
2225
|
+
}
|
|
2226
|
+
catch {
|
|
2227
|
+
// Ignore malformed downstream events
|
|
2228
|
+
}
|
|
2229
|
+
};
|
|
2230
|
+
downstreamSocket.onerror = () => {
|
|
2231
|
+
try {
|
|
2232
|
+
ws.send(JSON.stringify({
|
|
2233
|
+
type: 'instance_error',
|
|
2234
|
+
instanceId: instance.instanceId,
|
|
2235
|
+
timestamp: new Date().toISOString(),
|
|
2236
|
+
}));
|
|
2237
|
+
}
|
|
2238
|
+
catch {
|
|
2239
|
+
// ignore send errors
|
|
2240
|
+
}
|
|
2241
|
+
};
|
|
2242
|
+
state.downstreamSockets.push(downstreamSocket);
|
|
2243
|
+
}
|
|
2244
|
+
ws.send(JSON.stringify({
|
|
2245
|
+
type: 'connected',
|
|
2246
|
+
timestamp: new Date().toISOString(),
|
|
2247
|
+
instanceCount: selectedInstances.length,
|
|
2248
|
+
}));
|
|
2249
|
+
const heartbeatInterval = setInterval(() => {
|
|
2250
|
+
try {
|
|
2251
|
+
ws.send(JSON.stringify({
|
|
2252
|
+
type: 'heartbeat',
|
|
2253
|
+
timestamp: new Date().toISOString(),
|
|
2254
|
+
}));
|
|
2255
|
+
}
|
|
2256
|
+
catch {
|
|
2257
|
+
clearInterval(heartbeatInterval);
|
|
2258
|
+
}
|
|
2259
|
+
}, heartbeatIntervalMs);
|
|
2260
|
+
state.heartbeatInterval = heartbeatInterval;
|
|
2261
|
+
};
|
|
2262
|
+
if (initialAuth) {
|
|
2263
|
+
startAuthenticatedSession(parseBearerToken(c.req.header('Authorization')));
|
|
2264
|
+
return;
|
|
2265
|
+
}
|
|
2266
|
+
state.authTimeout = setTimeout(() => {
|
|
2267
|
+
const current = liveState.get(ws);
|
|
2268
|
+
if (!current || current.isAuthenticated) {
|
|
2269
|
+
return;
|
|
2270
|
+
}
|
|
2271
|
+
closeUnauthenticated(ws);
|
|
2272
|
+
cleanup(ws);
|
|
2273
|
+
}, 5_000);
|
|
2274
|
+
},
|
|
2275
|
+
async onMessage(event, ws) {
|
|
2276
|
+
const state = liveState.get(ws);
|
|
2277
|
+
if (!state || state.isAuthenticated) {
|
|
2278
|
+
return;
|
|
2279
|
+
}
|
|
2280
|
+
if (typeof event.data !== 'string') {
|
|
2281
|
+
closeUnauthenticated(ws);
|
|
2282
|
+
cleanup(ws);
|
|
2283
|
+
return;
|
|
2284
|
+
}
|
|
2285
|
+
let token = '';
|
|
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
|
+
}
|
|
2297
|
+
if (!token) {
|
|
2298
|
+
closeUnauthenticated(ws);
|
|
2299
|
+
cleanup(ws);
|
|
2300
|
+
return;
|
|
2301
|
+
}
|
|
2302
|
+
const auth = await authenticateWithBearer(token);
|
|
2303
|
+
const current = liveState.get(ws);
|
|
2304
|
+
if (!current || current.isAuthenticated) {
|
|
2305
|
+
return;
|
|
2306
|
+
}
|
|
2307
|
+
if (!auth) {
|
|
2308
|
+
closeUnauthenticated(ws);
|
|
2309
|
+
cleanup(ws);
|
|
2310
|
+
return;
|
|
2311
|
+
}
|
|
2312
|
+
current.isAuthenticated = true;
|
|
2313
|
+
if (current.authTimeout) {
|
|
2314
|
+
clearTimeout(current.authTimeout);
|
|
2315
|
+
current.authTimeout = null;
|
|
2316
|
+
}
|
|
2138
2317
|
for (const instance of selectedInstances) {
|
|
2139
2318
|
const downstreamQuery = new URLSearchParams();
|
|
2140
|
-
const downstreamToken = resolveForwardBearerToken({
|
|
2141
|
-
c,
|
|
2142
|
-
instance,
|
|
2143
|
-
});
|
|
2144
|
-
if (downstreamToken) {
|
|
2145
|
-
downstreamQuery.set('token', downstreamToken);
|
|
2146
|
-
}
|
|
2147
2319
|
if (partitionId) {
|
|
2148
2320
|
downstreamQuery.set('partitionId', partitionId);
|
|
2149
2321
|
}
|
|
@@ -2158,6 +2330,22 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
2158
2330
|
query: downstreamQuery,
|
|
2159
2331
|
});
|
|
2160
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
|
+
}
|
|
2161
2349
|
downstreamSocket.onmessage = (message) => {
|
|
2162
2350
|
if (typeof message.data !== 'string') {
|
|
2163
2351
|
return;
|
|
@@ -2174,7 +2362,7 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
2174
2362
|
!Array.isArray(payload.data)
|
|
2175
2363
|
? { ...payload.data, instanceId: instance.instanceId }
|
|
2176
2364
|
: { instanceId: instance.instanceId };
|
|
2177
|
-
const
|
|
2365
|
+
const liveEvent = {
|
|
2178
2366
|
...payload,
|
|
2179
2367
|
data: payloadData,
|
|
2180
2368
|
instanceId: instance.instanceId,
|
|
@@ -2182,7 +2370,7 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
2182
2370
|
? payload.timestamp
|
|
2183
2371
|
: new Date().toISOString(),
|
|
2184
2372
|
};
|
|
2185
|
-
ws.send(JSON.stringify(
|
|
2373
|
+
ws.send(JSON.stringify(liveEvent));
|
|
2186
2374
|
}
|
|
2187
2375
|
catch {
|
|
2188
2376
|
// Ignore malformed downstream events
|
|
@@ -2200,7 +2388,7 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
2200
2388
|
// ignore send errors
|
|
2201
2389
|
}
|
|
2202
2390
|
};
|
|
2203
|
-
downstreamSockets.push(downstreamSocket);
|
|
2391
|
+
current.downstreamSockets.push(downstreamSocket);
|
|
2204
2392
|
}
|
|
2205
2393
|
ws.send(JSON.stringify({
|
|
2206
2394
|
type: 'connected',
|
|
@@ -2218,10 +2406,7 @@ export function createConsoleGatewayRoutes(options) {
|
|
|
2218
2406
|
clearInterval(heartbeatInterval);
|
|
2219
2407
|
}
|
|
2220
2408
|
}, heartbeatIntervalMs);
|
|
2221
|
-
|
|
2222
|
-
downstreamSockets,
|
|
2223
|
-
heartbeatInterval,
|
|
2224
|
-
});
|
|
2409
|
+
current.heartbeatInterval = heartbeatInterval;
|
|
2225
2410
|
},
|
|
2226
2411
|
onClose(_event, ws) {
|
|
2227
2412
|
cleanup(ws);
|