@camstack/server 1.2.664 → 1.2.665
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/CHANGELOG.md +6 -0
- package/dist/api/trpc/share-view-access.js +34 -1
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.2.665] - 2026-10-28
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- A shared events link can open a track's detail page, and shared links now use the operator's colour palette
|
|
15
|
+
|
|
10
16
|
## [1.2.664] - 2026-10-27
|
|
11
17
|
|
|
12
18
|
### Changed
|
|
@@ -16,6 +16,8 @@ const GRID_VIEW_OPEN_METHODS = new Set([
|
|
|
16
16
|
'deviceManager.listAll',
|
|
17
17
|
'turnProvider.getTurnServers',
|
|
18
18
|
'live.onEvent',
|
|
19
|
+
// Display data only: preset id + hex swatches, no device or secret.
|
|
20
|
+
'displayPalette.getPalette',
|
|
19
21
|
]);
|
|
20
22
|
/**
|
|
21
23
|
* Talk-back, callable only on a token minted with `scope.talk` AND only for an
|
|
@@ -58,13 +60,19 @@ function extractDeviceId(input) {
|
|
|
58
60
|
* • `pipelineAnalytics.listRecentTracks` — the feed itself
|
|
59
61
|
* • `pipelineAnalytics.listEventKindsBatch` — the class→macro taxonomy
|
|
60
62
|
* • `pipelineAnalytics.searchObjectEvents` — CLIP text search
|
|
63
|
+
* • `pipelineAnalytics.getTrack` / `listTrackMedia` — the track detail page
|
|
61
64
|
*
|
|
62
65
|
* Intentionally NOT here: everything the grid link gets (WebRTC signalling,
|
|
63
66
|
* snapshots, TURN, camera metrics) — a shared events page is not a camera
|
|
64
67
|
* wall — and every mutating analytics method (`deleteTracks`, `setTrackFlags`,
|
|
65
68
|
* the retrain surface, the export planes).
|
|
66
69
|
*/
|
|
67
|
-
const EVENTS_VIEW_OPEN_METHODS = new Set([
|
|
70
|
+
const EVENTS_VIEW_OPEN_METHODS = new Set([
|
|
71
|
+
'auth.me',
|
|
72
|
+
'deviceManager.listAll',
|
|
73
|
+
// Display data only: preset id + hex swatches, no device or secret.
|
|
74
|
+
'displayPalette.getPalette',
|
|
75
|
+
]);
|
|
68
76
|
/**
|
|
69
77
|
* Methods whose input carries a `deviceIds` ARRAY. The rule is INTERSECTION,
|
|
70
78
|
* and the outcome is FILTER-AND-SERVE: a request that names at least one
|
|
@@ -95,6 +103,21 @@ const EVENTS_VIEW_DEVICE_ARRAY_METHODS = new Set([
|
|
|
95
103
|
const EVENTS_VIEW_OPTIONAL_DEVICE_METHODS = new Set([
|
|
96
104
|
'pipelineAnalytics.searchObjectEvents',
|
|
97
105
|
]);
|
|
106
|
+
/**
|
|
107
|
+
* Methods that read ONE track (or its media manifest) and REQUIRE a single
|
|
108
|
+
* numeric `input.deviceId` that is inside the scope. Used by the embed's track
|
|
109
|
+
* detail page (`?mode=track`).
|
|
110
|
+
*
|
|
111
|
+
* Unlike {@link EVENTS_VIEW_OPTIONAL_DEVICE_METHODS} an absent id is DENIED
|
|
112
|
+
* (fail closed): the answer is one row, not a list the response filter could
|
|
113
|
+
* cut. The provider must return null when the stored track belongs to another
|
|
114
|
+
* device than `input.deviceId` — the gate vouches for the id the caller named,
|
|
115
|
+
* the provider vouches that it is the track's own.
|
|
116
|
+
*/
|
|
117
|
+
const EVENTS_VIEW_REQUIRED_DEVICE_METHODS = new Set([
|
|
118
|
+
'pipelineAnalytics.getTrack',
|
|
119
|
+
'pipelineAnalytics.listTrackMedia',
|
|
120
|
+
]);
|
|
98
121
|
/** Pull a `deviceIds` array off a raw tRPC input. Null when absent/malformed. */
|
|
99
122
|
function extractDeviceIds(input) {
|
|
100
123
|
if (input === null || typeof input !== 'object')
|
|
@@ -133,6 +156,16 @@ function checkEventsViewAccess(scope, path, input) {
|
|
|
133
156
|
}
|
|
134
157
|
return { ok: true };
|
|
135
158
|
}
|
|
159
|
+
if (EVENTS_VIEW_REQUIRED_DEVICE_METHODS.has(path)) {
|
|
160
|
+
const deviceId = extractDeviceId(input);
|
|
161
|
+
if (deviceId === null) {
|
|
162
|
+
return { ok: false, reason: `'${path}' requires a numeric deviceId for share-view access` };
|
|
163
|
+
}
|
|
164
|
+
if (!scope.deviceIds.includes(deviceId)) {
|
|
165
|
+
return { ok: false, reason: `Device ${deviceId} is outside this share link's scope` };
|
|
166
|
+
}
|
|
167
|
+
return { ok: true };
|
|
168
|
+
}
|
|
136
169
|
return { ok: false, reason: `'${path}' is not available to share-view tokens` };
|
|
137
170
|
}
|
|
138
171
|
function checkShareViewAccess(scope, path, input) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/server",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.665",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -49,20 +49,20 @@
|
|
|
49
49
|
]
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@camstack/addon-admin-ui": "1.2.
|
|
52
|
+
"@camstack/addon-admin-ui": "1.2.498",
|
|
53
53
|
"@camstack/addon-agent-ui": "1.2.283",
|
|
54
54
|
"@camstack/addon-auth": "1.2.170",
|
|
55
|
-
"@camstack/addon-benchmark": "1.2.
|
|
55
|
+
"@camstack/addon-benchmark": "1.2.284",
|
|
56
56
|
"@camstack/addon-decoder-nodeav": "1.2.144",
|
|
57
57
|
"@camstack/addon-notifiers": "1.2.623",
|
|
58
58
|
"@camstack/addon-pipeline": "1.2.631",
|
|
59
|
-
"@camstack/addon-pipeline-orchestrator": "1.2.
|
|
60
|
-
"@camstack/addon-post-analysis": "1.2.
|
|
59
|
+
"@camstack/addon-pipeline-orchestrator": "1.2.633",
|
|
60
|
+
"@camstack/addon-post-analysis": "1.2.645",
|
|
61
61
|
"@camstack/develop": "0.1.38",
|
|
62
62
|
"@camstack/sdk": "1.2.154",
|
|
63
|
-
"@camstack/system": "1.2.
|
|
64
|
-
"@camstack/types": "1.2.
|
|
65
|
-
"@camstack/ui-library": "1.2.
|
|
63
|
+
"@camstack/system": "1.2.502",
|
|
64
|
+
"@camstack/types": "1.2.432",
|
|
65
|
+
"@camstack/ui-library": "1.2.341",
|
|
66
66
|
"@fastify/compress": "^9.0.0",
|
|
67
67
|
"@fastify/cookie": "^11.0.2",
|
|
68
68
|
"@fastify/cors": "^11.2.0",
|