@camstack/server 1.2.72 → 1.2.73
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.
|
@@ -98,15 +98,38 @@ function parseShareToken(data) {
|
|
|
98
98
|
class ShareTokenService {
|
|
99
99
|
getStore;
|
|
100
100
|
logger;
|
|
101
|
+
/**
|
|
102
|
+
* Declaration is one-time per process; reset only if the backend itself
|
|
103
|
+
* is swapped (it never is at runtime — the getter is lazy solely because
|
|
104
|
+
* the sqlite builtin registers after this service is constructed).
|
|
105
|
+
*/
|
|
106
|
+
collectionDeclared = false;
|
|
101
107
|
constructor(getStore, logger = null) {
|
|
102
108
|
this.getStore = getStore;
|
|
103
109
|
this.logger = logger;
|
|
104
110
|
}
|
|
105
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Resolve the backend AND ensure `share_view_tokens` is declared before
|
|
113
|
+
* the first operation. The structured settings backend fail-closes on
|
|
114
|
+
* undeclared collections; this KV shape (`id` PK + `data` TEXT) is
|
|
115
|
+
* byte-identical to what the legacy on-demand path created, so existing
|
|
116
|
+
* rows keep working with no migration.
|
|
117
|
+
*/
|
|
118
|
+
async store() {
|
|
106
119
|
const store = this.getStore();
|
|
107
120
|
if (!store) {
|
|
108
121
|
throw new Error('Share tokens unavailable — settings backend not ready');
|
|
109
122
|
}
|
|
123
|
+
if (!this.collectionDeclared) {
|
|
124
|
+
await store.declareCollection({
|
|
125
|
+
collection: SHARE_TOKENS_COLLECTION,
|
|
126
|
+
columns: [
|
|
127
|
+
{ name: 'id', type: 'TEXT', primaryKey: true, notNull: true },
|
|
128
|
+
{ name: 'data', type: 'TEXT', notNull: true },
|
|
129
|
+
],
|
|
130
|
+
});
|
|
131
|
+
this.collectionDeclared = true;
|
|
132
|
+
}
|
|
110
133
|
return store;
|
|
111
134
|
}
|
|
112
135
|
/**
|
|
@@ -137,7 +160,7 @@ class ShareTokenService {
|
|
|
137
160
|
createdAt: now,
|
|
138
161
|
expiresAt: ttlSec === 'never' ? null : now + ttlSec * 1000,
|
|
139
162
|
};
|
|
140
|
-
await this.store().insert({
|
|
163
|
+
await (await this.store()).insert({
|
|
141
164
|
collection: SHARE_TOKENS_COLLECTION,
|
|
142
165
|
record: { id: record.id, data: { ...record } },
|
|
143
166
|
});
|
|
@@ -161,7 +184,7 @@ class ShareTokenService {
|
|
|
161
184
|
if (!rawToken.startsWith(exports.SHARE_TOKEN_PREFIX))
|
|
162
185
|
return null;
|
|
163
186
|
const tokenHash = crypto.createHash('sha256').update(rawToken).digest('hex');
|
|
164
|
-
const results = await this.store().query({
|
|
187
|
+
const results = await (await this.store()).query({
|
|
165
188
|
collection: SHARE_TOKENS_COLLECTION,
|
|
166
189
|
filter: { where: { tokenHash } },
|
|
167
190
|
});
|
|
@@ -186,7 +209,7 @@ class ShareTokenService {
|
|
|
186
209
|
* permission mismatch so the router surfaces a FORBIDDEN.
|
|
187
210
|
*/
|
|
188
211
|
async revoke(input) {
|
|
189
|
-
const results = await this.store().query({
|
|
212
|
+
const results = await (await this.store()).query({
|
|
190
213
|
collection: SHARE_TOKENS_COLLECTION,
|
|
191
214
|
filter: { where: { id: input.id } },
|
|
192
215
|
});
|
|
@@ -196,13 +219,13 @@ class ShareTokenService {
|
|
|
196
219
|
const record = parseShareToken(first.data);
|
|
197
220
|
if (!record) {
|
|
198
221
|
// Corrupt record — delete it regardless (it can never validate).
|
|
199
|
-
await this.store().delete({ collection: SHARE_TOKENS_COLLECTION, key: input.id });
|
|
222
|
+
await (await this.store()).delete({ collection: SHARE_TOKENS_COLLECTION, key: input.id });
|
|
200
223
|
return true;
|
|
201
224
|
}
|
|
202
225
|
if (!input.callerIsAdmin && record.userId !== input.callerUserId) {
|
|
203
226
|
throw new Error('Only the token owner or an admin can revoke a share token');
|
|
204
227
|
}
|
|
205
|
-
await this.store().delete({ collection: SHARE_TOKENS_COLLECTION, key: input.id });
|
|
228
|
+
await (await this.store()).delete({ collection: SHARE_TOKENS_COLLECTION, key: input.id });
|
|
206
229
|
this.logger?.info('Share token revoked', {
|
|
207
230
|
meta: { id: record.id, byUserId: input.callerUserId },
|
|
208
231
|
});
|
|
@@ -210,7 +233,7 @@ class ShareTokenService {
|
|
|
210
233
|
}
|
|
211
234
|
/** Every share token minted by `userId`, expired ones included. */
|
|
212
235
|
async listForUser(userId) {
|
|
213
|
-
const results = await this.store().query({
|
|
236
|
+
const results = await (await this.store()).query({
|
|
214
237
|
collection: SHARE_TOKENS_COLLECTION,
|
|
215
238
|
filter: { where: { userId } },
|
|
216
239
|
});
|
|
@@ -218,14 +241,14 @@ class ShareTokenService {
|
|
|
218
241
|
}
|
|
219
242
|
/** All share tokens (admin listing). */
|
|
220
243
|
async listAll() {
|
|
221
|
-
const results = await this.store().query({
|
|
244
|
+
const results = await (await this.store()).query({
|
|
222
245
|
collection: SHARE_TOKENS_COLLECTION,
|
|
223
246
|
filter: {},
|
|
224
247
|
});
|
|
225
248
|
return results.map((r) => parseShareToken(r.data)).filter((r) => r !== null);
|
|
226
249
|
}
|
|
227
250
|
async touchLastUsed(record) {
|
|
228
|
-
await this.store().update({
|
|
251
|
+
await (await this.store()).update({
|
|
229
252
|
collection: SHARE_TOKENS_COLLECTION,
|
|
230
253
|
id: record.id,
|
|
231
254
|
data: { ...record, lastUsedAt: Date.now() },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/server",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.73",
|
|
4
4
|
"private": false,
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -33,19 +33,19 @@
|
|
|
33
33
|
]
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@camstack/addon-admin-ui": "1.2.
|
|
36
|
+
"@camstack/addon-admin-ui": "1.2.34",
|
|
37
37
|
"@camstack/addon-agent-ui": "1.2.10",
|
|
38
38
|
"@camstack/addon-auth": "1.2.11",
|
|
39
39
|
"@camstack/addon-decoder-nodeav": "1.2.9",
|
|
40
40
|
"@camstack/addon-notifiers": "1.2.13",
|
|
41
|
-
"@camstack/addon-pipeline": "1.2.
|
|
42
|
-
"@camstack/addon-pipeline-orchestrator": "1.2.
|
|
43
|
-
"@camstack/addon-post-analysis": "1.2.
|
|
41
|
+
"@camstack/addon-pipeline": "1.2.45",
|
|
42
|
+
"@camstack/addon-pipeline-orchestrator": "1.2.28",
|
|
43
|
+
"@camstack/addon-post-analysis": "1.2.50",
|
|
44
44
|
"@camstack/sdk": "1.2.10",
|
|
45
45
|
"@camstack/shm-ring": "1.1.9",
|
|
46
|
-
"@camstack/system": "1.2.
|
|
47
|
-
"@camstack/types": "1.2.
|
|
48
|
-
"@camstack/ui-library": "1.2.
|
|
46
|
+
"@camstack/system": "1.2.60",
|
|
47
|
+
"@camstack/types": "1.2.44",
|
|
48
|
+
"@camstack/ui-library": "1.2.32",
|
|
49
49
|
"@fastify/compress": "^9.0.0",
|
|
50
50
|
"@fastify/cookie": "^11.0.2",
|
|
51
51
|
"@fastify/cors": "^11.2.0",
|