@camstack/server 0.1.3
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/.env.example +17 -0
- package/package.json +55 -0
- package/src/__tests__/addon-install-e2e.test.ts +75 -0
- package/src/__tests__/addon-pages-e2e.test.ts +178 -0
- package/src/__tests__/addon-route-session.test.ts +17 -0
- package/src/__tests__/addon-settings-router.spec.ts +62 -0
- package/src/__tests__/addon-upload.spec.ts +355 -0
- package/src/__tests__/agent-registry.spec.ts +162 -0
- package/src/__tests__/agent-status-page.spec.ts +84 -0
- package/src/__tests__/auth-session-cookie.test.ts +21 -0
- package/src/__tests__/cap-providers/cap-usage-graph.spec.ts +23 -0
- package/src/__tests__/cap-providers/compute-topology-categories.spec.ts +64 -0
- package/src/__tests__/cap-routers/_meta.spec.ts +200 -0
- package/src/__tests__/cap-routers/addon-settings.router.spec.ts +106 -0
- package/src/__tests__/cap-routers/device-manager-aggregate.router.spec.ts +142 -0
- package/src/__tests__/cap-routers/harness.ts +159 -0
- package/src/__tests__/cap-routers/metrics-provider.router.spec.ts +119 -0
- package/src/__tests__/cap-routers/null-provider-guard.spec.ts +66 -0
- package/src/__tests__/cap-routers/pipeline-executor.router.spec.ts +135 -0
- package/src/__tests__/cap-routers/settings-store.router.spec.ts +247 -0
- package/src/__tests__/capability-e2e.test.ts +386 -0
- package/src/__tests__/cli-e2e.test.ts +129 -0
- package/src/__tests__/core-cap-bridge.spec.ts +89 -0
- package/src/__tests__/embedded-deps-e2e.test.ts +109 -0
- package/src/__tests__/event-bus-proxy-router.spec.ts +72 -0
- package/src/__tests__/fixtures/mock-analysis-addon-a.ts +37 -0
- package/src/__tests__/fixtures/mock-analysis-addon-b.ts +37 -0
- package/src/__tests__/fixtures/mock-log-addon.ts +37 -0
- package/src/__tests__/fixtures/mock-storage-addon.ts +40 -0
- package/src/__tests__/framework-allowlist.spec.ts +95 -0
- package/src/__tests__/https-e2e.test.ts +118 -0
- package/src/__tests__/lifecycle-e2e.test.ts +140 -0
- package/src/__tests__/live-events-subscription.spec.ts +150 -0
- package/src/__tests__/moleculer-register-node-idempotency.spec.ts +229 -0
- package/src/__tests__/oauth2-account-linking.spec.ts +736 -0
- package/src/__tests__/post-boot-restart.spec.ts +161 -0
- package/src/__tests__/singleton-contention.test.ts +487 -0
- package/src/__tests__/streaming-diagnostic.test.ts +512 -0
- package/src/__tests__/streaming-scale.test.ts +280 -0
- package/src/agent-status-page.ts +121 -0
- package/src/api/__tests__/addons-custom.spec.ts +134 -0
- package/src/api/__tests__/capabilities.router.test.ts +47 -0
- package/src/api/addon-upload.ts +472 -0
- package/src/api/addons-custom.router.ts +100 -0
- package/src/api/auth-whoami.ts +99 -0
- package/src/api/bridge-addons.router.ts +120 -0
- package/src/api/capabilities.router.ts +226 -0
- package/src/api/core/__tests__/auth-router-totp.spec.ts +256 -0
- package/src/api/core/addon-settings.router.ts +124 -0
- package/src/api/core/agents.router.ts +87 -0
- package/src/api/core/auth.router.ts +303 -0
- package/src/api/core/cap-providers.ts +993 -0
- package/src/api/core/capabilities.router.ts +119 -0
- package/src/api/core/collection-preference.ts +40 -0
- package/src/api/core/event-bus-proxy.router.ts +45 -0
- package/src/api/core/hwaccel.router.ts +81 -0
- package/src/api/core/live-events.router.ts +60 -0
- package/src/api/core/logs.router.ts +162 -0
- package/src/api/core/notifications.router.ts +65 -0
- package/src/api/core/repl.router.ts +41 -0
- package/src/api/core/settings-backend.router.ts +142 -0
- package/src/api/core/stream-probe.router.ts +57 -0
- package/src/api/core/system-events.router.ts +116 -0
- package/src/api/health/health.routes.ts +123 -0
- package/src/api/oauth2/__tests__/oauth2-routes.spec.ts +52 -0
- package/src/api/oauth2/consent-page.ts +42 -0
- package/src/api/oauth2/oauth2-routes.ts +248 -0
- package/src/api/trpc/__tests__/scope-access-device.spec.ts +223 -0
- package/src/api/trpc/__tests__/scope-access.spec.ts +107 -0
- package/src/api/trpc/cap-mount-helpers.ts +225 -0
- package/src/api/trpc/core-cap-bridge.ts +152 -0
- package/src/api/trpc/generated-cap-mounts.ts +707 -0
- package/src/api/trpc/generated-cap-routers.ts +6340 -0
- package/src/api/trpc/scope-access.ts +110 -0
- package/src/api/trpc/trpc.context.ts +255 -0
- package/src/api/trpc/trpc.middleware.ts +140 -0
- package/src/api/trpc/trpc.router.ts +275 -0
- package/src/auth/session-cookie.ts +44 -0
- package/src/boot/boot-config.ts +278 -0
- package/src/boot/post-boot.service.ts +103 -0
- package/src/core/addon/__tests__/addon-registry-capability.test.ts +53 -0
- package/src/core/addon/addon-package.service.ts +1684 -0
- package/src/core/addon/addon-registry.service.ts +2926 -0
- package/src/core/addon/addon-search.service.ts +90 -0
- package/src/core/addon/addon-settings-provider.ts +276 -0
- package/src/core/addon/addon.tokens.ts +2 -0
- package/src/core/addon-bridge/addon-bridge.service.ts +125 -0
- package/src/core/addon-pages/addon-pages.service.spec.ts +117 -0
- package/src/core/addon-pages/addon-pages.service.ts +80 -0
- package/src/core/addon-widgets/addon-widgets.service.ts +92 -0
- package/src/core/agent/agent-registry.service.ts +507 -0
- package/src/core/auth/auth.service.spec.ts +88 -0
- package/src/core/auth/auth.service.ts +8 -0
- package/src/core/capability/capability.service.ts +57 -0
- package/src/core/config/config.schema.ts +3 -0
- package/src/core/config/config.service.spec.ts +175 -0
- package/src/core/config/config.service.ts +7 -0
- package/src/core/events/event-bus.service.spec.ts +212 -0
- package/src/core/events/event-bus.service.ts +85 -0
- package/src/core/feature/feature.service.spec.ts +96 -0
- package/src/core/feature/feature.service.ts +8 -0
- package/src/core/lifecycle/lifecycle-state-machine.spec.ts +168 -0
- package/src/core/lifecycle/lifecycle-state-machine.ts +3 -0
- package/src/core/logging/log-ring-buffer.ts +3 -0
- package/src/core/logging/logging.service.spec.ts +247 -0
- package/src/core/logging/logging.service.ts +129 -0
- package/src/core/logging/scoped-logger.ts +3 -0
- package/src/core/moleculer/moleculer.service.ts +612 -0
- package/src/core/network/network-quality.service.spec.ts +47 -0
- package/src/core/network/network-quality.service.ts +5 -0
- package/src/core/notification/notification-wrapper.service.ts +36 -0
- package/src/core/notification/toast-wrapper.service.ts +31 -0
- package/src/core/provider/provider.tokens.ts +1 -0
- package/src/core/repl/repl-engine.service.spec.ts +417 -0
- package/src/core/repl/repl-engine.service.ts +156 -0
- package/src/core/storage/fs-storage-backend.spec.ts +70 -0
- package/src/core/storage/fs-storage-backend.ts +3 -0
- package/src/core/storage/settings-store.spec.ts +213 -0
- package/src/core/storage/settings-store.ts +2 -0
- package/src/core/storage/sql-schema.spec.ts +140 -0
- package/src/core/storage/sql-schema.ts +3 -0
- package/src/core/storage/storage-location-manager.spec.ts +121 -0
- package/src/core/storage/storage-location-manager.ts +3 -0
- package/src/core/storage/storage.service.spec.ts +73 -0
- package/src/core/storage/storage.service.ts +3 -0
- package/src/core/streaming/stream-probe.service.ts +212 -0
- package/src/core/topology/topology-emitter.service.ts +101 -0
- package/src/launcher.ts +309 -0
- package/src/main.ts +1049 -0
- package/src/manual-boot.ts +322 -0
- package/tsconfig.build.json +8 -0
- package/tsconfig.json +21 -0
- package/vitest.config.ts +26 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto'
|
|
2
|
+
import { readPendingRestart } from '@camstack/kernel'
|
|
3
|
+
import type { IScopedLogger, PendingRestartMarkerPayload } from '@camstack/types'
|
|
4
|
+
import { AddonRegistryService } from '../core/addon/addon-registry.service'
|
|
5
|
+
import { EventBusService } from '../core/events/event-bus.service'
|
|
6
|
+
import { LoggingService } from '../core/logging/logging.service'
|
|
7
|
+
|
|
8
|
+
export interface PostBootContext {
|
|
9
|
+
readonly port: number
|
|
10
|
+
readonly host: string
|
|
11
|
+
readonly dataPath: string
|
|
12
|
+
readonly trpcRegistered: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class PostBootService {
|
|
16
|
+
private readonly logger: IScopedLogger
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Holds the marker that fired `system.restart-completed` on this boot
|
|
20
|
+
* for `LAST_RESTART_RETENTION_MS`. Lets clients query the marker
|
|
21
|
+
* after they reconnect — the live event itself is emitted before the
|
|
22
|
+
* WS resubscribe has a chance to land.
|
|
23
|
+
*/
|
|
24
|
+
private static lastRestart: { payload: PendingRestartMarkerPayload; expiresAt: number } | null = null
|
|
25
|
+
|
|
26
|
+
/** How long the last-restart marker stays queryable after boot (5 min). */
|
|
27
|
+
static readonly LAST_RESTART_RETENTION_MS = 5 * 60_000
|
|
28
|
+
|
|
29
|
+
constructor(
|
|
30
|
+
_addonRegistry: AddonRegistryService,
|
|
31
|
+
private readonly eventBus: EventBusService,
|
|
32
|
+
loggingService: LoggingService,
|
|
33
|
+
) {
|
|
34
|
+
this.logger = loggingService.createLogger('PostBoot')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Snapshot of the most-recent restart marker, or `null` after the
|
|
39
|
+
* retention window. The hub's `addons.getLastRestart` cap method
|
|
40
|
+
* delegates here.
|
|
41
|
+
*/
|
|
42
|
+
static getLastRestart(): PendingRestartMarkerPayload | null {
|
|
43
|
+
const entry = PostBootService.lastRestart
|
|
44
|
+
if (entry === null) return null
|
|
45
|
+
if (entry.expiresAt <= Date.now()) {
|
|
46
|
+
PostBootService.lastRestart = null
|
|
47
|
+
return null
|
|
48
|
+
}
|
|
49
|
+
return entry.payload
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async run(context: PostBootContext): Promise<void> {
|
|
53
|
+
const { port, host, dataPath, trpcRegistered } = context
|
|
54
|
+
|
|
55
|
+
// Device stream wiring moved into `addon-stream-broker` — the addon
|
|
56
|
+
// does its own initial sync against `ctx.deviceRegistry` plus listens
|
|
57
|
+
// to `DeviceRegistered` events for future registrations. No kernel
|
|
58
|
+
// orchestration shim needed here anymore.
|
|
59
|
+
|
|
60
|
+
// Emit system.boot event
|
|
61
|
+
this.eventBus.emit({
|
|
62
|
+
id: randomUUID(),
|
|
63
|
+
timestamp: new Date(),
|
|
64
|
+
source: { type: 'core', id: 'system' },
|
|
65
|
+
category: 'system.boot',
|
|
66
|
+
data: { port, host, trpcRegistered, dataPath },
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
// If the previous shutdown was driven by RestartCoordinator, emit a
|
|
70
|
+
// `system.restart-completed` event so the admin UI can surface a
|
|
71
|
+
// success toast describing what changed (framework update, manual
|
|
72
|
+
// restart, …). `readPendingRestart` clears the marker atomically so
|
|
73
|
+
// we never re-fire on a crash-loop boot.
|
|
74
|
+
this.emitRestartCompletedIfPending(dataPath)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private emitRestartCompletedIfPending(dataDir: string): void {
|
|
78
|
+
const marker = readPendingRestart(dataDir)
|
|
79
|
+
if (marker === null) return
|
|
80
|
+
|
|
81
|
+
const payload: PendingRestartMarkerPayload = {
|
|
82
|
+
kind: marker.kind,
|
|
83
|
+
requestedAt: marker.requestedAt,
|
|
84
|
+
...(marker.packageName !== undefined ? { packageName: marker.packageName } : {}),
|
|
85
|
+
...(marker.fromVersion !== undefined ? { fromVersion: marker.fromVersion } : {}),
|
|
86
|
+
...(marker.toVersion !== undefined ? { toVersion: marker.toVersion } : {}),
|
|
87
|
+
...(marker.requestedBy !== undefined ? { requestedBy: marker.requestedBy } : {}),
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
this.logger.info('Restart completed', { meta: payload })
|
|
91
|
+
PostBootService.lastRestart = {
|
|
92
|
+
payload,
|
|
93
|
+
expiresAt: Date.now() + PostBootService.LAST_RESTART_RETENTION_MS,
|
|
94
|
+
}
|
|
95
|
+
this.eventBus.emit({
|
|
96
|
+
id: randomUUID(),
|
|
97
|
+
timestamp: new Date(),
|
|
98
|
+
source: { type: 'core', id: 'system' },
|
|
99
|
+
category: 'system.restart-completed',
|
|
100
|
+
data: payload,
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
|
|
2
|
+
// server/backend/src/core/addon/__tests__/addon-registry-capability.test.ts
|
|
3
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
4
|
+
import { CapabilityRegistry } from '@camstack/kernel'
|
|
5
|
+
import type { IScopedLogger } from '@camstack/types'
|
|
6
|
+
|
|
7
|
+
function createMockLogger(): IScopedLogger {
|
|
8
|
+
return {
|
|
9
|
+
error: vi.fn(),
|
|
10
|
+
warn: vi.fn(),
|
|
11
|
+
info: vi.fn(),
|
|
12
|
+
debug: vi.fn(),
|
|
13
|
+
child: vi.fn().mockReturnThis(),
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe('AddonRegistryService -- CapabilityRegistry integration', () => {
|
|
18
|
+
let registry: CapabilityRegistry
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
registry = new CapabilityRegistry(createMockLogger())
|
|
22
|
+
registry.ready()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('singleton provider is available after registerProvider', () => {
|
|
26
|
+
registry.declareCapability({ name: 'storage', scope: 'system', mode: 'singleton', methods: {} })
|
|
27
|
+
|
|
28
|
+
const mockStorageProvider = { getLocation: vi.fn() }
|
|
29
|
+
registry.registerProvider('storage', 'sqlite-storage', mockStorageProvider)
|
|
30
|
+
|
|
31
|
+
expect(registry.getSingleton('storage')).toBe(mockStorageProvider)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('streaming-engine singleton wired after provider registers', () => {
|
|
35
|
+
registry.declareCapability({ name: 'streaming-engine', scope: 'system', mode: 'singleton', methods: {} })
|
|
36
|
+
|
|
37
|
+
const mockEngine = { initialize: vi.fn() }
|
|
38
|
+
registry.registerProvider('streaming-engine', 'go2rtc', mockEngine)
|
|
39
|
+
|
|
40
|
+
expect(registry.getSingleton('streaming-engine')).toBe(mockEngine)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('log-destination collection receives all providers', () => {
|
|
44
|
+
registry.declareCapability({ name: 'log-destination', scope: 'system', mode: 'collection', methods: {} })
|
|
45
|
+
|
|
46
|
+
const dest1 = { id: 'winston' }
|
|
47
|
+
const dest2 = { id: 'loki' }
|
|
48
|
+
registry.registerProvider('log-destination', 'winston-logging', dest1)
|
|
49
|
+
registry.registerProvider('log-destination', 'loki-logging', dest2)
|
|
50
|
+
|
|
51
|
+
expect(registry.getCollection('log-destination')).toEqual([dest1, dest2])
|
|
52
|
+
})
|
|
53
|
+
})
|