@camstack/server 0.2.2 → 1.0.1
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/{src/agent-status-page.ts → dist/agent-status-page.js} +30 -45
- package/dist/api/addon-upload.js +441 -0
- package/dist/api/addons-custom.router.js +91 -0
- package/dist/api/auth-whoami.js +55 -0
- package/dist/api/bridge-addons.router.js +109 -0
- package/dist/api/capabilities.router.js +229 -0
- package/dist/api/core/addon-settings.router.js +117 -0
- package/dist/api/core/agents.router.js +73 -0
- package/dist/api/core/auth.router.js +286 -0
- package/dist/api/core/bulk-update-coordinator.js +229 -0
- package/dist/api/core/cap-providers.js +1124 -0
- package/dist/api/core/capabilities.router.js +138 -0
- package/dist/api/core/collection-preference.js +17 -0
- package/dist/api/core/event-bus-proxy.router.js +45 -0
- package/dist/api/core/hwaccel.router.js +91 -0
- package/dist/api/core/live-events.router.js +61 -0
- package/dist/api/core/logs.router.js +172 -0
- package/dist/api/core/notifications.router.js +67 -0
- package/dist/api/core/repl.router.js +35 -0
- package/dist/api/core/settings-backend.router.js +121 -0
- package/dist/api/core/stream-probe.router.js +58 -0
- package/dist/api/core/system-events.router.js +100 -0
- package/dist/api/health/health.routes.js +68 -0
- package/{src/api/oauth2/consent-page.ts → dist/api/oauth2/consent-page.js} +11 -20
- package/dist/api/oauth2/oauth2-routes.js +219 -0
- package/dist/api/trpc/cap-mount-helpers.js +194 -0
- package/dist/api/trpc/cap-route-error-formatter.js +133 -0
- package/dist/api/trpc/client-ip.js +147 -0
- package/dist/api/trpc/core-cap-bridge.js +115 -0
- package/dist/api/trpc/generated-cap-mounts.js +388 -0
- package/dist/api/trpc/generated-cap-routers.js +7635 -0
- package/dist/api/trpc/scope-access.js +93 -0
- package/dist/api/trpc/trpc.context.js +184 -0
- package/dist/api/trpc/trpc.middleware.js +139 -0
- package/dist/api/trpc/trpc.router.js +188 -0
- package/dist/auth/session-cookie.js +47 -0
- package/dist/boot/boot-config.js +241 -0
- package/dist/boot/integration-id-backfill.js +76 -0
- package/dist/boot/post-boot.service.js +85 -0
- package/dist/core/addon/addon-call-gateway.js +99 -0
- package/dist/core/addon/addon-package.service.js +1560 -0
- package/dist/core/addon/addon-registry.service.js +2739 -0
- package/{src/core/addon/addon-row-manifest.ts → dist/core/addon/addon-row-manifest.js} +5 -5
- package/dist/core/addon/addon-search.service.js +62 -0
- package/dist/core/addon/addon-settings-provider.js +102 -0
- package/dist/core/addon/addon.tokens.js +5 -0
- package/dist/core/addon-bridge/addon-bridge.service.js +145 -0
- package/dist/core/addon-pages/addon-pages.service.js +107 -0
- package/dist/core/addon-widgets/addon-widgets.service.js +120 -0
- package/dist/core/agent/agent-registry.service.js +477 -0
- package/dist/core/auth/auth.service.js +10 -0
- package/dist/core/capability/capability.service.js +58 -0
- package/dist/core/config/config.schema.js +7 -0
- package/dist/core/config/config.service.js +10 -0
- package/dist/core/events/event-bus.service.js +83 -0
- package/dist/core/feature/feature.service.js +10 -0
- package/dist/core/lifecycle/lifecycle-state-machine.js +6 -0
- package/dist/core/logging/log-ring-buffer.js +6 -0
- package/dist/core/logging/logging.service.js +130 -0
- package/dist/core/logging/scoped-logger.js +6 -0
- package/dist/core/moleculer/cap-call-fn.js +50 -0
- package/dist/core/moleculer/cap-route-authority.js +122 -0
- package/dist/core/moleculer/moleculer.service.js +898 -0
- package/dist/core/network/network-quality.service.js +7 -0
- package/dist/core/notification/notification-wrapper.service.js +33 -0
- package/dist/core/notification/toast-wrapper.service.js +25 -0
- package/dist/core/provider/provider.tokens.js +4 -0
- package/dist/core/repl/repl-engine.service.js +140 -0
- package/dist/core/storage/fs-storage-backend.js +6 -0
- package/dist/core/storage/storage-location-manager.js +6 -0
- package/dist/core/storage/storage.service.js +7 -0
- package/dist/core/streaming/stream-probe.service.js +209 -0
- package/dist/core/topology/topology-emitter.service.js +106 -0
- package/dist/launcher.js +325 -0
- package/dist/main.js +1098 -0
- package/dist/manual-boot.js +227 -0
- package/package.json +5 -1
- package/src/__tests__/addon-install-e2e.test.ts +0 -74
- package/src/__tests__/addon-pages-e2e.test.ts +0 -200
- package/src/__tests__/addon-route-session.test.ts +0 -17
- package/src/__tests__/addon-settings-router.spec.ts +0 -67
- package/src/__tests__/addon-upload.spec.ts +0 -475
- package/src/__tests__/agent-registry.spec.ts +0 -179
- package/src/__tests__/agent-status-page.spec.ts +0 -82
- package/src/__tests__/auth-session-cookie.test.ts +0 -48
- package/src/__tests__/bulk-update-coordinator.spec.ts +0 -303
- package/src/__tests__/cap-ownership-authority.spec.ts +0 -431
- package/src/__tests__/cap-providers/cap-providers-location-import.spec.ts +0 -206
- package/src/__tests__/cap-providers/cap-usage-graph.spec.ts +0 -37
- package/src/__tests__/cap-providers/compute-topology-categories.spec.ts +0 -110
- package/src/__tests__/cap-providers/integrations-delete-cascade.spec.ts +0 -292
- package/src/__tests__/cap-providers-bulk-update.spec.ts +0 -408
- package/src/__tests__/cap-route-adapter.spec.ts +0 -302
- package/src/__tests__/cap-routers/_meta.spec.ts +0 -199
- package/src/__tests__/cap-routers/addon-settings.router.spec.ts +0 -115
- package/src/__tests__/cap-routers/broker-routing.router.spec.ts +0 -177
- package/src/__tests__/cap-routers/cap-route-error-formatter.spec.ts +0 -125
- package/src/__tests__/cap-routers/capabilities-node.spec.ts +0 -68
- package/src/__tests__/cap-routers/device-link-overlay.spec.ts +0 -137
- package/src/__tests__/cap-routers/device-manager-aggregate.router.spec.ts +0 -194
- package/src/__tests__/cap-routers/harness.ts +0 -163
- package/src/__tests__/cap-routers/metrics-provider.router.spec.ts +0 -133
- package/src/__tests__/cap-routers/null-provider-guard.spec.ts +0 -64
- package/src/__tests__/cap-routers/pipeline-executor.router.spec.ts +0 -159
- package/src/__tests__/cap-routers/settings-store.router.spec.ts +0 -291
- package/src/__tests__/capability-e2e.test.ts +0 -384
- package/src/__tests__/cli-e2e.test.ts +0 -150
- package/src/__tests__/core-cap-bridge.spec.ts +0 -91
- package/src/__tests__/dev-bootstrap-shm-ring.spec.ts +0 -40
- package/src/__tests__/device-settings-contribution-dispatch.spec.ts +0 -280
- package/src/__tests__/embedded-deps-e2e.test.ts +0 -125
- package/src/__tests__/event-bus-proxy-router.spec.ts +0 -75
- package/src/__tests__/fixtures/mock-analysis-addon-a.ts +0 -37
- package/src/__tests__/fixtures/mock-analysis-addon-b.ts +0 -37
- package/src/__tests__/fixtures/mock-log-addon.ts +0 -37
- package/src/__tests__/fixtures/mock-storage-addon.ts +0 -40
- package/src/__tests__/framework-allowlist.spec.ts +0 -96
- package/src/__tests__/framework-installer-defer-restart.spec.ts +0 -165
- package/src/__tests__/https-e2e.test.ts +0 -124
- package/src/__tests__/lifecycle-e2e.test.ts +0 -189
- package/src/__tests__/live-events-subscription.spec.ts +0 -149
- package/src/__tests__/moleculer/uds-readiness.spec.ts +0 -150
- package/src/__tests__/moleculer/uds-topology.spec.ts +0 -418
- package/src/__tests__/moleculer/uds-unowned-call.spec.ts +0 -383
- package/src/__tests__/moleculer-register-node-idempotency.spec.ts +0 -273
- package/src/__tests__/native-cap-route.spec.ts +0 -427
- package/src/__tests__/oauth2-account-linking.spec.ts +0 -867
- package/src/__tests__/post-boot-restart.spec.ts +0 -161
- package/src/__tests__/singleton-contention.test.ts +0 -499
- package/src/__tests__/streaming-diagnostic.test.ts +0 -615
- package/src/__tests__/streaming-scale.test.ts +0 -314
- package/src/__tests__/uds-addon-call-wiring.spec.ts +0 -242
- package/src/__tests__/uds-log-ingest.spec.ts +0 -183
- package/src/api/__tests__/addons-custom.spec.ts +0 -148
- package/src/api/__tests__/capabilities.router.test.ts +0 -56
- package/src/api/addon-upload.ts +0 -529
- package/src/api/addons-custom.router.ts +0 -101
- package/src/api/auth-whoami.ts +0 -101
- package/src/api/bridge-addons.router.ts +0 -122
- package/src/api/capabilities.router.ts +0 -265
- package/src/api/core/__tests__/auth-router-totp.spec.ts +0 -297
- package/src/api/core/__tests__/integration-markers.spec.ts +0 -10
- package/src/api/core/addon-settings.router.ts +0 -127
- package/src/api/core/agents.router.ts +0 -86
- package/src/api/core/auth.router.ts +0 -322
- package/src/api/core/bulk-update-coordinator.ts +0 -305
- package/src/api/core/cap-providers.ts +0 -1339
- package/src/api/core/capabilities.router.ts +0 -149
- package/src/api/core/collection-preference.ts +0 -40
- package/src/api/core/event-bus-proxy.router.ts +0 -45
- package/src/api/core/hwaccel.router.ts +0 -108
- package/src/api/core/live-events.router.ts +0 -67
- package/src/api/core/logs.router.ts +0 -195
- package/src/api/core/notifications.router.ts +0 -66
- package/src/api/core/repl.router.ts +0 -39
- package/src/api/core/settings-backend.router.ts +0 -140
- package/src/api/core/stream-probe.router.ts +0 -57
- package/src/api/core/system-events.router.ts +0 -125
- package/src/api/health/health.routes.ts +0 -117
- package/src/api/oauth2/__tests__/oauth2-routes.spec.ts +0 -62
- package/src/api/oauth2/oauth2-routes.ts +0 -281
- package/src/api/trpc/__tests__/client-ip.spec.ts +0 -146
- package/src/api/trpc/__tests__/scope-access-device.spec.ts +0 -268
- package/src/api/trpc/__tests__/scope-access.spec.ts +0 -102
- package/src/api/trpc/__tests__/webrtc-session-ua-enrich.spec.ts +0 -136
- package/src/api/trpc/cap-mount-helpers.ts +0 -245
- package/src/api/trpc/cap-route-error-formatter.ts +0 -171
- package/src/api/trpc/client-ip.ts +0 -147
- package/src/api/trpc/core-cap-bridge.ts +0 -154
- package/src/api/trpc/generated-cap-mounts.ts +0 -1240
- package/src/api/trpc/generated-cap-routers.ts +0 -11523
- package/src/api/trpc/scope-access.ts +0 -110
- package/src/api/trpc/trpc.context.ts +0 -258
- package/src/api/trpc/trpc.middleware.ts +0 -146
- package/src/api/trpc/trpc.router.ts +0 -389
- package/src/auth/session-cookie.ts +0 -54
- package/src/boot/__tests__/integration-id-backfill.spec.ts +0 -131
- package/src/boot/boot-config.ts +0 -259
- package/src/boot/integration-id-backfill.ts +0 -109
- package/src/boot/post-boot.service.ts +0 -105
- package/src/core/addon/__tests__/addon-registry-capability.test.ts +0 -62
- package/src/core/addon/__tests__/addon-row-manifest.spec.ts +0 -62
- package/src/core/addon/addon-call-gateway.ts +0 -171
- package/src/core/addon/addon-package.service.ts +0 -1787
- package/src/core/addon/addon-registry.service.ts +0 -3130
- package/src/core/addon/addon-search.service.ts +0 -91
- package/src/core/addon/addon-settings-provider.ts +0 -220
- package/src/core/addon/addon.tokens.ts +0 -2
- package/src/core/addon-bridge/addon-bridge.service.ts +0 -130
- package/src/core/addon-pages/addon-pages.service.spec.ts +0 -117
- package/src/core/addon-pages/addon-pages.service.ts +0 -82
- package/src/core/addon-widgets/addon-widgets.service.ts +0 -95
- package/src/core/agent/agent-registry.service.ts +0 -529
- package/src/core/auth/auth.service.spec.ts +0 -86
- package/src/core/auth/auth.service.ts +0 -8
- package/src/core/capability/capability.service.ts +0 -66
- package/src/core/config/config.schema.ts +0 -3
- package/src/core/config/config.service.spec.ts +0 -175
- package/src/core/config/config.service.ts +0 -7
- package/src/core/events/event-bus.service.spec.ts +0 -235
- package/src/core/events/event-bus.service.ts +0 -89
- package/src/core/feature/feature.service.spec.ts +0 -99
- package/src/core/feature/feature.service.ts +0 -8
- package/src/core/lifecycle/lifecycle-state-machine.spec.ts +0 -166
- package/src/core/lifecycle/lifecycle-state-machine.ts +0 -3
- package/src/core/logging/log-ring-buffer.ts +0 -3
- package/src/core/logging/logging.service.spec.ts +0 -287
- package/src/core/logging/logging.service.ts +0 -143
- package/src/core/logging/scoped-logger.ts +0 -3
- package/src/core/moleculer/cap-call-fn.spec.ts +0 -173
- package/src/core/moleculer/cap-call-fn.ts +0 -107
- package/src/core/moleculer/cap-route-authority.ts +0 -194
- package/src/core/moleculer/moleculer.service.ts +0 -1072
- package/src/core/network/network-quality.service.spec.ts +0 -53
- package/src/core/network/network-quality.service.ts +0 -5
- package/src/core/notification/notification-wrapper.service.ts +0 -34
- package/src/core/notification/toast-wrapper.service.ts +0 -27
- package/src/core/provider/provider.tokens.ts +0 -1
- package/src/core/repl/repl-engine.service.spec.ts +0 -444
- package/src/core/repl/repl-engine.service.ts +0 -155
- package/src/core/storage/fs-storage-backend.spec.ts +0 -70
- package/src/core/storage/fs-storage-backend.ts +0 -3
- package/src/core/storage/storage-location-manager.spec.ts +0 -130
- package/src/core/storage/storage-location-manager.ts +0 -3
- package/src/core/storage/storage.service.spec.ts +0 -73
- package/src/core/storage/storage.service.ts +0 -3
- package/src/core/streaming/stream-probe.service.ts +0 -221
- package/src/core/topology/topology-emitter.service.ts +0 -105
- package/src/launcher.ts +0 -314
- package/src/main.ts +0 -1245
- package/src/manual-boot.ts +0 -301
- package/tsconfig.build.json +0 -8
- package/tsconfig.json +0 -33
- package/vitest.config.ts +0 -26
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Client-IP extraction + LAN/remote classification for the tRPC layer.
|
|
3
|
-
*
|
|
4
|
-
* Used by the `webrtcSession.createSession` override to decide whether a
|
|
5
|
-
* live-view session should force TURN-relay-only ICE: a viewer behind
|
|
6
|
-
* CGNAT/4G (a non-LAN source IP) only ever offers a relay candidate, so
|
|
7
|
-
* the broker must offer a genuinely relay-only SDP (the patched werift
|
|
8
|
-
* emits one under `iceTransportPolicy:'relay'`) to get a clean
|
|
9
|
-
* relay↔relay media path. LAN viewers (private/loopback source IP) keep
|
|
10
|
-
* the low-latency direct host/srflx path. The SERVER computes this — the
|
|
11
|
-
* client never sends a relay-only flag.
|
|
12
|
-
*/
|
|
13
|
-
import type { FastifyRequest } from 'fastify'
|
|
14
|
-
import type { IncomingMessage } from 'node:http'
|
|
15
|
-
|
|
16
|
-
export type ClientRequest = FastifyRequest | IncomingMessage
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Best-effort extraction of the originating client IP from a tRPC
|
|
20
|
-
* request. Order of preference:
|
|
21
|
-
* 1. `X-Forwarded-For` first hop (when behind a reverse proxy — the
|
|
22
|
-
* hub is typically fronted by Caddy/nginx/Cloudflare for remote
|
|
23
|
-
* access, so the socket peer is the proxy, not the viewer).
|
|
24
|
-
* 2. Fastify's `req.ip` (already proxy-aware when `trustProxy` is on).
|
|
25
|
-
* 3. The raw socket remote address.
|
|
26
|
-
*
|
|
27
|
-
* Returns `null` when no address can be determined (e.g. mesh-originated
|
|
28
|
-
* calls that carry no HTTP request) — the caller treats `null` as "not
|
|
29
|
-
* remote" so the LAN/direct path stays the safe default.
|
|
30
|
-
*/
|
|
31
|
-
export function extractClientIp(req: ClientRequest | undefined): string | null {
|
|
32
|
-
if (!req) return null
|
|
33
|
-
|
|
34
|
-
// 1. X-Forwarded-For — comma-separated list, client is the FIRST entry.
|
|
35
|
-
const xff = req.headers['x-forwarded-for']
|
|
36
|
-
const xffValue = Array.isArray(xff) ? xff[0] : xff
|
|
37
|
-
if (typeof xffValue === 'string' && xffValue.length > 0) {
|
|
38
|
-
const first = xffValue.split(',')[0]?.trim()
|
|
39
|
-
if (first) return normalizeIp(first)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// 2. Fastify's parsed `req.ip` (honours `trustProxy` config).
|
|
43
|
-
if ('ip' in req && typeof req.ip === 'string' && req.ip.length > 0) {
|
|
44
|
-
return normalizeIp(req.ip)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// 3. Raw socket peer (WS / IncomingMessage path).
|
|
48
|
-
const remote = req.socket?.remoteAddress
|
|
49
|
-
if (typeof remote === 'string' && remote.length > 0) {
|
|
50
|
-
return normalizeIp(remote)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return null
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Best-effort read of the originating client's `User-Agent` header. Both
|
|
58
|
-
* request shapes in the union (`FastifyRequest` and the raw WS-transport
|
|
59
|
-
* `IncomingMessage`) expose `.headers['user-agent']`. Returns `null` when
|
|
60
|
-
* absent (mesh-originated calls carry no HTTP request, and some clients
|
|
61
|
-
* omit the header). Used by the `webrtc-session` mount to tag a browser
|
|
62
|
-
* subscriber's broker attribution with the viewer's UA — the SERVER reads
|
|
63
|
-
* it from the request context; any client-supplied value is ignored.
|
|
64
|
-
*/
|
|
65
|
-
export function extractUserAgent(req: ClientRequest | undefined): string | null {
|
|
66
|
-
if (!req) return null
|
|
67
|
-
const ua = req.headers['user-agent']
|
|
68
|
-
const value = Array.isArray(ua) ? ua[0] : ua
|
|
69
|
-
if (typeof value === 'string' && value.length > 0) return value
|
|
70
|
-
return null
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Strip an IPv4-mapped IPv6 prefix (`::ffff:192.168.1.5` → `192.168.1.5`)
|
|
75
|
-
* and any zone id (`fe80::1%en0` → `fe80::1`) so the range checks below
|
|
76
|
-
* see a clean address.
|
|
77
|
-
*/
|
|
78
|
-
function normalizeIp(ip: string): string {
|
|
79
|
-
let out = ip.trim()
|
|
80
|
-
if (out.startsWith('::ffff:')) out = out.slice('::ffff:'.length)
|
|
81
|
-
const pct = out.indexOf('%')
|
|
82
|
-
if (pct !== -1) out = out.slice(0, pct)
|
|
83
|
-
return out
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* True when the address is NOT in a private / loopback / link-local /
|
|
88
|
-
* Tailscale range — i.e. an internet (remote) viewer. Covers IPv4
|
|
89
|
-
* (10/8, 172.16/12, 192.168/16, 127/8, 100.64/10 Tailscale CGNAT) and
|
|
90
|
-
* IPv6 (::1, fc00::/7 unique-local, fe80::/10 link-local — fd7a::/16
|
|
91
|
-
* Tailscale ULA is a subset of fc00::/7 and is therefore already
|
|
92
|
-
* covered).
|
|
93
|
-
*
|
|
94
|
-
* Tailscale clients (the 100.64.0.0/10 CGNAT overlay or the fd7a::/16
|
|
95
|
-
* ULA overlay) are deliberately classified LOCAL: over the Tailscale
|
|
96
|
-
* mesh BOTH peers sit on the 100.x / fd7a:: overlay and are mutually
|
|
97
|
-
* reachable, so the broker offers ALL candidates (host/srflx/relay)
|
|
98
|
-
* — including the hub's Tailscale host candidate — and a direct
|
|
99
|
-
* host↔host pair wins ICE with native (non-re-encoded) media. Forcing
|
|
100
|
-
* relay-only for Tailscale would push them onto the broken relay path.
|
|
101
|
-
*
|
|
102
|
-
* Unparseable or null addresses return `false` (treated as LAN — the
|
|
103
|
-
* safe default that preserves the existing direct path).
|
|
104
|
-
*/
|
|
105
|
-
export function isRemoteClientIp(ip: string | null): boolean {
|
|
106
|
-
if (!ip) return false
|
|
107
|
-
if (ip.includes(':')) return !isPrivateIpv6(ip)
|
|
108
|
-
if (isIpv4(ip)) return !isPrivateIpv4(ip)
|
|
109
|
-
// Not a recognisable IP literal — be conservative, treat as LAN.
|
|
110
|
-
return false
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function isIpv4(ip: string): boolean {
|
|
114
|
-
const parts = ip.split('.')
|
|
115
|
-
if (parts.length !== 4) return false
|
|
116
|
-
return parts.every((p) => {
|
|
117
|
-
if (!/^\d{1,3}$/.test(p)) return false
|
|
118
|
-
const n = Number.parseInt(p, 10)
|
|
119
|
-
return n >= 0 && n <= 255
|
|
120
|
-
})
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function isPrivateIpv4(ip: string): boolean {
|
|
124
|
-
const parts = ip.split('.').map((p) => Number.parseInt(p, 10))
|
|
125
|
-
const [a, b] = parts
|
|
126
|
-
if (a === undefined || b === undefined) return false
|
|
127
|
-
if (a === 10) return true // 10.0.0.0/8
|
|
128
|
-
if (a === 127) return true // 127.0.0.0/8 loopback
|
|
129
|
-
if (a === 172 && b >= 16 && b <= 31) return true // 172.16.0.0/12
|
|
130
|
-
if (a === 192 && b === 168) return true // 192.168.0.0/16
|
|
131
|
-
if (a === 169 && b === 254) return true // 169.254.0.0/16 link-local
|
|
132
|
-
// 100.64.0.0/10 — Tailscale CGNAT overlay (100.64.0.0 – 100.127.255.255).
|
|
133
|
-
// Both Tailscale peers are mutually reachable on this overlay, so treat
|
|
134
|
-
// it as local (direct host↔host pair, no relay).
|
|
135
|
-
if (a === 100 && b >= 64 && b <= 127) return true
|
|
136
|
-
return false
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function isPrivateIpv6(ip: string): boolean {
|
|
140
|
-
const lower = ip.toLowerCase()
|
|
141
|
-
if (lower === '::1') return true // loopback
|
|
142
|
-
if (lower === '::') return true // unspecified
|
|
143
|
-
if (lower.startsWith('fe80')) return true // fe80::/10 link-local
|
|
144
|
-
// fc00::/7 unique-local — covers fc.. and fd..
|
|
145
|
-
if (lower.startsWith('fc') || lower.startsWith('fd')) return true
|
|
146
|
-
return false
|
|
147
|
-
}
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Core-capability mesh bridge.
|
|
3
|
-
*
|
|
4
|
-
* The hub's CORE routers — hand-written single-impl routers plus the
|
|
5
|
-
* handful of service-backed cap routers — are mounted only as the hub's
|
|
6
|
-
* tRPC `appRouter`. No addon registers a provider for them, so
|
|
7
|
-
* `addon-service-factory` never publishes a Moleculer service exposing
|
|
8
|
-
* their actions. A forked addon calling `ctx.api.<coreCap>.<method>`
|
|
9
|
-
* therefore falls through `localProviderLink` into `brokerTransportLink`,
|
|
10
|
-
* whose load-balanced discovery wait has no deadline — the call hangs
|
|
11
|
-
* forever (this was the `export-alexa` `redetectHubUrl` hang).
|
|
12
|
-
*
|
|
13
|
-
* `buildCoreCapService` walks the appRouter, picks the core namespaces,
|
|
14
|
-
* and wraps each query/mutation as a `$core-caps.<kebab-cap>.<method>`
|
|
15
|
-
* Moleculer action invoked through a trusted-mesh tRPC caller. With the
|
|
16
|
-
* service mounted on the hub node, `brokerTransportLink` resolves and
|
|
17
|
-
* routes the call exactly like any addon-provided cap.
|
|
18
|
-
*/
|
|
19
|
-
import type { ServiceSchema } from 'moleculer'
|
|
20
|
-
import { createCoreCapService, type CoreCapAction } from '@camstack/kernel'
|
|
21
|
-
import { createCallerFactory } from './trpc.middleware.js'
|
|
22
|
-
import { createMeshTrpcContext } from './trpc.context.js'
|
|
23
|
-
import type { AppRouter } from './trpc.router.js'
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* appRouter namespaces that back the CORE API surface and must be
|
|
27
|
-
* reachable cross-process. This is the core-router list from
|
|
28
|
-
* `trpc.router.ts` (`buildCapabilityRouters`) minus the deliberate
|
|
29
|
-
* exclusions below.
|
|
30
|
-
*
|
|
31
|
-
* Excluded on purpose:
|
|
32
|
-
* - `auth` — issuing service / scoped tokens over the trusted mesh
|
|
33
|
-
* would let any forked addon mint admin credentials. Addons must
|
|
34
|
-
* not perform authentication operations; this stays hub-local.
|
|
35
|
-
* - `live`, `systemEvents` — listed in `NEVER_BRIDGED_CAPS`
|
|
36
|
-
* (`trpc-links.ts`). They are hub-only push streams, not
|
|
37
|
-
* request/reply, and the worker side fast-fails on them by design.
|
|
38
|
-
*/
|
|
39
|
-
const CORE_NAMESPACES: ReadonlySet<string> = new Set<string>([
|
|
40
|
-
// Service-backed cap routers (no addon provider).
|
|
41
|
-
'system',
|
|
42
|
-
'toast',
|
|
43
|
-
'integrations',
|
|
44
|
-
'nodes',
|
|
45
|
-
'addons',
|
|
46
|
-
// Hand-written single-impl core routers.
|
|
47
|
-
'capabilities',
|
|
48
|
-
'notifications',
|
|
49
|
-
'logs',
|
|
50
|
-
'hwaccel',
|
|
51
|
-
'streamProbe',
|
|
52
|
-
'settingsBackend',
|
|
53
|
-
'eventBusProxy',
|
|
54
|
-
'repl',
|
|
55
|
-
'addonSettingsRaw',
|
|
56
|
-
])
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* camelCase → kebab-case. Mirrors `toKebab` in `trpc-links.ts` so the
|
|
60
|
-
* action name matches what `brokerTransportLink` derives from `op.path`.
|
|
61
|
-
*/
|
|
62
|
-
function toKebab(name: string): string {
|
|
63
|
-
return name.replace(/[A-Z]/g, (m, i: number) => (i > 0 ? '-' : '') + m.toLowerCase())
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** A tRPC procedure node — distinguished from a router by `_def.procedure`. */
|
|
67
|
-
interface ProcedureNode {
|
|
68
|
-
readonly _def: { readonly procedure?: unknown; readonly type?: unknown }
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function isProcedureNode(value: unknown): value is ProcedureNode {
|
|
72
|
-
// A built tRPC procedure is a callable object — `_def.procedures`
|
|
73
|
-
// stores the procedure function directly, not a wrapper object.
|
|
74
|
-
if (value === null || (typeof value !== 'object' && typeof value !== 'function')) return false
|
|
75
|
-
const def: unknown = (value as { _def?: unknown })._def
|
|
76
|
-
return (
|
|
77
|
-
def !== null && typeof def === 'object' && (def as { procedure?: unknown }).procedure === true
|
|
78
|
-
)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
interface DiscoveredProcedure {
|
|
82
|
-
readonly path: string
|
|
83
|
-
readonly type: string
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Recursively flatten a tRPC router record to dotted procedure paths.
|
|
88
|
-
* Robust to both layouts tRPC v11 may use for `_def.procedures`: a
|
|
89
|
-
* nested record of sub-routers, or a flat record already keyed by
|
|
90
|
-
* dotted path (a flat key simply yields its key verbatim).
|
|
91
|
-
*/
|
|
92
|
-
function collectProcedures(
|
|
93
|
-
record: Readonly<Record<string, unknown>>,
|
|
94
|
-
prefix: string,
|
|
95
|
-
out: DiscoveredProcedure[],
|
|
96
|
-
): void {
|
|
97
|
-
for (const [key, value] of Object.entries(record)) {
|
|
98
|
-
const path = prefix.length > 0 ? `${prefix}.${key}` : key
|
|
99
|
-
if (isProcedureNode(value)) {
|
|
100
|
-
const type = value._def.type
|
|
101
|
-
out.push({ path, type: typeof type === 'string' ? type : 'query' })
|
|
102
|
-
} else if (value !== null && typeof value === 'object') {
|
|
103
|
-
collectProcedures(value as Readonly<Record<string, unknown>>, path, out)
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
type ProcedureInvoker = (input: unknown) => Promise<unknown>
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Navigate the decorated caller record (a recursive proxy) to the
|
|
112
|
-
* procedure function at `path`. Every node on the proxy is callable,
|
|
113
|
-
* so navigation uses `Reflect.get` across both objects and functions.
|
|
114
|
-
*/
|
|
115
|
-
function resolveInvoker(caller: unknown, path: string): ProcedureInvoker | null {
|
|
116
|
-
let node: unknown = caller
|
|
117
|
-
for (const segment of path.split('.')) {
|
|
118
|
-
if (node === null || (typeof node !== 'object' && typeof node !== 'function')) return null
|
|
119
|
-
// Documented boundary: `node` is an object or function past the
|
|
120
|
-
// guard above; `Reflect.get` is typed for `object` targets.
|
|
121
|
-
node = Reflect.get(node as object, segment)
|
|
122
|
-
}
|
|
123
|
-
return typeof node === 'function' ? (node as ProcedureInvoker) : null
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Build the `$core-caps` Moleculer service schema from the hub
|
|
128
|
-
* appRouter. Query + mutation procedures in {@link CORE_NAMESPACES} are
|
|
129
|
-
* exposed; subscriptions are skipped (the broker transport is
|
|
130
|
-
* request/reply only).
|
|
131
|
-
*/
|
|
132
|
-
export function buildCoreCapService(appRouter: AppRouter): ServiceSchema {
|
|
133
|
-
const meshCaller: unknown = createCallerFactory(appRouter)(createMeshTrpcContext())
|
|
134
|
-
|
|
135
|
-
const discovered: DiscoveredProcedure[] = []
|
|
136
|
-
collectProcedures(appRouter._def.procedures, '', discovered)
|
|
137
|
-
|
|
138
|
-
const actions: CoreCapAction[] = []
|
|
139
|
-
for (const { path, type } of discovered) {
|
|
140
|
-
const dot = path.indexOf('.')
|
|
141
|
-
if (dot < 0) continue
|
|
142
|
-
const namespace = path.slice(0, dot)
|
|
143
|
-
if (!CORE_NAMESPACES.has(namespace)) continue
|
|
144
|
-
if (type === 'subscription') continue
|
|
145
|
-
|
|
146
|
-
const invoke = resolveInvoker(meshCaller, path)
|
|
147
|
-
if (invoke === null) continue
|
|
148
|
-
|
|
149
|
-
const method = path.slice(dot + 1)
|
|
150
|
-
actions.push({ actionName: `${toKebab(namespace)}.${method}`, invoke })
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return createCoreCapService({ actions })
|
|
154
|
-
}
|