@camstack/server 1.2.87 → 1.2.89
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/main.js +149 -197
- package/package.json +5 -5
package/dist/main.js
CHANGED
|
@@ -39,7 +39,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
39
39
|
/* eslint-disable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-argument -- pre-existing lint debt across this 800+ line bootstrap module. The flagged sites cross typed boundaries (Fastify request typing, AddonRouteRegistry, AuthService inherited methods) where the projectService context can't trace inheritance chains. Tracked separately; do not amend in unrelated edits. */
|
|
40
40
|
const fastify_1 = require("@trpc/server/adapters/fastify");
|
|
41
41
|
const ws_1 = require("@trpc/server/adapters/ws");
|
|
42
|
-
const static_1 = __importDefault(require("@fastify/static"));
|
|
43
42
|
const compress_1 = __importDefault(require("@fastify/compress"));
|
|
44
43
|
const cookie_1 = __importDefault(require("@fastify/cookie"));
|
|
45
44
|
const sse_no_compression_js_1 = require("./api/sse-no-compression.js");
|
|
@@ -1074,216 +1073,166 @@ async function bootstrap() {
|
|
|
1074
1073
|
await addonRegistry.setAppRouter(appRouter);
|
|
1075
1074
|
console.log('[bootstrap] AddonRegistry wired with tRPC direct caller');
|
|
1076
1075
|
}
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
//
|
|
1081
|
-
//
|
|
1082
|
-
//
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
// are spawning in parallel. The previous 10s window was racing
|
|
1108
|
-
// the admin-ui runner's boot — fastify-static didn't register and
|
|
1109
|
-
// every `GET /` returned 404. Bump to 60s; we only pay this
|
|
1110
|
-
// wait once at boot.
|
|
1111
|
-
const ADMIN_UI_WAIT_MS = 60_000;
|
|
1112
|
-
const POLL_MS = 200;
|
|
1113
|
-
const deadline = Date.now() + ADMIN_UI_WAIT_MS;
|
|
1114
|
-
while (!adminUI && Date.now() < deadline) {
|
|
1115
|
-
await new Promise((r) => setTimeout(r, POLL_MS));
|
|
1116
|
-
adminUI = capRegistry.getSingletonForNode('admin-ui', 'hub');
|
|
1076
|
+
const adminUiState = { staticDir: null, indexPath: null };
|
|
1077
|
+
const viewerUiState = { staticDir: null, indexPath: null };
|
|
1078
|
+
const VIEWER_MOUNT = '/viewer/camstack';
|
|
1079
|
+
// Register UI routes before Fastify starts, but resolve their forked
|
|
1080
|
+
// providers after listen. During a cold boot the control plane is therefore
|
|
1081
|
+
// immediately reachable while the non-critical UI runners finish starting.
|
|
1082
|
+
fastify.get('/viewer', async (_request, reply) => reply.redirect(`${VIEWER_MOUNT}/`));
|
|
1083
|
+
fastify.get('/viewer/', async (_request, reply) => reply.redirect(`${VIEWER_MOUNT}/`));
|
|
1084
|
+
fastify.get('/webrtc-test.html', async (_request, reply) => {
|
|
1085
|
+
const webrtcTestPath = path.join(dataPath, 'webrtc-test.html');
|
|
1086
|
+
if (!fs.existsSync(webrtcTestPath))
|
|
1087
|
+
return reply.callNotFound();
|
|
1088
|
+
return reply.type('text/html').send(fs.createReadStream(webrtcTestPath));
|
|
1089
|
+
});
|
|
1090
|
+
fastify.get('/viewer/*', async (request, reply) => {
|
|
1091
|
+
const { staticDir, indexPath } = viewerUiState;
|
|
1092
|
+
if (!staticDir || !indexPath) {
|
|
1093
|
+
return reply.status(503).send({ error: 'Viewer UI is starting' });
|
|
1094
|
+
}
|
|
1095
|
+
const pathOnly = request.url.split('?')[0] ?? request.url;
|
|
1096
|
+
let rel = '';
|
|
1097
|
+
if (pathOnly.startsWith(`${VIEWER_MOUNT}/`)) {
|
|
1098
|
+
rel = pathOnly.slice(VIEWER_MOUNT.length + 1);
|
|
1099
|
+
}
|
|
1100
|
+
if (rel && /\.[a-zA-Z0-9]+$/.test(pathOnly)) {
|
|
1101
|
+
const abs = path.join(staticDir, rel);
|
|
1102
|
+
if ((abs === staticDir || abs.startsWith(staticDir + path.sep)) && fs.existsSync(abs)) {
|
|
1103
|
+
reply.header('cache-control', (0, spa_static_1.spaAssetCacheControl)(rel));
|
|
1104
|
+
reply.type((0, spa_static_1.contentTypeForPath)(rel));
|
|
1105
|
+
return sendMaybePrecompressed(reply, abs, request.headers['accept-encoding']);
|
|
1117
1106
|
}
|
|
1107
|
+
return reply.callNotFound();
|
|
1118
1108
|
}
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1109
|
+
reply.header('cache-control', 'no-cache, must-revalidate');
|
|
1110
|
+
return reply.type('text/html').send(fs.createReadStream(indexPath));
|
|
1111
|
+
});
|
|
1112
|
+
fastify.get('/*', async (request, reply) => {
|
|
1113
|
+
const url = request.url;
|
|
1114
|
+
if (url.startsWith('/trpc') ||
|
|
1115
|
+
url.startsWith('/api/') ||
|
|
1116
|
+
url.startsWith('/agent') ||
|
|
1117
|
+
url.startsWith('/health') ||
|
|
1118
|
+
url.startsWith('/viewer')) {
|
|
1119
|
+
return reply.callNotFound();
|
|
1120
|
+
}
|
|
1121
|
+
const { staticDir, indexPath } = adminUiState;
|
|
1122
|
+
if (!staticDir || !indexPath) {
|
|
1123
|
+
return reply.status(503).send({ error: 'Admin UI is starting' });
|
|
1124
|
+
}
|
|
1125
|
+
const pathOnly = url.split('?')[0] ?? url;
|
|
1126
|
+
if (/\.[a-zA-Z0-9]+$/.test(pathOnly)) {
|
|
1127
|
+
const rel = pathOnly.replace(/^\/+/, '');
|
|
1128
|
+
const abs = path.join(staticDir, rel);
|
|
1129
|
+
if ((abs === staticDir || abs.startsWith(staticDir + path.sep)) && fs.existsSync(abs)) {
|
|
1130
|
+
reply.header('cache-control', (0, spa_static_1.spaAssetCacheControl)(rel));
|
|
1131
|
+
return sendMaybePrecompressed(reply, abs, request.headers['accept-encoding']);
|
|
1132
|
+
}
|
|
1133
|
+
return reply.callNotFound();
|
|
1134
|
+
}
|
|
1135
|
+
reply.header('cache-control', 'no-cache, must-revalidate');
|
|
1136
|
+
return reply.type('text/html').send(fs.createReadStream(indexPath));
|
|
1137
|
+
});
|
|
1138
|
+
const resolveAdminUi = async () => {
|
|
1139
|
+
try {
|
|
1140
|
+
const bootAddonRegistry = app.get(addon_registry_service_1.AddonRegistryService);
|
|
1141
|
+
const capRegistry = bootAddonRegistry.getCapabilityRegistry();
|
|
1142
|
+
// HUB-NODE-SCOPED resolution — never the cluster-global singleton.
|
|
1143
|
+
// Agent nodes register the same `admin-ui` cap through their own
|
|
1144
|
+
// agent-ui addon (placement: agent-only), and the cluster-elected
|
|
1145
|
+
// active provider can land on the AGENT's registration depending on
|
|
1146
|
+
// node boot order. Its `getStaticDir()` then answers with a path on
|
|
1147
|
+
// the agent's filesystem — nonexistent here — and the SPA catch-all
|
|
1148
|
+
// silently never registers (every `GET /` 404s until the next
|
|
1149
|
+
// restart re-rolls the election). `getSingletonForNode(cap, 'hub')`
|
|
1150
|
+
// only ever resolves providers hosted on THIS node.
|
|
1151
|
+
let adminUI = capRegistry?.getSingletonForNode('admin-ui', 'hub');
|
|
1152
|
+
// CAMSTACK_SKIP_ADMIN_UI_WAIT bypasses the background capability poll in
|
|
1153
|
+
// e2e runs that do not need the SPA.
|
|
1154
|
+
const skipAdminUIWait = process.env['CAMSTACK_SKIP_ADMIN_UI_WAIT'] === '1';
|
|
1155
|
+
if (!adminUI && capRegistry && !skipAdminUIWait) {
|
|
1156
|
+
// Forked-addon spawn + Moleculer registration + tRPC hydration
|
|
1157
|
+
// can take ~15-20s on cold boot, especially when several runners
|
|
1158
|
+
// are spawning in parallel. The previous 10s window was racing
|
|
1159
|
+
// the admin-ui runner's boot — fastify-static didn't register and
|
|
1160
|
+
// every `GET /` returned 404. Bump to 60s; we only pay this
|
|
1161
|
+
// wait once at boot.
|
|
1162
|
+
const ADMIN_UI_WAIT_MS = 60_000;
|
|
1163
|
+
const POLL_MS = 200;
|
|
1164
|
+
const deadline = Date.now() + ADMIN_UI_WAIT_MS;
|
|
1165
|
+
while (!adminUI && Date.now() < deadline) {
|
|
1166
|
+
await new Promise((r) => setTimeout(r, POLL_MS));
|
|
1167
|
+
adminUI = capRegistry.getSingletonForNode('admin-ui', 'hub');
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
if (adminUI) {
|
|
1171
|
+
const { staticDir } = await adminUI.getStaticDir();
|
|
1172
|
+
const indexPath = path.join(staticDir, 'index.html');
|
|
1173
|
+
if (fs.existsSync(staticDir) && fs.existsSync(indexPath)) {
|
|
1174
|
+
spaIndexHtml = indexPath;
|
|
1175
|
+
adminUiState.staticDir = staticDir;
|
|
1176
|
+
adminUiState.indexPath = indexPath;
|
|
1177
|
+
(0, precompressed_asset_js_1.warmPrecompressedAssets)(staticDir, (msg, meta) => console.log(`[static] ${msg} ${JSON.stringify(meta)}`));
|
|
1178
|
+
const { version } = await adminUI.getVersion();
|
|
1179
|
+
console.log(`[bootstrap] Admin UI served from: ${staticDir} (v${version})`);
|
|
1180
|
+
}
|
|
1181
|
+
else {
|
|
1182
|
+
console.warn(`[bootstrap] Admin UI dist not found at: ${staticDir} — run 'npm run build' in addon-admin-ui`);
|
|
1145
1183
|
}
|
|
1146
|
-
// SPA fallback + live static serving: this single catch-all owns every
|
|
1147
|
-
// GET. Core API prefixes fall through to their own routers via
|
|
1148
|
-
// `callNotFound`. Uses a wildcard route instead of setNotFoundHandler.
|
|
1149
|
-
fastify.get('/*', async (request, reply) => {
|
|
1150
|
-
const url = request.url;
|
|
1151
|
-
if (url.startsWith('/trpc') ||
|
|
1152
|
-
url.startsWith('/api/') ||
|
|
1153
|
-
url.startsWith('/agent') ||
|
|
1154
|
-
url.startsWith('/health') ||
|
|
1155
|
-
// The viewer SPA owns /viewer/** via its own catch-all (registered
|
|
1156
|
-
// below). Fall through here so admin-ui never serves it — and so
|
|
1157
|
-
// /viewer 404s cleanly when the viewer-ui addon isn't registered.
|
|
1158
|
-
url.startsWith('/viewer')) {
|
|
1159
|
-
return reply.callNotFound();
|
|
1160
|
-
}
|
|
1161
|
-
// A request whose last path segment has a file extension is a static
|
|
1162
|
-
// asset: serve it LIVE from the current dist so a redeployed
|
|
1163
|
-
// admin-ui's new content-hashed files are picked up without a hub
|
|
1164
|
-
// restart. When the file is missing, 404 — never the SPA
|
|
1165
|
-
// `index.html`: serving HTML under a `.js`/`.css` URL makes upstream
|
|
1166
|
-
// caches (Cloudflare, the browser) pin `text/html`, which then fails
|
|
1167
|
-
// the module MIME check long after the file is actually available.
|
|
1168
|
-
const pathOnly = url.split('?')[0] ?? url;
|
|
1169
|
-
if (/\.[a-zA-Z0-9]+$/.test(pathOnly)) {
|
|
1170
|
-
const rel = pathOnly.replace(/^\/+/, '');
|
|
1171
|
-
const abs = path.join(staticDir, rel);
|
|
1172
|
-
if ((abs === staticDir || abs.startsWith(staticDir + path.sep)) && fs.existsSync(abs)) {
|
|
1173
|
-
// Cache policy that lets PWA updates actually propagate (the
|
|
1174
|
-
// stale-bundle bug): the service worker + registration + manifest
|
|
1175
|
-
// MUST be revalidated every load or a redeploy never reaches the
|
|
1176
|
-
// client (the SW keeps serving the old precache). Content-hashed
|
|
1177
|
-
// build assets (assets/index-<hash>.js) are immutable. Everything
|
|
1178
|
-
// else gets a short cache.
|
|
1179
|
-
reply.header('cache-control', (0, spa_static_1.spaAssetCacheControl)(rel));
|
|
1180
|
-
return sendMaybePrecompressed(reply, abs, request.headers['accept-encoding']);
|
|
1181
|
-
}
|
|
1182
|
-
return reply.callNotFound();
|
|
1183
|
-
}
|
|
1184
|
-
// index.html (the SPA shell) must never be cached — it references the
|
|
1185
|
-
// content-hashed bundles, so a stale copy pins the old app forever.
|
|
1186
|
-
reply.header('cache-control', 'no-cache, must-revalidate');
|
|
1187
|
-
return reply.type('text/html').send(fs.createReadStream(spaIndexHtml));
|
|
1188
|
-
});
|
|
1189
|
-
// Compress the dist in the background so the first CLIENT does not pay
|
|
1190
|
-
// for it — a 5 MB bundle takes ~6.7s at quality 11, and that is a worse
|
|
1191
|
-
// first load than the per-request compression this replaced.
|
|
1192
|
-
(0, precompressed_asset_js_1.warmPrecompressedAssets)(staticDir, (msg, meta) => console.log(`[static] ${msg} ${JSON.stringify(meta)}`));
|
|
1193
|
-
const { version } = await adminUI.getVersion();
|
|
1194
|
-
console.log(`[bootstrap] Admin UI served from: ${staticDir} (v${version})`);
|
|
1195
1184
|
}
|
|
1196
1185
|
else {
|
|
1197
|
-
console.warn(
|
|
1186
|
+
console.warn('[bootstrap] admin-ui capability not registered — no static file serving');
|
|
1198
1187
|
}
|
|
1199
1188
|
}
|
|
1200
|
-
|
|
1201
|
-
console.
|
|
1189
|
+
catch (err) {
|
|
1190
|
+
console.error('[bootstrap] Failed to set up admin UI static serving:', err);
|
|
1202
1191
|
}
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
// viewer is non-critical and admin-ui already gated the slow cold boot).
|
|
1225
|
-
const VIEWER_UI_WAIT_MS = 30_000;
|
|
1226
|
-
const POLL_MS = 200;
|
|
1227
|
-
const deadline = Date.now() + VIEWER_UI_WAIT_MS;
|
|
1228
|
-
while (!viewerUI && Date.now() < deadline) {
|
|
1229
|
-
await new Promise((r) => setTimeout(r, POLL_MS));
|
|
1230
|
-
viewerUI = capRegistry.getSingletonForNode('viewer-ui', 'hub');
|
|
1192
|
+
};
|
|
1193
|
+
const resolveViewerUi = async () => {
|
|
1194
|
+
try {
|
|
1195
|
+
const bootAddonRegistry = app.get(addon_registry_service_1.AddonRegistryService);
|
|
1196
|
+
const capRegistry = bootAddonRegistry.getCapabilityRegistry();
|
|
1197
|
+
// Hub-node-scoped for the same reason as admin-ui above: a remote
|
|
1198
|
+
// node's registration must never win the election for the provider
|
|
1199
|
+
// whose staticDir this process reads from local disk.
|
|
1200
|
+
let viewerUI = capRegistry?.getSingletonForNode('viewer-ui', 'hub');
|
|
1201
|
+
const skipViewerUIWait = process.env['CAMSTACK_SKIP_VIEWER_UI_WAIT'] === '1';
|
|
1202
|
+
if (!viewerUI && capRegistry && !skipViewerUIWait) {
|
|
1203
|
+
// The viewer-ui addon runs in its own forked runner and registers a few
|
|
1204
|
+
// seconds after boot. Poll briefly (shorter than admin-ui's 60s — the
|
|
1205
|
+
// viewer is non-critical and admin-ui already gated the slow cold boot).
|
|
1206
|
+
const VIEWER_UI_WAIT_MS = 30_000;
|
|
1207
|
+
const POLL_MS = 200;
|
|
1208
|
+
const deadline = Date.now() + VIEWER_UI_WAIT_MS;
|
|
1209
|
+
while (!viewerUI && Date.now() < deadline) {
|
|
1210
|
+
await new Promise((r) => setTimeout(r, POLL_MS));
|
|
1211
|
+
viewerUI = capRegistry.getSingletonForNode('viewer-ui', 'hub');
|
|
1212
|
+
}
|
|
1231
1213
|
}
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
const pathOnly = request.url.split('?')[0] ?? request.url;
|
|
1246
|
-
// Map the request path to a dist-relative path by stripping the mount
|
|
1247
|
-
// prefix. Anything not under the mount is treated as the SPA shell.
|
|
1248
|
-
let rel = '';
|
|
1249
|
-
if (pathOnly === VIEWER_MOUNT || pathOnly === `${VIEWER_MOUNT}/`) {
|
|
1250
|
-
rel = '';
|
|
1251
|
-
}
|
|
1252
|
-
else if (pathOnly.startsWith(`${VIEWER_MOUNT}/`)) {
|
|
1253
|
-
rel = pathOnly.slice(VIEWER_MOUNT.length + 1);
|
|
1254
|
-
}
|
|
1255
|
-
// A path whose last segment has a file extension is a static asset:
|
|
1256
|
-
// serve it, or 404 — NEVER the SPA shell (serving HTML under a
|
|
1257
|
-
// .js/.css URL pins text/html in caches and breaks the module MIME
|
|
1258
|
-
// check long after the file is actually available).
|
|
1259
|
-
if (rel && /\.[a-zA-Z0-9]+$/.test(pathOnly)) {
|
|
1260
|
-
const abs = path.join(staticDir, rel);
|
|
1261
|
-
if ((abs === staticDir || abs.startsWith(staticDir + path.sep)) && fs.existsSync(abs)) {
|
|
1262
|
-
reply.header('cache-control', (0, spa_static_1.spaAssetCacheControl)(rel));
|
|
1263
|
-
reply.type((0, spa_static_1.contentTypeForPath)(rel));
|
|
1264
|
-
return sendMaybePrecompressed(reply, abs, request.headers['accept-encoding']);
|
|
1265
|
-
}
|
|
1266
|
-
return reply.callNotFound();
|
|
1267
|
-
}
|
|
1268
|
-
// SPA shell — never cached (it references content-hashed bundles).
|
|
1269
|
-
reply.header('cache-control', 'no-cache, must-revalidate');
|
|
1270
|
-
return reply.type('text/html').send(fs.createReadStream(indexPath));
|
|
1271
|
-
});
|
|
1272
|
-
(0, precompressed_asset_js_1.warmPrecompressedAssets)(staticDir, (msg, meta) => console.log(`[static] ${msg} ${JSON.stringify(meta)}`));
|
|
1273
|
-
const { version } = await viewerUI.getVersion();
|
|
1274
|
-
console.log(`[bootstrap] Viewer UI served from: ${staticDir} at ${VIEWER_MOUNT} (v${version})`);
|
|
1214
|
+
if (viewerUI) {
|
|
1215
|
+
const { staticDir } = await viewerUI.getStaticDir();
|
|
1216
|
+
const indexPath = path.join(staticDir, 'index.html');
|
|
1217
|
+
if (fs.existsSync(staticDir) && fs.existsSync(indexPath)) {
|
|
1218
|
+
viewerUiState.staticDir = staticDir;
|
|
1219
|
+
viewerUiState.indexPath = indexPath;
|
|
1220
|
+
(0, precompressed_asset_js_1.warmPrecompressedAssets)(staticDir, (msg, meta) => console.log(`[static] ${msg} ${JSON.stringify(meta)}`));
|
|
1221
|
+
const { version } = await viewerUI.getVersion();
|
|
1222
|
+
console.log(`[bootstrap] Viewer UI served from: ${staticDir} at ${VIEWER_MOUNT} (v${version})`);
|
|
1223
|
+
}
|
|
1224
|
+
else {
|
|
1225
|
+
console.warn(`[bootstrap] Viewer UI dist not found at: ${staticDir} — run 'npm run build' in addon-viewer-ui`);
|
|
1226
|
+
}
|
|
1275
1227
|
}
|
|
1276
1228
|
else {
|
|
1277
|
-
console.warn(
|
|
1229
|
+
console.warn('[bootstrap] viewer-ui capability not registered — viewer not served');
|
|
1278
1230
|
}
|
|
1279
1231
|
}
|
|
1280
|
-
|
|
1281
|
-
console.
|
|
1232
|
+
catch (err) {
|
|
1233
|
+
console.error('[bootstrap] Failed to set up viewer UI static serving:', err);
|
|
1282
1234
|
}
|
|
1283
|
-
}
|
|
1284
|
-
catch (err) {
|
|
1285
|
-
console.error('[bootstrap] Failed to set up viewer UI static serving:', err);
|
|
1286
|
-
}
|
|
1235
|
+
};
|
|
1287
1236
|
try {
|
|
1288
1237
|
await app.listen(port, host);
|
|
1289
1238
|
}
|
|
@@ -1300,6 +1249,9 @@ async function bootstrap() {
|
|
|
1300
1249
|
const logger = app.get(logging_service_1.LoggingService).createLogger('System');
|
|
1301
1250
|
const protocol = tlsOptions ? 'https' : 'http';
|
|
1302
1251
|
logger.info('CamStack server listening', { meta: { protocol, host, port, trpcRegistered } });
|
|
1252
|
+
// UI runners are not part of control-plane readiness. Their mutable route
|
|
1253
|
+
// state switches from 503 to static serving as soon as each capability is up.
|
|
1254
|
+
void Promise.all([resolveAdminUi(), resolveViewerUi()]);
|
|
1303
1255
|
// Post-boot: fork workers, register device streams, emit system.boot
|
|
1304
1256
|
const postBoot = app.get(post_boot_service_1.PostBootService);
|
|
1305
1257
|
await postBoot.run({ port, host, dataPath, trpcRegistered });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/server",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.89",
|
|
4
4
|
"private": false,
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -38,12 +38,12 @@
|
|
|
38
38
|
"@camstack/addon-auth": "1.2.11",
|
|
39
39
|
"@camstack/addon-decoder-nodeav": "1.2.9",
|
|
40
40
|
"@camstack/addon-notifiers": "1.2.13",
|
|
41
|
-
"@camstack/addon-pipeline": "1.2.
|
|
42
|
-
"@camstack/addon-pipeline-orchestrator": "1.2.
|
|
43
|
-
"@camstack/addon-post-analysis": "1.2.
|
|
41
|
+
"@camstack/addon-pipeline": "1.2.57",
|
|
42
|
+
"@camstack/addon-pipeline-orchestrator": "1.2.38",
|
|
43
|
+
"@camstack/addon-post-analysis": "1.2.58",
|
|
44
44
|
"@camstack/sdk": "1.2.11",
|
|
45
45
|
"@camstack/shm-ring": "1.1.9",
|
|
46
|
-
"@camstack/system": "1.2.
|
|
46
|
+
"@camstack/system": "1.2.75",
|
|
47
47
|
"@camstack/types": "1.2.55",
|
|
48
48
|
"@camstack/ui-library": "1.2.38",
|
|
49
49
|
"@fastify/compress": "^9.0.0",
|