@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,389 +0,0 @@
|
|
|
1
|
-
import { trpcRouter } from './trpc.middleware'
|
|
2
|
-
// All capability tRPC routers are auto-mounted via `mountAllCaps()` below.
|
|
3
|
-
// Only the OVERRIDES (service-backed providers, custom collection
|
|
4
|
-
// dispatch logic, hub-only remote-proxy caps) and the CORE (non-cap)
|
|
5
|
-
// routers are wired manually in this file. Tracking comment for the
|
|
6
|
-
// historical migration index (every `create<X>Router` that used to live
|
|
7
|
-
// here has been replaced by its codegen'd cap-router counterpart):
|
|
8
|
-
// auth → auth (kept manual for AuthService DI), users/system →
|
|
9
|
-
// `userMgmt`/`system` caps, settings → `system-config` builtin,
|
|
10
|
-
// devices → `deviceManager` cap, integrations → `integrations` cap,
|
|
11
|
-
// streaming → `streamingManagement` cap, events → `eventQuery` cap,
|
|
12
|
-
// logs → kept manual, live → kept manual, processes → `processMgmt`
|
|
13
|
-
// cap, agents → `nodes` cap, sessions → `session` cap, trackMedia /
|
|
14
|
-
// trackTrail → caps, network →
|
|
15
|
-
// `networkQuality` cap, addons → `addons` cap, bridgePipeline removed
|
|
16
|
-
// (legacy), detection → `detectionConfig` cap, capabilities → kept
|
|
17
|
-
// manual, update → addons cap, addonPages → cap, notification →
|
|
18
|
-
// `notifications` (kept manual for NotificationService DI),
|
|
19
|
-
// scopedToken → `userManagement`, toast → `toast` cap, inference →
|
|
20
|
-
// `pipelineExecutor` cap, pipeline → `pipelineConfig` cap,
|
|
21
|
-
// systemEvents → kept manual.
|
|
22
|
-
import type { CapabilityRegistry } from '@camstack/kernel'
|
|
23
|
-
import type { InferProvider, BrokerConsumerAttribution } from '@camstack/types'
|
|
24
|
-
import {
|
|
25
|
-
pipelineExecutorCapability,
|
|
26
|
-
pipelineRunnerCapability,
|
|
27
|
-
pipelineOrchestratorCapability,
|
|
28
|
-
audioAnalyzerCapability,
|
|
29
|
-
audioCodecCapability,
|
|
30
|
-
platformProbeCapability,
|
|
31
|
-
decoderCapability,
|
|
32
|
-
localNetworkCapability,
|
|
33
|
-
webrtcSessionCapability,
|
|
34
|
-
} from '@camstack/types'
|
|
35
|
-
import {
|
|
36
|
-
// The auto-mount covers ~75 caps. The handful re-imported below back
|
|
37
|
-
// the manual overrides that need service-backed providers or custom
|
|
38
|
-
// collection dispatch logic; the auto-mount entry is replaced by the
|
|
39
|
-
// override line via spread precedence.
|
|
40
|
-
createCapRouter_pipelineExecutor,
|
|
41
|
-
createCapRouter_pipelineRunner,
|
|
42
|
-
createCapRouter_pipelineOrchestrator,
|
|
43
|
-
createCapRouter_audioAnalyzer,
|
|
44
|
-
createCapRouter_audioCodec,
|
|
45
|
-
createCapRouter_decoder,
|
|
46
|
-
createCapRouter_platformProbe,
|
|
47
|
-
createCapRouter_localNetwork,
|
|
48
|
-
createCapRouter_snapshotProvider,
|
|
49
|
-
createCapRouter_networkQuality,
|
|
50
|
-
createCapRouter_system,
|
|
51
|
-
createCapRouter_toast,
|
|
52
|
-
createCapRouter_integrations,
|
|
53
|
-
createCapRouter_nodes,
|
|
54
|
-
createCapRouter_addons,
|
|
55
|
-
createCapRouter_webrtcSession,
|
|
56
|
-
} from './generated-cap-routers'
|
|
57
|
-
import { mountAllCaps } from './generated-cap-mounts.js'
|
|
58
|
-
import {
|
|
59
|
-
buildSystemProvider,
|
|
60
|
-
buildNetworkQualityProvider,
|
|
61
|
-
buildToastProvider,
|
|
62
|
-
buildNodesProvider,
|
|
63
|
-
buildIntegrationsProvider,
|
|
64
|
-
buildAddonsProvider,
|
|
65
|
-
} from '../core/cap-providers.js'
|
|
66
|
-
import { createAuthRouter } from '../core/auth.router.js'
|
|
67
|
-
import { createAddonSettingsRouter } from '../core/addon-settings.router.js'
|
|
68
|
-
import { createSettingsBackendRouter } from '../core/settings-backend.router.js'
|
|
69
|
-
import { createEventBusProxyRouter } from '../core/event-bus-proxy.router.js'
|
|
70
|
-
import { createReplRouter } from '../core/repl.router.js'
|
|
71
|
-
import { createNotificationsRouter } from '../core/notifications.router.js'
|
|
72
|
-
import { createLogsRouter } from '../core/logs.router.js'
|
|
73
|
-
import { createSystemEventsRouter } from '../core/system-events.router.js'
|
|
74
|
-
import { createLiveEventsRouter } from '../core/live-events.router.js'
|
|
75
|
-
import { createCapabilitiesRouter } from '../core/capabilities.router.js'
|
|
76
|
-
import { createStreamProbeRouter } from '../core/stream-probe.router.js'
|
|
77
|
-
import { createHwAccelRouter } from '../core/hwaccel.router.js'
|
|
78
|
-
import { requireSingleton, firstSupported, anySupports } from './cap-mount-helpers.js'
|
|
79
|
-
import { extractUserAgent } from './client-ip.js'
|
|
80
|
-
import type { TrpcContext } from './trpc.context.js'
|
|
81
|
-
import type { AuthService } from '../../core/auth/auth.service'
|
|
82
|
-
import type { ConfigService } from '../../core/config/config.service'
|
|
83
|
-
import type { FeatureService } from '../../core/feature/feature.service'
|
|
84
|
-
import type { LoggingService } from '../../core/logging/logging.service'
|
|
85
|
-
import type { EventBusService } from '../../core/events/event-bus.service'
|
|
86
|
-
import type { AgentRegistryService } from '../../core/agent/agent-registry.service'
|
|
87
|
-
import type { MoleculerService } from '../../core/moleculer/moleculer.service'
|
|
88
|
-
import type { AddonRegistryService } from '../../core/addon/addon-registry.service'
|
|
89
|
-
import type { AddonPackageService } from '../../core/addon/addon-package.service'
|
|
90
|
-
import type { ReplEngineService } from '../../core/repl/repl-engine.service'
|
|
91
|
-
import type { NetworkQualityService } from '../../core/network/network-quality.service'
|
|
92
|
-
import type { AddonBridgeService } from '../../core/addon-bridge/addon-bridge.service'
|
|
93
|
-
import type { NotificationService, ToastService } from '@camstack/core'
|
|
94
|
-
import type { StreamProbeService } from '../../core/streaming/stream-probe.service'
|
|
95
|
-
|
|
96
|
-
export interface RouterServices {
|
|
97
|
-
authService: AuthService
|
|
98
|
-
configService: ConfigService
|
|
99
|
-
featureService: FeatureService
|
|
100
|
-
loggingService: LoggingService
|
|
101
|
-
eventBus: EventBusService
|
|
102
|
-
agentRegistry: AgentRegistryService
|
|
103
|
-
moleculer: MoleculerService
|
|
104
|
-
addonRegistry: AddonRegistryService
|
|
105
|
-
replEngine: ReplEngineService
|
|
106
|
-
networkQualityService: NetworkQualityService
|
|
107
|
-
addonBridge: AddonBridgeService
|
|
108
|
-
addonPackageService: AddonPackageService
|
|
109
|
-
notificationService: NotificationService | null
|
|
110
|
-
toastService: ToastService | null
|
|
111
|
-
capabilityRegistry: CapabilityRegistry | null
|
|
112
|
-
streamProbe: StreamProbeService | null
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
type WebrtcSessionProvider = InferProvider<typeof webrtcSessionCapability>
|
|
116
|
-
type CreateSessionInput = Parameters<WebrtcSessionProvider['createSession']>[0]
|
|
117
|
-
type HandleOfferInput = Parameters<WebrtcSessionProvider['handleOffer']>[0]
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Merge the server-read User-Agent into a signaling call's
|
|
121
|
-
* `consumerAttribution`, building a NEW input object (immutable — never
|
|
122
|
-
* mutates the caller's input). When `userAgent` is null (mesh-originated
|
|
123
|
-
* call, or a client that omits the header) the input passes through
|
|
124
|
-
* unchanged. Any client-supplied `userAgent` is OVERWRITTEN — the hub
|
|
125
|
-
* trusts only the request context, never the client.
|
|
126
|
-
*/
|
|
127
|
-
export function enrichInputWithUserAgent<
|
|
128
|
-
TInput extends { consumerAttribution?: BrokerConsumerAttribution },
|
|
129
|
-
>(input: TInput, userAgent: string | null): TInput {
|
|
130
|
-
if (userAgent === null) return input
|
|
131
|
-
const base: BrokerConsumerAttribution = input.consumerAttribution ?? { kind: 'webrtc-browser' }
|
|
132
|
-
return {
|
|
133
|
-
...input,
|
|
134
|
-
consumerAttribution: { ...base, userAgent },
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Relay-only forcing for remote viewers is DISABLED (2026-05-26).
|
|
140
|
-
*
|
|
141
|
-
* It was meant to give CGNAT/4G viewers a clean relay↔relay path, but werift's
|
|
142
|
-
* TURN media-forward is unreliable between two real TURN servers (relay↔relay
|
|
143
|
-
* connects yet media never arrives → connected-but-black), and forcing relay
|
|
144
|
-
* ALSO kills the direct LAN/Tailscale host pair — which carries full native
|
|
145
|
-
* quality with no relay. We now offer ALL candidates (host incl. the hub's
|
|
146
|
-
* advertised Tailscale address, srflx, relay) and let ICE nominate the best
|
|
147
|
-
* reachable pair: direct when possible, relay only as a fallback. The
|
|
148
|
-
* `relayOnly` cap field + broker support remain for when relay media-forward
|
|
149
|
-
* is fixed.
|
|
150
|
-
*
|
|
151
|
-
* The wrapper additionally enriches the `createSession` / `handleOffer`
|
|
152
|
-
* subscriber attribution with the originating client's User-Agent, read
|
|
153
|
-
* from the tRPC request context (browser sessions). All OTHER methods
|
|
154
|
-
* delegate straight through — auth, the remote-proxy factory and every
|
|
155
|
-
* signaling behaviour are untouched.
|
|
156
|
-
*/
|
|
157
|
-
export function wrapWebrtcSessionProviderWithRelay(
|
|
158
|
-
provider: WebrtcSessionProvider,
|
|
159
|
-
ctx: TrpcContext,
|
|
160
|
-
): WebrtcSessionProvider {
|
|
161
|
-
const userAgent = extractUserAgent(ctx.req)
|
|
162
|
-
return {
|
|
163
|
-
...provider,
|
|
164
|
-
createSession: (input: CreateSessionInput) =>
|
|
165
|
-
provider.createSession(enrichInputWithUserAgent(input, userAgent)),
|
|
166
|
-
handleOffer: (input: HandleOfferInput) =>
|
|
167
|
-
provider.handleOffer(enrichInputWithUserAgent(input, userAgent)),
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Build the AppRouter. Mounts every codegen'd cap router via the auto-
|
|
173
|
-
* mount entrypoint and overrides the handful that need service-backed
|
|
174
|
-
* providers or custom collection dispatch. Non-cap (core) routers ride
|
|
175
|
-
* alongside in the same root object.
|
|
176
|
-
*
|
|
177
|
-
* Override-by-spread: spread `mountAllCaps(services)` first, then the
|
|
178
|
-
* overrides AFTER — the later property wins. The drift guard in
|
|
179
|
-
* `scripts/codegen.ts` ensures every codegen'd `createCapRouter_X` is
|
|
180
|
-
* present in the auto-mount inventory (or explicitly in the legacy
|
|
181
|
-
* skip-list), so the override list below NEVER needs to add a new entry
|
|
182
|
-
* just to mount a new cap — only to swap in a custom provider.
|
|
183
|
-
*/
|
|
184
|
-
function buildCapabilityRouters(services: RouterServices) {
|
|
185
|
-
return {
|
|
186
|
-
// ── Auto-mount: every codegen'd cap router with a canonical
|
|
187
|
-
// provider shape. Everything below this line OVERRIDES the
|
|
188
|
-
// auto-mount entry for caps with service-backed providers,
|
|
189
|
-
// custom collection routing, or a hub-only `null` remote proxy.
|
|
190
|
-
...mountAllCaps(services),
|
|
191
|
-
|
|
192
|
-
// ── Non-cap (core) routers — hand-written, single-impl ──────────
|
|
193
|
-
notifications: createNotificationsRouter(services.notificationService),
|
|
194
|
-
// Raw DB proxy for forked workers to read/write addon store.
|
|
195
|
-
// Workers use ctx.api.addonSettingsRaw.getGlobal.query({...}).
|
|
196
|
-
// NOT the three-level settings gateway — that's the codegen'd
|
|
197
|
-
// `addonSettings` cap router (mounted via auto-mount above).
|
|
198
|
-
addonSettingsRaw: createAddonSettingsRouter(services.configService),
|
|
199
|
-
settingsBackend: createSettingsBackendRouter(() => services.addonRegistry.getSettingsBackend()),
|
|
200
|
-
eventBusProxy: createEventBusProxyRouter(services.eventBus),
|
|
201
|
-
repl: createReplRouter(services.replEngine),
|
|
202
|
-
systemEvents: createSystemEventsRouter(services.eventBus),
|
|
203
|
-
capabilities: createCapabilitiesRouter(services.capabilityRegistry, services.configService),
|
|
204
|
-
logs: createLogsRouter(services.loggingService),
|
|
205
|
-
live: createLiveEventsRouter(services.eventBus, services.addonRegistry),
|
|
206
|
-
// stream-probe — fixed core API (ffprobe wrapper), not a cap.
|
|
207
|
-
streamProbe: createStreamProbeRouter(services.streamProbe),
|
|
208
|
-
// hwaccel — fixed core API, wraps the per-node `$hwaccel` Moleculer
|
|
209
|
-
// service. UI pipeline / NodeDetail pages query per-node to show
|
|
210
|
-
// which hardware backend each agent will use.
|
|
211
|
-
hwaccel: createHwAccelRouter(services.moleculer?.broker ?? null),
|
|
212
|
-
auth: createAuthRouter(services.authService, services.capabilityRegistry),
|
|
213
|
-
|
|
214
|
-
// ── Cap overrides: service-backed providers ─────────────────────
|
|
215
|
-
// These caps don't have an addon registering a provider in the
|
|
216
|
-
// CapabilityRegistry — the provider is built on-demand from
|
|
217
|
-
// backend services. `mountAllCaps` would return `null` for them
|
|
218
|
-
// (registry lookup miss), so we re-mount with `buildXProvider`.
|
|
219
|
-
networkQuality: createCapRouter_networkQuality((_ctx) =>
|
|
220
|
-
buildNetworkQualityProvider(services.networkQualityService),
|
|
221
|
-
),
|
|
222
|
-
system: createCapRouter_system((_ctx) =>
|
|
223
|
-
buildSystemProvider(services.featureService, services.capabilityRegistry),
|
|
224
|
-
),
|
|
225
|
-
toast: createCapRouter_toast((ctx) => buildToastProvider(services.toastService, ctx)),
|
|
226
|
-
integrations: createCapRouter_integrations((_ctx) =>
|
|
227
|
-
buildIntegrationsProvider(
|
|
228
|
-
services.addonRegistry,
|
|
229
|
-
services.eventBus,
|
|
230
|
-
services.loggingService,
|
|
231
|
-
services.capabilityRegistry,
|
|
232
|
-
),
|
|
233
|
-
),
|
|
234
|
-
nodes: createCapRouter_nodes((_ctx) =>
|
|
235
|
-
buildNodesProvider(services.agentRegistry, services.moleculer, services.addonRegistry),
|
|
236
|
-
),
|
|
237
|
-
addons: createCapRouter_addons((ctx) =>
|
|
238
|
-
buildAddonsProvider(
|
|
239
|
-
services.addonRegistry,
|
|
240
|
-
services.addonPackageService,
|
|
241
|
-
services.loggingService,
|
|
242
|
-
services.moleculer,
|
|
243
|
-
services.configService,
|
|
244
|
-
ctx,
|
|
245
|
-
services.eventBus,
|
|
246
|
-
),
|
|
247
|
-
),
|
|
248
|
-
|
|
249
|
-
// ── Cap overrides: cross-node remote-proxy cast ─────────────────
|
|
250
|
-
// These caps' providers have manual interface types that pre-date
|
|
251
|
-
// `InferProvider<typeof xCap>` — structurally identical, nominally
|
|
252
|
-
// distinct. Casting at the override site is cheaper than reworking
|
|
253
|
-
// the provider declarations. Auto-mount can't infer the cast.
|
|
254
|
-
pipelineExecutor: createCapRouter_pipelineExecutor(
|
|
255
|
-
(_ctx) =>
|
|
256
|
-
requireSingleton(services.capabilityRegistry, 'pipeline-executor') as InferProvider<
|
|
257
|
-
typeof pipelineExecutorCapability
|
|
258
|
-
> | null,
|
|
259
|
-
(capName, nodeId) =>
|
|
260
|
-
services.moleculer.createCapabilityProxy(capName, nodeId) as InferProvider<
|
|
261
|
-
typeof pipelineExecutorCapability
|
|
262
|
-
> | null,
|
|
263
|
-
),
|
|
264
|
-
pipelineRunner: createCapRouter_pipelineRunner(
|
|
265
|
-
(_ctx) =>
|
|
266
|
-
requireSingleton(services.capabilityRegistry, 'pipeline-runner') as InferProvider<
|
|
267
|
-
typeof pipelineRunnerCapability
|
|
268
|
-
> | null,
|
|
269
|
-
(capName, nodeId) =>
|
|
270
|
-
services.moleculer.createCapabilityProxy(capName, nodeId) as InferProvider<
|
|
271
|
-
typeof pipelineRunnerCapability
|
|
272
|
-
> | null,
|
|
273
|
-
),
|
|
274
|
-
pipelineOrchestrator: createCapRouter_pipelineOrchestrator(
|
|
275
|
-
(_ctx) =>
|
|
276
|
-
requireSingleton(services.capabilityRegistry, 'pipeline-orchestrator') as InferProvider<
|
|
277
|
-
typeof pipelineOrchestratorCapability
|
|
278
|
-
> | null,
|
|
279
|
-
(capName, nodeId) =>
|
|
280
|
-
services.moleculer.createCapabilityProxy(capName, nodeId) as InferProvider<
|
|
281
|
-
typeof pipelineOrchestratorCapability
|
|
282
|
-
> | null,
|
|
283
|
-
),
|
|
284
|
-
audioAnalyzer: createCapRouter_audioAnalyzer(
|
|
285
|
-
(_ctx) => requireSingleton(services.capabilityRegistry, 'audio-analyzer'),
|
|
286
|
-
(capName, nodeId) =>
|
|
287
|
-
services.moleculer.createCapabilityProxy(capName, nodeId) as InferProvider<
|
|
288
|
-
typeof audioAnalyzerCapability
|
|
289
|
-
> | null,
|
|
290
|
-
),
|
|
291
|
-
audioCodec: createCapRouter_audioCodec(
|
|
292
|
-
(_ctx) =>
|
|
293
|
-
requireSingleton(services.capabilityRegistry, 'audio-codec') as InferProvider<
|
|
294
|
-
typeof audioCodecCapability
|
|
295
|
-
> | null,
|
|
296
|
-
(capName, nodeId) =>
|
|
297
|
-
services.moleculer.createCapabilityProxy(capName, nodeId) as InferProvider<
|
|
298
|
-
typeof audioCodecCapability
|
|
299
|
-
> | null,
|
|
300
|
-
),
|
|
301
|
-
decoder: createCapRouter_decoder(
|
|
302
|
-
(_ctx) =>
|
|
303
|
-
requireSingleton(services.capabilityRegistry, 'decoder') as InferProvider<
|
|
304
|
-
typeof decoderCapability
|
|
305
|
-
> | null,
|
|
306
|
-
(capName, nodeId) =>
|
|
307
|
-
services.moleculer.createCapabilityProxy(capName, nodeId) as InferProvider<
|
|
308
|
-
typeof decoderCapability
|
|
309
|
-
> | null,
|
|
310
|
-
),
|
|
311
|
-
platformProbe: createCapRouter_platformProbe(
|
|
312
|
-
(_ctx) => requireSingleton(services.capabilityRegistry, 'platform-probe'),
|
|
313
|
-
(capName, nodeId) =>
|
|
314
|
-
services.moleculer.createCapabilityProxy(capName, nodeId) as InferProvider<
|
|
315
|
-
typeof platformProbeCapability
|
|
316
|
-
> | null,
|
|
317
|
-
),
|
|
318
|
-
|
|
319
|
-
// ── Cap overrides: hub-only, no remote fallback ─────────────────
|
|
320
|
-
// The cap is intentionally single-node; agents are not directly
|
|
321
|
-
// addressable. Auto-mount would still set up a proxy factory; we
|
|
322
|
-
// explicitly return `null` to short-circuit any cross-node attempt.
|
|
323
|
-
localNetwork: createCapRouter_localNetwork(
|
|
324
|
-
(_ctx) => requireSingleton(services.capabilityRegistry, 'local-network'),
|
|
325
|
-
(_capName, _nodeId) => null as InferProvider<typeof localNetworkCapability> | null,
|
|
326
|
-
),
|
|
327
|
-
|
|
328
|
-
// ── Cap overrides: collection dispatch (contribution / probe) ──
|
|
329
|
-
// `turn-provider.getTurnServers` is now handled generically by the
|
|
330
|
-
// auto-mount: it's an array-output method on a `collection` cap, so
|
|
331
|
-
// `mountAllCaps` fans it across every enabled provider via
|
|
332
|
-
// `concatCollection` when no `addonId` is supplied. No hand-written
|
|
333
|
-
// override needed.
|
|
334
|
-
//
|
|
335
|
-
// `snapshot-provider.supportsDevice` is an OR across providers;
|
|
336
|
-
// `getSnapshot` picks the first one that claims the device. The
|
|
337
|
-
// generic first-provider resolver from the auto-mount can't model
|
|
338
|
-
// this — we hand-write the probe + fan-out logic.
|
|
339
|
-
snapshotProvider: createCapRouter_snapshotProvider((_ctx) => {
|
|
340
|
-
const reg = services.capabilityRegistry
|
|
341
|
-
if (!reg) return null
|
|
342
|
-
type SnapshotProviderInferred =
|
|
343
|
-
import('@camstack/types').CapabilityProviderMap['snapshot-provider']
|
|
344
|
-
const providers = reg.getCollection<SnapshotProviderInferred>('snapshot-provider')
|
|
345
|
-
if (!providers || providers.length === 0) return null
|
|
346
|
-
const supportsDevice = anySupports(providers, 'supportsDevice')
|
|
347
|
-
const getSnapshot = firstSupported(providers, 'supportsDevice', 'getSnapshot')
|
|
348
|
-
return { supportsDevice, getSnapshot }
|
|
349
|
-
}),
|
|
350
|
-
|
|
351
|
-
// ── Cap override: server-detected remote → relay-only ────────────
|
|
352
|
-
// The broker (a forked addon) can't see the HTTP request, so it
|
|
353
|
-
// can't tell a LAN viewer from a remote one. We override only the
|
|
354
|
-
// `getProvider` accessor to return a per-request provider whose
|
|
355
|
-
// `createSession` carries a server-computed `relayOnly` flag derived
|
|
356
|
-
// from the client IP in `ctx.req`. Remote (CGNAT/4G) viewers force
|
|
357
|
-
// TURN-relay-only ICE; LAN viewers keep the direct host/srflx path.
|
|
358
|
-
// All other methods delegate straight through, and the cross-node
|
|
359
|
-
// remote-proxy routing is preserved (forked/agent-hosted brokers).
|
|
360
|
-
webrtcSession: createCapRouter_webrtcSession(
|
|
361
|
-
(ctx) => {
|
|
362
|
-
const provider =
|
|
363
|
-
services.capabilityRegistry?.getSingleton<WebrtcSessionProvider>('webrtc-session') ?? null
|
|
364
|
-
return provider ? wrapWebrtcSessionProviderWithRelay(provider, ctx) : null
|
|
365
|
-
},
|
|
366
|
-
(capName, nodeId) =>
|
|
367
|
-
services.moleculer.createCapabilityProxy(capName, nodeId) as WebrtcSessionProvider | null,
|
|
368
|
-
),
|
|
369
|
-
|
|
370
|
-
// NOT MOUNTED — legacy provider shapes (positional args / sync
|
|
371
|
-
// returns) that don't match the codegen routers' {input}-object +
|
|
372
|
-
// Promise<T> contract. Tracked by `LEGACY_SHAPE_SKIP` in
|
|
373
|
-
// `generated-cap-mounts.ts` until the provider refactor (task #195):
|
|
374
|
-
// - addon-routes (IAddonRouteProvider: getRoutes sync)
|
|
375
|
-
// - auth-provider (IAuthProvider: positional credentials)
|
|
376
|
-
// - log-destination (ILogDestination: positional + extra lifecycle)
|
|
377
|
-
// - restreamer (IRestreamer: registerDevice positional)
|
|
378
|
-
// - streaming-engine (IStreamingEngine: registerStream positional)
|
|
379
|
-
// - webrtc (IWebRtcProvider: missing hasAdaptiveBitrate)
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
export function buildAppRouter(services: RouterServices) {
|
|
384
|
-
return trpcRouter({
|
|
385
|
-
...buildCapabilityRouters(services),
|
|
386
|
-
})
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
export type AppRouter = ReturnType<typeof buildAppRouter>
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/** Browser session cookie carrying the hub JWT. Set by POST /api/auth/session
|
|
2
|
-
* after a tRPC login; read by the addon-route catch-all for `authenticated`
|
|
3
|
-
* routes hit by a plain browser navigation. */
|
|
4
|
-
export const SESSION_COOKIE = 'camstack_session'
|
|
5
|
-
|
|
6
|
-
interface CookieSpec {
|
|
7
|
-
readonly name: string
|
|
8
|
-
readonly value: string
|
|
9
|
-
readonly options: {
|
|
10
|
-
httpOnly: boolean
|
|
11
|
-
sameSite: 'lax'
|
|
12
|
-
secure: boolean
|
|
13
|
-
path: string
|
|
14
|
-
maxAge: number
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function buildSessionCookie(token: string, ttlSec: number): CookieSpec {
|
|
19
|
-
return {
|
|
20
|
-
name: SESSION_COOKIE,
|
|
21
|
-
value: token,
|
|
22
|
-
options: { httpOnly: true, sameSite: 'lax', secure: true, path: '/', maxAge: ttlSec },
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function clearSessionCookie(): CookieSpec {
|
|
27
|
-
return {
|
|
28
|
-
name: SESSION_COOKIE,
|
|
29
|
-
value: '',
|
|
30
|
-
options: { httpOnly: true, sameSite: 'lax', secure: true, path: '/', maxAge: 0 },
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** A browser navigation we can bounce to the login page: a top-level GET
|
|
35
|
-
* that wants HTML. Anything else (API call, POST, non-HTML) keeps the
|
|
36
|
-
* 401 behavior so programmatic clients get a clean error. */
|
|
37
|
-
export function shouldRedirectToLogin(method: string, accept: string | undefined): boolean {
|
|
38
|
-
return method === 'GET' && typeof accept === 'string' && accept.includes('text/html')
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** Build the `/login?next=…` URL for an unauthenticated browser request. */
|
|
42
|
-
export function loginRedirectUrl(originalUrl: string): string {
|
|
43
|
-
return `/login?next=${encodeURIComponent(originalUrl)}`
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** Allowed redirect target for `GET /api/embed-auth`: a same-origin RELATIVE
|
|
47
|
-
* path to a stream-broker embed page. Defeats open-redirects — the endpoint
|
|
48
|
-
* sets the session cookie from a Bearer token, so the `next` must be safe to
|
|
49
|
-
* bounce to. Rejects absolute/protocol-relative URLs, backslashes, and `..`. */
|
|
50
|
-
export function isEmbedRedirectTarget(next: string): boolean {
|
|
51
|
-
if (!next.startsWith('/addon/stream-broker/embed/')) return false
|
|
52
|
-
if (next.includes('\\') || next.includes('://') || next.includes('..')) return false
|
|
53
|
-
return true
|
|
54
|
-
}
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import {
|
|
3
|
-
planIntegrationIdBackfill,
|
|
4
|
-
planDeleteTimeStamps,
|
|
5
|
-
runIntegrationIdBackfill,
|
|
6
|
-
} from '../integration-id-backfill'
|
|
7
|
-
|
|
8
|
-
describe('planDeleteTimeStamps', () => {
|
|
9
|
-
it("claims an untagged top-level device of the deleted integration's single-integration addon", () => {
|
|
10
|
-
const stamps = planDeleteTimeStamps(
|
|
11
|
-
'int_rtsp',
|
|
12
|
-
[{ id: 'int_rtsp', addonId: 'provider-rtsp' }],
|
|
13
|
-
[
|
|
14
|
-
{ id: 4, addonId: 'provider-rtsp', parentDeviceId: null },
|
|
15
|
-
{ id: 6, addonId: 'provider-rtsp', parentDeviceId: null },
|
|
16
|
-
],
|
|
17
|
-
)
|
|
18
|
-
expect(stamps).toEqual([
|
|
19
|
-
{ deviceId: 4, integrationId: 'int_rtsp' },
|
|
20
|
-
{ deviceId: 6, integrationId: 'int_rtsp' },
|
|
21
|
-
])
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
it('returns no stamps for a multi-integration addon (ambiguous — never auto-claim)', () => {
|
|
25
|
-
const stamps = planDeleteTimeStamps(
|
|
26
|
-
'int_a',
|
|
27
|
-
[
|
|
28
|
-
{ id: 'int_a', addonId: 'provider-ha' },
|
|
29
|
-
{ id: 'int_b', addonId: 'provider-ha' },
|
|
30
|
-
],
|
|
31
|
-
[{ id: 11, addonId: 'provider-ha', parentDeviceId: null }],
|
|
32
|
-
)
|
|
33
|
-
expect(stamps).toEqual([])
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
it('only returns stamps for the integration being deleted, not siblings of other addons', () => {
|
|
37
|
-
const stamps = planDeleteTimeStamps(
|
|
38
|
-
'int_rtsp',
|
|
39
|
-
[
|
|
40
|
-
{ id: 'int_rtsp', addonId: 'provider-rtsp' },
|
|
41
|
-
{ id: 'int_onvif', addonId: 'provider-onvif' },
|
|
42
|
-
],
|
|
43
|
-
[
|
|
44
|
-
{ id: 4, addonId: 'provider-rtsp', parentDeviceId: null },
|
|
45
|
-
{ id: 5, addonId: 'provider-onvif', parentDeviceId: null },
|
|
46
|
-
],
|
|
47
|
-
)
|
|
48
|
-
expect(stamps).toEqual([{ deviceId: 4, integrationId: 'int_rtsp' }])
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
it('skips devices already tagged with the deleted integration', () => {
|
|
52
|
-
const stamps = planDeleteTimeStamps(
|
|
53
|
-
'int_rtsp',
|
|
54
|
-
[{ id: 'int_rtsp', addonId: 'provider-rtsp' }],
|
|
55
|
-
[{ id: 4, addonId: 'provider-rtsp', parentDeviceId: null, integrationId: 'int_rtsp' }],
|
|
56
|
-
)
|
|
57
|
-
expect(stamps).toEqual([])
|
|
58
|
-
})
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
describe('planIntegrationIdBackfill', () => {
|
|
62
|
-
it('stamps a top-level untagged device whose addon has exactly one integration', () => {
|
|
63
|
-
const stamps = planIntegrationIdBackfill(
|
|
64
|
-
[{ id: 'int_1', addonId: 'provider-rtsp' }],
|
|
65
|
-
[{ id: 10, addonId: 'provider-rtsp', parentDeviceId: null }],
|
|
66
|
-
)
|
|
67
|
-
expect(stamps).toEqual([{ deviceId: 10, integrationId: 'int_1' }])
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
it('skips devices whose addon hosts multiple integrations (ambiguous)', () => {
|
|
71
|
-
const stamps = planIntegrationIdBackfill(
|
|
72
|
-
[
|
|
73
|
-
{ id: 'int_a', addonId: 'provider-ha' },
|
|
74
|
-
{ id: 'int_b', addonId: 'provider-ha' },
|
|
75
|
-
],
|
|
76
|
-
[{ id: 11, addonId: 'provider-ha', parentDeviceId: null }],
|
|
77
|
-
)
|
|
78
|
-
expect(stamps).toEqual([])
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
it('skips already-tagged devices and child devices', () => {
|
|
82
|
-
const stamps = planIntegrationIdBackfill(
|
|
83
|
-
[{ id: 'int_1', addonId: 'provider-rtsp' }],
|
|
84
|
-
[
|
|
85
|
-
{ id: 12, addonId: 'provider-rtsp', parentDeviceId: null, integrationId: 'int_1' },
|
|
86
|
-
{ id: 13, addonId: 'provider-rtsp', parentDeviceId: 12 },
|
|
87
|
-
],
|
|
88
|
-
)
|
|
89
|
-
expect(stamps).toEqual([])
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
it('skips devices whose addon has no integration', () => {
|
|
93
|
-
const stamps = planIntegrationIdBackfill(
|
|
94
|
-
[{ id: 'int_1', addonId: 'provider-rtsp' }],
|
|
95
|
-
[{ id: 14, addonId: 'provider-onvif', parentDeviceId: null }],
|
|
96
|
-
)
|
|
97
|
-
expect(stamps).toEqual([])
|
|
98
|
-
})
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
describe('runIntegrationIdBackfill', () => {
|
|
102
|
-
it('applies stamps and reports the count, skipping failures', async () => {
|
|
103
|
-
const stamped: Array<{ deviceId: number; integrationId: string }> = []
|
|
104
|
-
const result = await runIntegrationIdBackfill({
|
|
105
|
-
listIntegrations: async () => [{ id: 'int_1', addonId: 'provider-rtsp' }],
|
|
106
|
-
listDevices: async () => [
|
|
107
|
-
{ id: 10, addonId: 'provider-rtsp', parentDeviceId: null },
|
|
108
|
-
{ id: 11, addonId: 'provider-rtsp', parentDeviceId: null },
|
|
109
|
-
],
|
|
110
|
-
setIntegrationId: async (deviceId, integrationId) => {
|
|
111
|
-
if (deviceId === 11) throw new Error('boom')
|
|
112
|
-
stamped.push({ deviceId, integrationId })
|
|
113
|
-
},
|
|
114
|
-
logger: { info: () => {}, warn: () => {} },
|
|
115
|
-
})
|
|
116
|
-
expect(result).toEqual({ stamped: 1 })
|
|
117
|
-
expect(stamped).toEqual([{ deviceId: 10, integrationId: 'int_1' }])
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
it('does nothing when there is nothing to stamp', async () => {
|
|
121
|
-
const result = await runIntegrationIdBackfill({
|
|
122
|
-
listIntegrations: async () => [],
|
|
123
|
-
listDevices: async () => [],
|
|
124
|
-
setIntegrationId: async () => {
|
|
125
|
-
throw new Error('should not be called')
|
|
126
|
-
},
|
|
127
|
-
logger: { info: () => {}, warn: () => {} },
|
|
128
|
-
})
|
|
129
|
-
expect(result).toEqual({ stamped: 0 })
|
|
130
|
-
})
|
|
131
|
-
})
|