@camstack/server 1.2.594 → 1.2.595
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/addon-root-inventory.js +50 -11
- package/dist/agent/addon-scan.js +44 -6
- package/dist/agent/agent-addon-runtime.js +8 -3
- package/dist/agent/agent-addon-updates.js +52 -12
- package/dist/agent/agent-cap-dispatch-service.js +5 -3
- package/dist/agent/agent-config.js +42 -7
- package/dist/agent/agent-deploy-swap.js +41 -5
- package/dist/agent/agent-group-runner.js +47 -8
- package/dist/agent/agent-http-auth.js +22 -10
- package/dist/agent/agent-http.js +113 -64
- package/dist/agent/agent-hub-addon-update.js +16 -10
- package/dist/agent/agent-hub-registration.js +7 -4
- package/dist/agent/agent-infra-capabilities.js +9 -5
- package/dist/agent/agent-json-file.js +42 -5
- package/dist/agent/agent-mesh-gate.js +8 -5
- package/dist/agent/agent-node-certificates.js +28 -25
- package/dist/agent/agent-pairing-local.js +47 -10
- package/dist/agent/agent-pairing-routes.js +20 -16
- package/dist/agent/agent-pairing-store.js +58 -18
- package/dist/agent/agent-reconnect.js +4 -1
- package/dist/agent/agent-service.js +71 -35
- package/dist/agent/agent-sniffing-listener.js +48 -11
- package/dist/agent/agent-undeploy.js +41 -5
- package/dist/agent/agent-update-routes.js +19 -14
- package/dist/agent/agent-update-service.js +80 -38
- package/dist/agent/apply-model-distribution.js +4 -1
- package/dist/agent/apply-stage-dev-uploads.js +9 -6
- package/dist/agent/derive-hub-url.js +8 -3
- package/dist/agent/fetch-bundle-from-hub.js +9 -6
- package/dist/agent/hubs-found.js +43 -6
- package/dist/agent/infra-boot-guard.js +4 -1
- package/dist/agent/infra-boot-plan.js +6 -2
- package/dist/agent/main.js +183 -148
- package/dist/agent/register-agent-cap-dispatch.js +6 -3
- package/dist/agent/serial-queue.js +7 -2
- package/dist/agent-status-page.js +4 -1
- package/dist/api/addon-upload.js +61 -22
- package/dist/api/agent-addon-delivery.js +11 -6
- package/dist/api/auth-whoami.js +4 -1
- package/dist/api/backup-download-pump.js +9 -5
- package/dist/api/backup-download.js +10 -7
- package/dist/api/core/addon-settings.router.js +19 -16
- package/dist/api/core/agent-pairing.router.js +79 -76
- package/dist/api/core/agents.router.js +24 -21
- package/dist/api/core/auth-admin-tokens.procedures.js +52 -48
- package/dist/api/core/auth-device-cert.procedures.js +67 -62
- package/dist/api/core/auth-limits.js +25 -16
- package/dist/api/core/auth-reauth.procedures.js +37 -32
- package/dist/api/core/auth-revoke-credentials.procedures.js +26 -22
- package/dist/api/core/auth-sessions.procedures.js +56 -52
- package/dist/api/core/auth.router.js +304 -300
- package/dist/api/core/cap-providers.js +121 -74
- package/dist/api/core/capabilities.router.js +37 -34
- package/dist/api/core/capability-preference-writes.js +17 -10
- package/dist/api/core/certificates.router.js +109 -106
- package/dist/api/core/cluster-nodes.router.js +18 -15
- package/dist/api/core/collection-preference.js +6 -2
- package/dist/api/core/event-bus-proxy.router.js +37 -34
- package/dist/api/core/failure-contributions.js +8 -4
- package/dist/api/core/hwaccel.router.js +20 -17
- package/dist/api/core/integration-secrets.js +12 -8
- package/dist/api/core/lifecycle-job-runner.js +49 -13
- package/dist/api/core/live-events.router.js +32 -29
- package/dist/api/core/load-contributions.js +8 -4
- package/dist/api/core/logging-settings.js +43 -30
- package/dist/api/core/logs.router.js +43 -40
- package/dist/api/core/notifications.router.js +18 -15
- package/dist/api/core/public-event-filter.js +8 -4
- package/dist/api/core/repl.router.js +21 -18
- package/dist/api/core/request-census-settings.js +16 -11
- package/dist/api/core/server-management.provider.js +4 -1
- package/dist/api/core/server-name.js +17 -9
- package/dist/api/core/site-location.js +24 -16
- package/dist/api/core/stream-probe.router.js +24 -21
- package/dist/api/core/system-events.router.js +26 -23
- package/dist/api/cors/cors-policy.js +29 -20
- package/dist/api/cors/register-cors-policy.js +14 -9
- package/dist/api/deploy-stage-registry.js +10 -6
- package/dist/api/dev-uploads-bundle.js +50 -12
- package/dist/api/health/adaptive-probe.routes.js +13 -9
- package/dist/api/health/health.routes.js +13 -8
- package/dist/api/hub-cluster-url.js +9 -3
- package/dist/api/model-distributor.js +50 -12
- package/dist/api/oauth2/consent-page.js +13 -10
- package/dist/api/oauth2/oauth2-routes.js +25 -17
- package/dist/api/oauth2/private-host.js +4 -1
- package/dist/api/server-upload-propagate.js +51 -15
- package/dist/api/server-upload.js +50 -13
- package/dist/api/sse-no-compression.js +4 -1
- package/dist/api/static/frame-policy.js +9 -4
- package/dist/api/static/precompressed-asset.js +57 -15
- package/dist/api/static/spa-mount.js +47 -8
- package/dist/api/static/spa-static.js +12 -5
- package/dist/api/tarball-manifest.js +44 -7
- package/dist/api/trpc/cap-mount-helpers.js +12 -7
- package/dist/api/trpc/cap-route-error-formatter.js +13 -10
- package/dist/api/trpc/cap-router-runtime.js +51 -40
- package/dist/api/trpc/client-ip.js +20 -9
- package/dist/api/trpc/core-cap-bridge.js +42 -32
- package/dist/api/trpc/device-collection-dispatch.js +27 -23
- package/dist/api/trpc/device-config-secret-redaction.js +40 -24
- package/dist/api/trpc/generated-cap-mounts.js +222 -218
- package/dist/api/trpc/generated-cap-routers.js +4856 -4676
- package/dist/api/trpc/http-request-census-hook.js +11 -8
- package/dist/api/trpc/http-request-census.js +41 -34
- package/dist/api/trpc/http-request-census.singleton.js +8 -4
- package/dist/api/trpc/localized-schema-paths.js +4 -1
- package/dist/api/trpc/principal-visibility.js +22 -14
- package/dist/api/trpc/remote-addon-update-grant.js +4 -1
- package/dist/api/trpc/scope-access.js +15 -10
- package/dist/api/trpc/server-provided-caps.js +7 -3
- package/dist/api/trpc/share-view-access.js +14 -6
- package/dist/api/trpc/trpc-error-device-tags.js +6 -3
- package/dist/api/trpc/trpc-error-principal.js +6 -2
- package/dist/api/trpc/trpc.context.js +25 -20
- package/dist/api/trpc/trpc.middleware.js +46 -38
- package/dist/api/trpc/trpc.router.js +99 -58
- package/dist/api/trpc/user-error.js +8 -5
- package/dist/api/trpc/ws-request-census.js +24 -17
- package/dist/api/trpc/ws-slow-consumer-guard.js +16 -11
- package/dist/api/upload-auth.js +9 -5
- package/dist/auth/addon-route-jwt-gate.js +7 -4
- package/dist/auth/addon-route-share-gate.js +12 -7
- package/dist/auth/app-confirm-page.js +22 -17
- package/dist/auth/app-confirm-routes.js +31 -26
- package/dist/auth/app-confirmations.js +44 -7
- package/dist/auth/app-page-shell.js +30 -17
- package/dist/auth/app-page-strings.js +17 -11
- package/dist/auth/app-passkey-page.js +21 -16
- package/dist/auth/app-passkey-routes.js +41 -37
- package/dist/auth/app-refused-route.js +18 -13
- package/dist/auth/auth-network-policy.js +15 -11
- package/dist/auth/auth-provider-gate.js +9 -4
- package/dist/auth/auth-rate-limit.js +7 -3
- package/dist/auth/client-address.js +9 -6
- package/dist/auth/device-enroll-proofs.js +43 -6
- package/dist/auth/external-link-intents.js +7 -3
- package/dist/auth/hub-sessions.js +12 -6
- package/dist/auth/native-link-completions.js +11 -7
- package/dist/auth/native-return.js +13 -9
- package/dist/auth/passkey-provider.js +32 -26
- package/dist/auth/second-factors.js +10 -6
- package/dist/auth/session-connections.js +12 -6
- package/dist/auth/session-cookie.js +23 -12
- package/dist/auth/session-lifetimes.js +12 -8
- package/dist/auth/session-mint.js +11 -4
- package/dist/auth/sso-finish-app.js +16 -13
- package/dist/auth/sso-finish-deps.js +27 -21
- package/dist/auth/sso-finish.js +38 -28
- package/dist/auth/sso-routes.js +25 -22
- package/dist/auth/used-bridge-ids.js +5 -1
- package/dist/boot/boot-config.js +55 -16
- package/dist/boot/init-sequence.js +4 -1
- package/dist/boot/integration-id-backfill.js +8 -3
- package/dist/boot/platform-keychain-key.js +42 -5
- package/dist/boot/post-boot.service.js +18 -14
- package/dist/boot/reconcile-lifecycle-jobs.js +6 -3
- package/dist/boot/retired-public-url-env.js +8 -4
- package/dist/bootstrap-packages.js +4 -1
- package/dist/closure-node-modules.js +38 -2
- package/dist/core/addon/addon-call-gateway.js +5 -1
- package/dist/core/addon/addon-package.service.js +123 -83
- package/dist/core/addon/addon-process-view.js +6 -2
- package/dist/core/addon/addon-realign-plan.js +4 -1
- package/dist/core/addon/addon-registry.service.js +187 -146
- package/dist/core/addon/addon-row-manifest.js +4 -1
- package/dist/core/addon/addon-search.service.js +7 -3
- package/dist/core/addon/addon-settings-provider.js +3 -1
- package/dist/core/addon/addon-uninstall.js +11 -4
- package/dist/core/addon/addon.tokens.js +5 -2
- package/dist/core/addon/device-meta-mirror.js +13 -8
- package/dist/core/addon/forked-custom-actions.js +11 -6
- package/dist/core/addon/integration-visibility.js +6 -3
- package/dist/core/addon/materialize-route-path.js +4 -1
- package/dist/core/addon/package-dir-utils.js +45 -8
- package/dist/core/addon/prune-misplaced-addons.js +43 -5
- package/dist/core/addon/runner-convergence.js +4 -1
- package/dist/core/addon/runner-serialization.js +4 -1
- package/dist/core/addon/runner-spawn-fanout.js +8 -3
- package/dist/core/addon/settings-door-wiring.js +8 -5
- package/dist/core/addon/single-flight-refresh.js +4 -1
- package/dist/core/addon-bridge/addon-bridge.service.js +44 -7
- package/dist/core/addon-pages/addon-pages.service.js +40 -3
- package/dist/core/addon-widgets/addon-widgets.service.js +42 -4
- package/dist/core/agent/agent-addon-backfill.js +17 -9
- package/dist/core/agent/agent-delivery-ledger.js +7 -3
- package/dist/core/agent/agent-liveness-event.js +6 -2
- package/dist/core/agent/agent-pairing-http.js +45 -41
- package/dist/core/agent/agent-registry.service.js +89 -50
- package/dist/core/agent/cluster-node-history-mirror.js +7 -3
- package/dist/core/agent/cluster-node-history-store.js +35 -31
- package/dist/core/agent/hub-agent-pairing.js +5 -1
- package/dist/core/agent/node-certificates-client.js +6 -3
- package/dist/core/agent/node-directory.js +4 -1
- package/dist/core/app-links/well-known-app-links.js +6 -2
- package/dist/core/auth/auth.service.js +8 -3
- package/dist/core/auth/handoff-code.service.js +49 -11
- package/dist/core/auth/record-kinds.js +19 -16
- package/dist/core/auth/share-token.service.js +79 -42
- package/dist/core/capability/capability.service.js +5 -1
- package/dist/core/config/config.schema.js +6 -1
- package/dist/core/config/config.service.js +6 -2
- package/dist/core/events/event-bus.service.js +7 -3
- package/dist/core/feature/feature.service.js +6 -2
- package/dist/core/lifecycle/lifecycle-runner.singleton.js +10 -5
- package/dist/core/lifecycle/lifecycle-state-machine.js +5 -1
- package/dist/core/logging/log-ring-buffer.js +5 -1
- package/dist/core/logging/logging.service.js +9 -5
- package/dist/core/logging/scoped-logger.js +5 -1
- package/dist/core/moleculer/agent-readiness-pull.js +4 -1
- package/dist/core/moleculer/agent-registration-request.js +7 -3
- package/dist/core/moleculer/cap-call-fn.js +7 -4
- package/dist/core/moleculer/cap-route-authority.js +8 -3
- package/dist/core/moleculer/hub-mesh-guard.js +9 -5
- package/dist/core/moleculer/moleculer.service.js +86 -79
- package/dist/core/network/network-quality.service.js +6 -2
- package/dist/core/notification/notification-wrapper.service.js +7 -3
- package/dist/core/notification/toast-wrapper.service.js +7 -3
- package/dist/core/provider/provider.tokens.js +4 -1
- package/dist/core/repl/repl-engine.service.js +8 -4
- package/dist/core/runner-health/runner-boot-check.js +15 -7
- package/dist/core/runner-health/update-preflight.js +14 -9
- package/dist/core/server-update/server-update.service.js +49 -12
- package/dist/core/server-update/system-apply-closure-patches.js +42 -6
- package/dist/core/server-update/system-ensure-prebuilds.js +6 -3
- package/dist/core/server-update/system-exec-npm.js +5 -2
- package/dist/core/storage/fs-storage-backend.js +5 -1
- package/dist/core/storage/storage-location-manager.js +5 -1
- package/dist/core/storage/storage.service.js +6 -2
- package/dist/core/streaming/ffmpeg-stream-info.js +8 -3
- package/dist/core/streaming/stream-probe.service.js +16 -12
- package/dist/core/streaming/ttl-cache.js +5 -1
- package/dist/core/topology/topology-emitter.service.js +23 -19
- package/dist/core/update-availability-emitter.js +7 -3
- package/dist/core/update-availability-store.js +45 -8
- package/dist/core/updates/agent-installed-packages.js +9 -5
- package/dist/core/updates/restart-marker.js +8 -3
- package/dist/core/updates/update-check-scheduler.js +18 -13
- package/dist/core/updates/wrapper-update-checker.js +7 -3
- package/dist/first-boot-addon-plan.js +6 -2
- package/dist/framework-nodepath.js +38 -2
- package/dist/i18n/request-lang.js +11 -7
- package/dist/launcher.js +65 -30
- package/dist/lifecycle-journal-path.js +38 -2
- package/dist/main.js +324 -286
- package/dist/manual-boot.js +156 -117
- package/dist/node-role.js +4 -1
- package/dist/package-inventory.js +48 -7
- package/dist/pending-restore.js +43 -7
- package/dist/single-copy-cleanup-runner.js +45 -9
- package/dist/single-copy-cleanup.js +15 -7
- package/dist/sqlite-restore.js +48 -9
- package/dist/starter.js +39 -4
- package/dist/ui-serving/lazy-ui-serving.js +39 -3
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Reads the hub's human name (D861) from the one row that holds it,
|
|
3
4
|
* `system-settings` / `server.name`, and falls back to the machine hostname.
|
|
@@ -11,37 +12,44 @@
|
|
|
11
12
|
* hub that looks unnamed because its store is down is not mistaken for one that
|
|
12
13
|
* was never named.
|
|
13
14
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
16
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
17
|
+
};
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.ServerNameReader = void 0;
|
|
20
|
+
exports.serverNameReaderFor = serverNameReaderFor;
|
|
21
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
22
|
+
const types_1 = require("@camstack/types");
|
|
16
23
|
const SYSTEM_SETTINGS_COLLECTION = 'system-settings';
|
|
17
|
-
|
|
24
|
+
class ServerNameReader {
|
|
18
25
|
deps;
|
|
19
26
|
constructor(deps) {
|
|
20
27
|
this.deps = deps;
|
|
21
28
|
}
|
|
22
29
|
async read() {
|
|
23
|
-
const hostname = (this.deps.hostname ??
|
|
30
|
+
const hostname = (this.deps.hostname ?? node_os_1.default.hostname)();
|
|
24
31
|
try {
|
|
25
32
|
const stored = await this.deps.store.get({
|
|
26
33
|
collection: SYSTEM_SETTINGS_COLLECTION,
|
|
27
|
-
key: SERVER_NAME_SETTING_KEY,
|
|
34
|
+
key: types_1.SERVER_NAME_SETTING_KEY,
|
|
28
35
|
});
|
|
29
|
-
return resolveServerName(stored, hostname);
|
|
36
|
+
return (0, types_1.resolveServerName)(stored, hostname);
|
|
30
37
|
}
|
|
31
38
|
catch (err) {
|
|
32
39
|
this.deps.logger?.warn('server name read failed - answering the hostname default', {
|
|
33
|
-
meta: { error: errMsg(err) },
|
|
40
|
+
meta: { error: (0, types_1.errMsg)(err) },
|
|
34
41
|
});
|
|
35
|
-
return resolveServerName(undefined, hostname);
|
|
42
|
+
return (0, types_1.resolveServerName)(undefined, hostname);
|
|
36
43
|
}
|
|
37
44
|
}
|
|
38
45
|
}
|
|
46
|
+
exports.ServerNameReader = ServerNameReader;
|
|
39
47
|
/**
|
|
40
48
|
* A reader that resolves the `settings-store` cap lazily from the registry (it
|
|
41
49
|
* boots after the routers are built). Without a registry, or before the cap is
|
|
42
50
|
* up, the read throws and {@link ServerNameReader} answers the hostname default.
|
|
43
51
|
*/
|
|
44
|
-
|
|
52
|
+
function serverNameReaderFor(registry, logger) {
|
|
45
53
|
return new ServerNameReader({
|
|
46
54
|
store: {
|
|
47
55
|
get: async (input) => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Site location — the installation's coordinates, as a fact of the SITE.
|
|
3
4
|
*
|
|
@@ -44,9 +45,15 @@
|
|
|
44
45
|
* and a derivation must never overwrite a typed value, which is why
|
|
45
46
|
* `SiteLocationService.detect` refuses when `source === 'operator-set'`.
|
|
46
47
|
*/
|
|
47
|
-
|
|
48
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
|
+
exports.SiteLocationService = exports.GEO_IP_ENDPOINTS = exports.GEO_IP_TIMEOUT_MS = exports.SITE_LOCATION_KEY = void 0;
|
|
50
|
+
exports.isUsableCoordinate = isUsableCoordinate;
|
|
51
|
+
exports.toStatus = toStatus;
|
|
52
|
+
exports.parseGeoIpPayload = parseGeoIpPayload;
|
|
53
|
+
exports.geolocatePublicIp = geolocatePublicIp;
|
|
54
|
+
const types_1 = require("@camstack/types");
|
|
48
55
|
/** The `system-settings` row this module owns. */
|
|
49
|
-
|
|
56
|
+
exports.SITE_LOCATION_KEY = 'site-location';
|
|
50
57
|
const SYSTEM_SETTINGS_COLLECTION = 'system-settings';
|
|
51
58
|
/**
|
|
52
59
|
* The addon store the coordinates used to live in. Read ONCE, lazily, to
|
|
@@ -63,7 +70,7 @@ const LEGACY_LNG_KEY = 'siteLongitude';
|
|
|
63
70
|
* is long enough for a cold TLS handshake to a CDN and short enough that a
|
|
64
71
|
* captive-portal black hole does not look like a hang.
|
|
65
72
|
*/
|
|
66
|
-
|
|
73
|
+
exports.GEO_IP_TIMEOUT_MS = 4_000;
|
|
67
74
|
/**
|
|
68
75
|
* No-key, HTTPS geo-IP endpoints, tried in order until one parses.
|
|
69
76
|
*
|
|
@@ -72,7 +79,7 @@ export const GEO_IP_TIMEOUT_MS = 4_000;
|
|
|
72
79
|
* and the request carries nothing but the hub's IP — which the endpoint would
|
|
73
80
|
* see regardless, since seeing it is the entire mechanism.
|
|
74
81
|
*/
|
|
75
|
-
|
|
82
|
+
exports.GEO_IP_ENDPOINTS = [
|
|
76
83
|
'https://ipinfo.io/json',
|
|
77
84
|
'https://ipapi.co/json/',
|
|
78
85
|
];
|
|
@@ -82,7 +89,7 @@ export const GEO_IP_ENDPOINTS = [
|
|
|
82
89
|
* is treated as absent rather than clamped: a wrong-but-plausible coordinate
|
|
83
90
|
* produces a wrong sunrise silently, which is worse than the declared fallback.
|
|
84
91
|
*/
|
|
85
|
-
|
|
92
|
+
function isUsableCoordinate(lat, lng) {
|
|
86
93
|
if (typeof lat !== 'number' || typeof lng !== 'number')
|
|
87
94
|
return false;
|
|
88
95
|
if (!Number.isFinite(lat) || !Number.isFinite(lng))
|
|
@@ -92,7 +99,7 @@ export function isUsableCoordinate(lat, lng) {
|
|
|
92
99
|
return lat !== 0 || lng !== 0;
|
|
93
100
|
}
|
|
94
101
|
/** Project a stored row onto the wire shape. */
|
|
95
|
-
|
|
102
|
+
function toStatus(record) {
|
|
96
103
|
if (record === null) {
|
|
97
104
|
return { location: null, derivationAttemptedAt: null, derivationError: null };
|
|
98
105
|
}
|
|
@@ -145,7 +152,7 @@ function asRecord(raw) {
|
|
|
145
152
|
* three field names, and an adapter per vendor is how a 30-line fetch becomes a
|
|
146
153
|
* folder.
|
|
147
154
|
*/
|
|
148
|
-
|
|
155
|
+
function parseGeoIpPayload(payload) {
|
|
149
156
|
if (payload === null || typeof payload !== 'object')
|
|
150
157
|
return null;
|
|
151
158
|
const obj = payload;
|
|
@@ -176,12 +183,12 @@ export function parseGeoIpPayload(payload) {
|
|
|
176
183
|
* Every failure mode collapses to `null` plus a log line: this is a *default*,
|
|
177
184
|
* and a default that throws would take down whatever asked for it.
|
|
178
185
|
*/
|
|
179
|
-
|
|
186
|
+
async function geolocatePublicIp(fetchImpl, logger, endpoints = exports.GEO_IP_ENDPOINTS) {
|
|
180
187
|
for (const url of endpoints) {
|
|
181
188
|
try {
|
|
182
189
|
const response = await fetchImpl(url, {
|
|
183
190
|
headers: { Accept: 'application/json' },
|
|
184
|
-
signal: AbortSignal.timeout(GEO_IP_TIMEOUT_MS),
|
|
191
|
+
signal: AbortSignal.timeout(exports.GEO_IP_TIMEOUT_MS),
|
|
185
192
|
});
|
|
186
193
|
if (!response.ok) {
|
|
187
194
|
logger?.debug('geo-IP endpoint refused', { meta: { url, status: response.status } });
|
|
@@ -193,7 +200,7 @@ export async function geolocatePublicIp(fetchImpl, logger, endpoints = GEO_IP_EN
|
|
|
193
200
|
logger?.debug('geo-IP endpoint answered without usable coordinates', { meta: { url } });
|
|
194
201
|
}
|
|
195
202
|
catch (err) {
|
|
196
|
-
logger?.debug('geo-IP endpoint failed', { meta: { url, error: errMsg(err) } });
|
|
203
|
+
logger?.debug('geo-IP endpoint failed', { meta: { url, error: (0, types_1.errMsg)(err) } });
|
|
197
204
|
}
|
|
198
205
|
}
|
|
199
206
|
return null;
|
|
@@ -206,7 +213,7 @@ export async function geolocatePublicIp(fetchImpl, logger, endpoints = GEO_IP_EN
|
|
|
206
213
|
* `derivationAttemptedAt` bounds them across requests and across restarts. The
|
|
207
214
|
* durable marker is the real guarantee; the in-process one only stops a burst.
|
|
208
215
|
*/
|
|
209
|
-
|
|
216
|
+
class SiteLocationService {
|
|
210
217
|
deps;
|
|
211
218
|
static inFlight = null;
|
|
212
219
|
constructor(deps) {
|
|
@@ -223,7 +230,7 @@ export class SiteLocationService {
|
|
|
223
230
|
try {
|
|
224
231
|
return asRecord(await this.deps.store.get({
|
|
225
232
|
collection: SYSTEM_SETTINGS_COLLECTION,
|
|
226
|
-
key: SITE_LOCATION_KEY,
|
|
233
|
+
key: exports.SITE_LOCATION_KEY,
|
|
227
234
|
}));
|
|
228
235
|
}
|
|
229
236
|
catch (err) {
|
|
@@ -231,14 +238,14 @@ export class SiteLocationService {
|
|
|
231
238
|
// the derivation off to overwrite a value that exists. Rethrow-as-null is
|
|
232
239
|
// exactly the D49 shape this repo has been bitten by, so the caller is
|
|
233
240
|
// told instead.
|
|
234
|
-
this.deps.logger?.warn('site location read failed', { meta: { error: errMsg(err) } });
|
|
241
|
+
this.deps.logger?.warn('site location read failed', { meta: { error: (0, types_1.errMsg)(err) } });
|
|
235
242
|
throw err;
|
|
236
243
|
}
|
|
237
244
|
}
|
|
238
245
|
async write(record) {
|
|
239
246
|
await this.deps.store.set({
|
|
240
247
|
collection: SYSTEM_SETTINGS_COLLECTION,
|
|
241
|
-
key: SITE_LOCATION_KEY,
|
|
248
|
+
key: exports.SITE_LOCATION_KEY,
|
|
242
249
|
value: record,
|
|
243
250
|
});
|
|
244
251
|
}
|
|
@@ -268,7 +275,7 @@ export class SiteLocationService {
|
|
|
268
275
|
}
|
|
269
276
|
catch (err) {
|
|
270
277
|
this.deps.logger?.debug('legacy site coordinates read failed', {
|
|
271
|
-
meta: { error: errMsg(err) },
|
|
278
|
+
meta: { error: (0, types_1.errMsg)(err) },
|
|
272
279
|
});
|
|
273
280
|
return null;
|
|
274
281
|
}
|
|
@@ -333,7 +340,7 @@ export class SiteLocationService {
|
|
|
333
340
|
// The marker is what makes it one-shot. Losing it costs a retry on the
|
|
334
341
|
// next read, never a wrong value.
|
|
335
342
|
this.deps.logger?.warn('site location failure marker could not be persisted', {
|
|
336
|
-
meta: { error: errMsg(err) },
|
|
343
|
+
meta: { error: (0, types_1.errMsg)(err) },
|
|
337
344
|
});
|
|
338
345
|
}
|
|
339
346
|
return toStatus(failure);
|
|
@@ -379,3 +386,4 @@ export class SiteLocationService {
|
|
|
379
386
|
return this.derive();
|
|
380
387
|
}
|
|
381
388
|
}
|
|
389
|
+
exports.SiteLocationService = SiteLocationService;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createStreamProbeRouter = createStreamProbeRouter;
|
|
1
4
|
/**
|
|
2
5
|
* Stream probe router — fixed core API (not a capability).
|
|
3
6
|
*
|
|
@@ -8,32 +11,32 @@
|
|
|
8
11
|
* Previously these endpoints lived under `streaming.probeStream` — moved
|
|
9
12
|
* here as part of eliminating the `streaming` aggregator capability.
|
|
10
13
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const ProbedStreamSchema = z.object({
|
|
15
|
-
width: z.number().optional(),
|
|
16
|
-
height: z.number().optional(),
|
|
17
|
-
codec: z.string().optional(),
|
|
18
|
-
fps: z.number().optional(),
|
|
19
|
-
bitrateKbps: z.number().optional(),
|
|
20
|
-
quality: z.enum(['high', 'mid', 'low']),
|
|
14
|
+
const zod_1 = require("zod");
|
|
15
|
+
const types_1 = require("@camstack/types");
|
|
16
|
+
const trpc_middleware_js_1 = require("../trpc/trpc.middleware.js");
|
|
17
|
+
const ProbedStreamSchema = zod_1.z.object({
|
|
18
|
+
width: zod_1.z.number().optional(),
|
|
19
|
+
height: zod_1.z.number().optional(),
|
|
20
|
+
codec: zod_1.z.string().optional(),
|
|
21
|
+
fps: zod_1.z.number().optional(),
|
|
22
|
+
bitrateKbps: zod_1.z.number().optional(),
|
|
23
|
+
quality: zod_1.z.enum(['high', 'mid', 'low']),
|
|
21
24
|
});
|
|
22
|
-
const FieldProbeResultSchema = z.object({
|
|
23
|
-
status: z.enum(['ok', 'error']),
|
|
24
|
-
labels: z.array(z.string()).optional(),
|
|
25
|
-
error: z.string().optional(),
|
|
25
|
+
const FieldProbeResultSchema = zod_1.z.object({
|
|
26
|
+
status: zod_1.z.enum(['ok', 'error']),
|
|
27
|
+
labels: zod_1.z.array(zod_1.z.string()).optional(),
|
|
28
|
+
error: zod_1.z.string().optional(),
|
|
26
29
|
});
|
|
27
|
-
|
|
28
|
-
return trpcRouter({
|
|
29
|
-
probe: adminProcedure
|
|
30
|
-
.input(z.object({ url: z.string(), force: z.boolean().optional() }))
|
|
30
|
+
function createStreamProbeRouter(sp) {
|
|
31
|
+
return (0, trpc_middleware_js_1.trpcRouter)({
|
|
32
|
+
probe: trpc_middleware_js_1.adminProcedure
|
|
33
|
+
.input(zod_1.z.object({ url: zod_1.z.string(), force: zod_1.z.boolean().optional() }))
|
|
31
34
|
.output(ProbedStreamSchema)
|
|
32
35
|
.mutation(async ({ input }) => {
|
|
33
36
|
if (!sp)
|
|
34
37
|
throw new Error('StreamProbeService not available');
|
|
35
38
|
const metadata = await sp.probe(input.url, { force: input.force });
|
|
36
|
-
return { ...metadata, quality: classifyStream(metadata) };
|
|
39
|
+
return { ...metadata, quality: (0, types_1.classifyStream)(metadata) };
|
|
37
40
|
}),
|
|
38
41
|
/**
|
|
39
42
|
* Generic field probe — decides stream vs HTTP reachability based on
|
|
@@ -43,8 +46,8 @@ export function createStreamProbeRouter(sp) {
|
|
|
43
46
|
* that need the kernel's probe without re-implementing ffprobe /
|
|
44
47
|
* HTTP fetch in every addon.
|
|
45
48
|
*/
|
|
46
|
-
probeField: adminProcedure
|
|
47
|
-
.input(z.object({ key: z.string(), value: z.unknown() }))
|
|
49
|
+
probeField: trpc_middleware_js_1.adminProcedure
|
|
50
|
+
.input(zod_1.z.object({ key: zod_1.z.string(), value: zod_1.z.unknown() }))
|
|
48
51
|
.output(FieldProbeResultSchema)
|
|
49
52
|
.mutation(async ({ input }) => {
|
|
50
53
|
if (!sp)
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSystemEventsRouter = createSystemEventsRouter;
|
|
1
4
|
/**
|
|
2
5
|
* System events router — fixed core API (not a capability).
|
|
3
6
|
*
|
|
@@ -6,10 +9,10 @@
|
|
|
6
9
|
* All filters are applied server-side by the SystemEventBus — the client
|
|
7
10
|
* only receives matching events.
|
|
8
11
|
*/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
const zod_1 = require("zod");
|
|
13
|
+
const trpc_middleware_js_1 = require("../trpc/trpc.middleware.js");
|
|
14
|
+
const principal_visibility_js_1 = require("../trpc/principal-visibility.js");
|
|
15
|
+
const public_event_filter_js_1 = require("./public-event-filter.js");
|
|
13
16
|
function serialize(e) {
|
|
14
17
|
return {
|
|
15
18
|
id: e.id,
|
|
@@ -30,20 +33,20 @@ function serialize(e) {
|
|
|
30
33
|
* Category handling is split (asymmetric): see the per-method schemas
|
|
31
34
|
* below.
|
|
32
35
|
*/
|
|
33
|
-
const ScopeFieldsSchema = z.object({
|
|
36
|
+
const ScopeFieldsSchema = zod_1.z.object({
|
|
34
37
|
/** Legacy source filter (exact type+id match). */
|
|
35
|
-
source: z
|
|
38
|
+
source: zod_1.z
|
|
36
39
|
.object({
|
|
37
|
-
type: z.string(),
|
|
38
|
-
id: z.union([z.string(), z.number()]),
|
|
40
|
+
type: zod_1.z.string(),
|
|
41
|
+
id: zod_1.z.union([zod_1.z.string(), zod_1.z.number()]),
|
|
39
42
|
})
|
|
40
43
|
.optional(),
|
|
41
44
|
/** Agent/node filter (prefix match: 'hub' matches 'hub/pipeline'). */
|
|
42
|
-
agentId: z.string().optional(),
|
|
45
|
+
agentId: zod_1.z.string().optional(),
|
|
43
46
|
/** Addon filter. Matches source.addonId or source.id when type='addon'. */
|
|
44
|
-
addonId: z.string().optional(),
|
|
47
|
+
addonId: zod_1.z.string().optional(),
|
|
45
48
|
/** Device filter. Matches source.deviceId or source.id when type='device'. */
|
|
46
|
-
deviceId: z.number().optional(),
|
|
49
|
+
deviceId: zod_1.z.number().optional(),
|
|
47
50
|
});
|
|
48
51
|
/**
|
|
49
52
|
* `getRecent` accepts a category array so the UI can drive a
|
|
@@ -58,8 +61,8 @@ const ScopeFieldsSchema = z.object({
|
|
|
58
61
|
* `category` field as `string | string[]`.
|
|
59
62
|
*/
|
|
60
63
|
const GetRecentInputSchema = ScopeFieldsSchema.extend({
|
|
61
|
-
category: z.union([z.string(), z.array(z.string())]).optional(),
|
|
62
|
-
limit: z.number().int().min(1).max(500).optional(),
|
|
64
|
+
category: zod_1.z.union([zod_1.z.string(), zod_1.z.array(zod_1.z.string())]).optional(),
|
|
65
|
+
limit: zod_1.z.number().int().min(1).max(500).optional(),
|
|
63
66
|
});
|
|
64
67
|
/**
|
|
65
68
|
* `subscribe` keeps category as a single string — a WIRE choice now, not a bus
|
|
@@ -75,17 +78,17 @@ const GetRecentInputSchema = ScopeFieldsSchema.extend({
|
|
|
75
78
|
* wildcard and narrow client-side.
|
|
76
79
|
*/
|
|
77
80
|
const SubscribeInputSchema = ScopeFieldsSchema.extend({
|
|
78
|
-
category: z.string().optional(),
|
|
81
|
+
category: zod_1.z.string().optional(),
|
|
79
82
|
});
|
|
80
|
-
|
|
81
|
-
return trpcRouter({
|
|
82
|
-
getRecent: protectedProcedure.input(GetRecentInputSchema).query(({ input, ctx }) => {
|
|
83
|
+
function createSystemEventsRouter(eb) {
|
|
84
|
+
return (0, trpc_middleware_js_1.trpcRouter)({
|
|
85
|
+
getRecent: trpc_middleware_js_1.protectedProcedure.input(GetRecentInputSchema).query(({ input, ctx }) => {
|
|
83
86
|
// SECURITY (F0.5): filter the history to what a device-restricted scoped
|
|
84
87
|
// token may view. null ⇒ admin / broad category-view operator ⇒ unfiltered.
|
|
85
88
|
// An owner-only category (D224) is not history anyone may read. Both
|
|
86
89
|
// projections run BEFORE the limit, so N asked is N public rows answered.
|
|
87
|
-
const keep = scopedEventVisibility(ctx.user, ctx.deviceScopeLookup);
|
|
88
|
-
return publicRecent(eb, {
|
|
90
|
+
const keep = (0, principal_visibility_js_1.scopedEventVisibility)(ctx.user, ctx.deviceScopeLookup);
|
|
91
|
+
return (0, public_event_filter_js_1.publicRecent)(eb, {
|
|
89
92
|
...(input.source ? { source: input.source } : {}),
|
|
90
93
|
...(input.agentId ? { agentId: input.agentId } : {}),
|
|
91
94
|
...(input.addonId ? { addonId: input.addonId } : {}),
|
|
@@ -93,11 +96,11 @@ export function createSystemEventsRouter(eb) {
|
|
|
93
96
|
...(input.category ? { category: input.category } : {}),
|
|
94
97
|
}, input.limit, keep).map(serialize);
|
|
95
98
|
}),
|
|
96
|
-
subscribe: protectedProcedure.input(SubscribeInputSchema).subscription(({ input, ctx }) => {
|
|
99
|
+
subscribe: trpc_middleware_js_1.protectedProcedure.input(SubscribeInputSchema).subscription(({ input, ctx }) => {
|
|
97
100
|
// SECURITY (F0.5): a device-restricted scoped token must not receive every
|
|
98
101
|
// camera's live events. Same projection as `getRecent`.
|
|
99
|
-
const keep = scopedEventVisibility(ctx.user, ctx.deviceScopeLookup);
|
|
100
|
-
return iterableSubscription((push) => {
|
|
102
|
+
const keep = (0, principal_visibility_js_1.scopedEventVisibility)(ctx.user, ctx.deviceScopeLookup);
|
|
103
|
+
return (0, trpc_middleware_js_1.iterableSubscription)((push) => {
|
|
101
104
|
return eb.subscribe({
|
|
102
105
|
...(input.source ? { source: input.source } : {}),
|
|
103
106
|
...(input.agentId ? { agentId: input.agentId } : {}),
|
|
@@ -105,7 +108,7 @@ export function createSystemEventsRouter(eb) {
|
|
|
105
108
|
...(input.deviceId !== undefined ? { deviceId: input.deviceId } : {}),
|
|
106
109
|
...(input.category ? { category: input.category } : {}),
|
|
107
110
|
}, (event) => {
|
|
108
|
-
if (!isPublicEvent(event))
|
|
111
|
+
if (!(0, public_event_filter_js_1.isPublicEvent)(event))
|
|
109
112
|
return;
|
|
110
113
|
if (!keep || keep(event))
|
|
111
114
|
push(serialize(event));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* cors-policy — which browser origins may READ this hub's responses (D707).
|
|
3
4
|
*
|
|
@@ -31,15 +32,23 @@
|
|
|
31
32
|
*
|
|
32
33
|
* Pure: no Fastify, no I/O — `register-cors-policy.ts` is the glue.
|
|
33
34
|
*/
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.CORS_PREFLIGHT_MAX_AGE_SEC = exports.CORS_EXPOSED_HEADERS = exports.CORS_ALLOWED_HEADERS = exports.CORS_ALLOWED_METHODS = exports.OPAQUE_ORIGIN = exports.VIEWER_PWA_ORIGIN = void 0;
|
|
37
|
+
exports.normalizeOrigin = normalizeOrigin;
|
|
38
|
+
exports.parseConfiguredOrigins = parseConfiguredOrigins;
|
|
39
|
+
exports.builtInOrigins = builtInOrigins;
|
|
40
|
+
exports.judgeCorsOrigin = judgeCorsOrigin;
|
|
41
|
+
exports.corsResponseHeaders = corsResponseHeaders;
|
|
42
|
+
exports.appendVary = appendVary;
|
|
34
43
|
/** The hosted viewer PWA (operator decision 2026-10-06). */
|
|
35
|
-
|
|
44
|
+
exports.VIEWER_PWA_ORIGIN = 'https://camstack.zentik.app';
|
|
36
45
|
/**
|
|
37
46
|
* The serialisation of an opaque origin. The native app's OTA embed
|
|
38
47
|
* (`assets/embed/camstack-embed.html`, loaded from `file://`) sends it.
|
|
39
48
|
*/
|
|
40
|
-
|
|
49
|
+
exports.OPAQUE_ORIGIN = 'null';
|
|
41
50
|
/** Every method a hub route answers. */
|
|
42
|
-
|
|
51
|
+
exports.CORS_ALLOWED_METHODS = 'GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS';
|
|
43
52
|
/**
|
|
44
53
|
* Request headers a cross-origin caller may send. `authorization` is the
|
|
45
54
|
* whole point; `range` is recorded-media seeking; the `trpc-*` / `x-trpc-*`
|
|
@@ -47,25 +56,25 @@ export const CORS_ALLOWED_METHODS = 'GET, HEAD, POST, PUT, PATCH, DELETE, OPTION
|
|
|
47
56
|
* the cookie→bearer exchange is a same-origin mechanism and a cross-origin
|
|
48
57
|
* page has no business asking for it.
|
|
49
58
|
*/
|
|
50
|
-
|
|
59
|
+
exports.CORS_ALLOWED_HEADERS = 'authorization, content-type, range, if-range, if-none-match, cache-control, trpc-accept, x-trpc-source, x-trpc-batch-mode';
|
|
51
60
|
/**
|
|
52
61
|
* Response headers a cross-origin script may read. The byte-range trio is
|
|
53
62
|
* recorded playback (a seek reads `content-range`); `content-disposition` is
|
|
54
63
|
* export downloads; `retry-after` is the rate limiter; the `x-camstack-*`
|
|
55
64
|
* trio is the clip-still contract (a 204 that says WHY there is no image).
|
|
56
65
|
*/
|
|
57
|
-
|
|
66
|
+
exports.CORS_EXPOSED_HEADERS = 'content-range, accept-ranges, content-length, content-disposition, etag, retry-after, x-camstack-reason, x-camstack-reason-code, x-camstack-disposition';
|
|
58
67
|
/** Preflight cache. Chromium caps at 7200 s; Firefox honours up to 86400. */
|
|
59
|
-
|
|
68
|
+
exports.CORS_PREFLIGHT_MAX_AGE_SEC = 7200;
|
|
60
69
|
/**
|
|
61
70
|
* The canonical serialisation of an origin (`scheme://host[:port]`, lower
|
|
62
71
|
* case, default port dropped), or `null` when `raw` is not an http(s) origin.
|
|
63
72
|
* `null` (the opaque origin) is accepted verbatim.
|
|
64
73
|
*/
|
|
65
|
-
|
|
74
|
+
function normalizeOrigin(raw) {
|
|
66
75
|
const trimmed = raw.trim();
|
|
67
|
-
if (trimmed === OPAQUE_ORIGIN)
|
|
68
|
-
return OPAQUE_ORIGIN;
|
|
76
|
+
if (trimmed === exports.OPAQUE_ORIGIN)
|
|
77
|
+
return exports.OPAQUE_ORIGIN;
|
|
69
78
|
let url;
|
|
70
79
|
try {
|
|
71
80
|
url = new URL(trimmed);
|
|
@@ -85,7 +94,7 @@ export function normalizeOrigin(raw) {
|
|
|
85
94
|
return url.origin;
|
|
86
95
|
}
|
|
87
96
|
/** Read the configured value (array from YAML, already split from env). */
|
|
88
|
-
|
|
97
|
+
function parseConfiguredOrigins(value) {
|
|
89
98
|
if (!Array.isArray(value))
|
|
90
99
|
return { origins: [], rejected: [] };
|
|
91
100
|
const origins = [];
|
|
@@ -104,8 +113,8 @@ export function parseConfiguredOrigins(value) {
|
|
|
104
113
|
return { origins, rejected };
|
|
105
114
|
}
|
|
106
115
|
/** The built-in entries. */
|
|
107
|
-
|
|
108
|
-
return [VIEWER_PWA_ORIGIN, OPAQUE_ORIGIN];
|
|
116
|
+
function builtInOrigins() {
|
|
117
|
+
return [exports.VIEWER_PWA_ORIGIN, exports.OPAQUE_ORIGIN];
|
|
109
118
|
}
|
|
110
119
|
/** The first value of a possibly comma-joined forwarding header, lower case. */
|
|
111
120
|
function firstHost(value) {
|
|
@@ -116,7 +125,7 @@ function firstHost(value) {
|
|
|
116
125
|
}
|
|
117
126
|
/** `host[:port]` of an http(s) origin, or null. */
|
|
118
127
|
function hostOfOrigin(origin) {
|
|
119
|
-
if (origin === OPAQUE_ORIGIN)
|
|
128
|
+
if (origin === exports.OPAQUE_ORIGIN)
|
|
120
129
|
return null;
|
|
121
130
|
try {
|
|
122
131
|
return new URL(origin).host.toLowerCase();
|
|
@@ -129,7 +138,7 @@ function hostOfOrigin(origin) {
|
|
|
129
138
|
* Judge one request's `Origin`. `listed` is the full allowlist (built-ins and
|
|
130
139
|
* the operator's extras), already normalised.
|
|
131
140
|
*/
|
|
132
|
-
|
|
141
|
+
function judgeCorsOrigin(facts, listed) {
|
|
133
142
|
const raw = facts.origin;
|
|
134
143
|
if (raw === undefined || raw.length === 0)
|
|
135
144
|
return { kind: 'no-origin' };
|
|
@@ -153,25 +162,25 @@ export function judgeCorsOrigin(facts, listed) {
|
|
|
153
162
|
* page, and `Vary: Origin` (set by the glue on every response) keeps a cache
|
|
154
163
|
* from serving one origin's answer to another.
|
|
155
164
|
*/
|
|
156
|
-
|
|
165
|
+
function corsResponseHeaders(origin, preflight) {
|
|
157
166
|
const base = {
|
|
158
167
|
'access-control-allow-origin': origin,
|
|
159
|
-
'access-control-expose-headers': CORS_EXPOSED_HEADERS,
|
|
168
|
+
'access-control-expose-headers': exports.CORS_EXPOSED_HEADERS,
|
|
160
169
|
};
|
|
161
170
|
if (preflight === null)
|
|
162
171
|
return base;
|
|
163
172
|
return {
|
|
164
173
|
...base,
|
|
165
|
-
'access-control-allow-methods': CORS_ALLOWED_METHODS,
|
|
166
|
-
'access-control-allow-headers': CORS_ALLOWED_HEADERS,
|
|
167
|
-
'access-control-max-age': String(CORS_PREFLIGHT_MAX_AGE_SEC),
|
|
174
|
+
'access-control-allow-methods': exports.CORS_ALLOWED_METHODS,
|
|
175
|
+
'access-control-allow-headers': exports.CORS_ALLOWED_HEADERS,
|
|
176
|
+
'access-control-max-age': String(exports.CORS_PREFLIGHT_MAX_AGE_SEC),
|
|
168
177
|
// A public origin (the PWA) calling a LAN hub is a "private network"
|
|
169
178
|
// request to Chromium; its preflight must be answered explicitly.
|
|
170
179
|
...(preflight.requestPrivateNetwork ? { 'access-control-allow-private-network': 'true' } : {}),
|
|
171
180
|
};
|
|
172
181
|
}
|
|
173
182
|
/** Append `token` to a `Vary` value unless it is already there. */
|
|
174
|
-
|
|
183
|
+
function appendVary(existing, token) {
|
|
175
184
|
if (existing === undefined || existing.trim().length === 0)
|
|
176
185
|
return token;
|
|
177
186
|
const parts = existing.split(',').map((p) => p.trim().toLowerCase());
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createCorsPolicy = createCorsPolicy;
|
|
4
|
+
exports.registerCorsPolicy = registerCorsPolicy;
|
|
5
|
+
exports.refuseForeignWsUpgrade = refuseForeignWsUpgrade;
|
|
6
|
+
const cors_policy_js_1 = require("./cors-policy.js");
|
|
2
7
|
/** Distinct refused origins remembered for log de-duplication. */
|
|
3
8
|
const MAX_LOGGED_DENIALS = 256;
|
|
4
9
|
function headerValue(value) {
|
|
@@ -13,8 +18,8 @@ function varyText(value) {
|
|
|
13
18
|
return value.filter((v) => typeof v === 'string').join(', ');
|
|
14
19
|
return undefined;
|
|
15
20
|
}
|
|
16
|
-
|
|
17
|
-
const builtIns = builtInOrigins();
|
|
21
|
+
function createCorsPolicy(deps) {
|
|
22
|
+
const builtIns = (0, cors_policy_js_1.builtInOrigins)();
|
|
18
23
|
let lastConfigured = Symbol('unset');
|
|
19
24
|
let listed = new Set(builtIns);
|
|
20
25
|
const loggedDenials = new Set();
|
|
@@ -23,7 +28,7 @@ export function createCorsPolicy(deps) {
|
|
|
23
28
|
if (configured === lastConfigured)
|
|
24
29
|
return listed;
|
|
25
30
|
lastConfigured = configured;
|
|
26
|
-
const parsed = parseConfiguredOrigins(configured);
|
|
31
|
+
const parsed = (0, cors_policy_js_1.parseConfiguredOrigins)(configured);
|
|
27
32
|
if (parsed.rejected.length > 0) {
|
|
28
33
|
deps.logger.warn('cors: ignored configured origins that are not http(s) origins', {
|
|
29
34
|
meta: { rejected: parsed.rejected, setting: 'server.corsOrigins' },
|
|
@@ -34,7 +39,7 @@ export function createCorsPolicy(deps) {
|
|
|
34
39
|
};
|
|
35
40
|
return {
|
|
36
41
|
judge(headers) {
|
|
37
|
-
const verdict = judgeCorsOrigin({
|
|
42
|
+
const verdict = (0, cors_policy_js_1.judgeCorsOrigin)({
|
|
38
43
|
origin: headerValue(headers.origin),
|
|
39
44
|
host: headerValue(headers.host),
|
|
40
45
|
forwardedHost: headerValue(headers['x-forwarded-host']),
|
|
@@ -59,17 +64,17 @@ export function createCorsPolicy(deps) {
|
|
|
59
64
|
* answered here with 204 (as before D707) — with grants only for an allowed
|
|
60
65
|
* origin, so a refused preflight fails in the browser.
|
|
61
66
|
*/
|
|
62
|
-
|
|
67
|
+
function registerCorsPolicy(fastify, policy) {
|
|
63
68
|
fastify.addHook('onRequest', async (request, reply) => {
|
|
64
69
|
// The answer depends on Origin for every route, so every response says so
|
|
65
70
|
// — a cached same-origin answer must never be replayed to another origin.
|
|
66
|
-
const vary = appendVary(varyText(reply.getHeader('vary')), 'Origin');
|
|
71
|
+
const vary = (0, cors_policy_js_1.appendVary)(varyText(reply.getHeader('vary')), 'Origin');
|
|
67
72
|
reply.header('vary', vary);
|
|
68
73
|
reply.raw.setHeader('vary', vary);
|
|
69
74
|
const verdict = policy.judge(request.headers);
|
|
70
75
|
const isPreflight = request.method === 'OPTIONS' && request.headers['access-control-request-method'] !== undefined;
|
|
71
76
|
if (verdict.kind === 'allowed') {
|
|
72
|
-
const headers = corsResponseHeaders(verdict.origin, isPreflight
|
|
77
|
+
const headers = (0, cors_policy_js_1.corsResponseHeaders)(verdict.origin, isPreflight
|
|
73
78
|
? {
|
|
74
79
|
requestPrivateNetwork: headerValue(request.headers['access-control-request-private-network']) === 'true',
|
|
75
80
|
}
|
|
@@ -94,7 +99,7 @@ export function registerCorsPolicy(fastify, policy) {
|
|
|
94
99
|
* Node client, an agent) passes — Origin is the browser's signal, and auth is
|
|
95
100
|
* the bearer in `connectionParams` either way.
|
|
96
101
|
*/
|
|
97
|
-
|
|
102
|
+
function refuseForeignWsUpgrade(policy, headers, socket) {
|
|
98
103
|
const verdict = policy.judge(headers);
|
|
99
104
|
if (verdict.kind !== 'denied')
|
|
100
105
|
return false;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DeployStageRegistry = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
2
5
|
/**
|
|
3
6
|
* In-memory, single-use, TTL-bounded store of staged addon tarballs the hub
|
|
4
7
|
* serves to agents over HTTP. One entry per deploy target; the token is
|
|
5
8
|
* consumed on the first successful read so a bundle URL is never reusable.
|
|
6
9
|
*/
|
|
7
|
-
|
|
10
|
+
class DeployStageRegistry {
|
|
8
11
|
entries = new Map();
|
|
9
12
|
ttlMs;
|
|
10
13
|
now;
|
|
@@ -15,9 +18,9 @@ export class DeployStageRegistry {
|
|
|
15
18
|
stage(buffer) {
|
|
16
19
|
// Reclaim abandoned (never-consumed) entries before inserting a new one.
|
|
17
20
|
this.sweepExpired();
|
|
18
|
-
const id = randomUUID();
|
|
19
|
-
const token = randomUUID();
|
|
20
|
-
const sha256 = createHash('sha256').update(buffer).digest('hex');
|
|
21
|
+
const id = (0, node_crypto_1.randomUUID)();
|
|
22
|
+
const token = (0, node_crypto_1.randomUUID)();
|
|
23
|
+
const sha256 = (0, node_crypto_1.createHash)('sha256').update(buffer).digest('hex');
|
|
21
24
|
this.entries.set(id, { token, buffer, sha256, expiresAt: this.now() + this.ttlMs });
|
|
22
25
|
return { id, token, sha256, bytes: buffer.length };
|
|
23
26
|
}
|
|
@@ -32,7 +35,7 @@ export class DeployStageRegistry {
|
|
|
32
35
|
// Constant-time comparison: different byte lengths → not equal (no timing oracle).
|
|
33
36
|
const entryBuf = Buffer.from(entry.token);
|
|
34
37
|
const tokenBuf = Buffer.from(token);
|
|
35
|
-
if (entryBuf.length !== tokenBuf.length || !timingSafeEqual(entryBuf, tokenBuf))
|
|
38
|
+
if (entryBuf.length !== tokenBuf.length || !(0, node_crypto_1.timingSafeEqual)(entryBuf, tokenBuf))
|
|
36
39
|
return null;
|
|
37
40
|
this.entries.delete(id); // single-use
|
|
38
41
|
return entry.buffer;
|
|
@@ -54,3 +57,4 @@ export class DeployStageRegistry {
|
|
|
54
57
|
this.entries.delete(id);
|
|
55
58
|
}
|
|
56
59
|
}
|
|
60
|
+
exports.DeployStageRegistry = DeployStageRegistry;
|