@vandenberghinc/volt 1.2.2 → 1.2.4
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/backend/dist/cjs/backend/src/database/collection.d.ts +17 -10
- package/backend/dist/cjs/backend/src/database/collection.js +289 -126
- package/backend/dist/cjs/backend/src/payments/stripe/products.js +21 -6
- package/backend/dist/cjs/backend/src/stream.d.ts +58 -6
- package/backend/dist/cjs/backend/src/stream.js +91 -12
- package/backend/dist/cjs/backend/src/users.d.ts +11 -4
- package/backend/dist/cjs/backend/src/users.js +93 -26
- package/backend/dist/esm/backend/src/database/collection.d.ts +17 -10
- package/backend/dist/esm/backend/src/database/collection.js +311 -152
- package/backend/dist/esm/backend/src/database/filters/strict_update_filter_test.js +10 -0
- package/backend/dist/esm/backend/src/payments/stripe/products.js +24 -3
- package/backend/dist/esm/backend/src/stream.d.ts +58 -6
- package/backend/dist/esm/backend/src/stream.js +96 -12
- package/backend/dist/esm/backend/src/users.d.ts +11 -4
- package/backend/dist/esm/backend/src/users.js +95 -27
- package/frontend/dist/backend/src/database/collection.d.ts +17 -10
- package/frontend/dist/backend/src/database/collection.js +311 -152
- package/frontend/dist/backend/src/payments/stripe/products.js +24 -3
- package/frontend/dist/backend/src/stream.d.ts +58 -6
- package/frontend/dist/backend/src/stream.js +96 -12
- package/frontend/dist/backend/src/users.d.ts +11 -4
- package/frontend/dist/backend/src/users.js +95 -27
- package/frontend/dist/frontend/src/modules/user.js +3 -2
- package/frontend/dist/frontend/src/ui/table.d.ts +2 -2
- package/frontend/dist/frontend/src/ui/table.js +3 -6
- package/package.json +2 -2
|
@@ -1237,31 +1237,110 @@ class Stream {
|
|
|
1237
1237
|
remove_headers(...names) {
|
|
1238
1238
|
return this.remove_header(...names);
|
|
1239
1239
|
}
|
|
1240
|
-
// Set a cookie.
|
|
1241
1240
|
/**
|
|
1242
|
-
* Set a cookie
|
|
1241
|
+
* Set a cookie to be sent with the response.
|
|
1242
|
+
*
|
|
1243
|
+
* Accepts either:
|
|
1244
|
+
* 1) a pre-built cookie header string (used as-is, no validation), or
|
|
1245
|
+
* 2) a structured object describing the cookie, from which a standards-compliant
|
|
1246
|
+
* cookie string will be generated.
|
|
1247
|
+
*
|
|
1248
|
+
* If a cookie with the same name already exists in the pending response list,
|
|
1249
|
+
* it will be replaced.
|
|
1250
|
+
*
|
|
1251
|
+
* @warning Cookies are only included in the response when using `send()`,
|
|
1252
|
+
* `success()` or `error()`.
|
|
1243
1253
|
*
|
|
1244
|
-
* @warning Will only be added to the response when the user uses `send()`, `success()` or `error()`.
|
|
1245
|
-
* @param cookie The cookie string.
|
|
1246
1254
|
* @example
|
|
1247
1255
|
* ```ts
|
|
1248
|
-
* stream.set_cookie("
|
|
1256
|
+
* stream.set_cookie("sid=abc123; Path=/; SameSite=Lax; Secure; HttpOnly");
|
|
1257
|
+
*
|
|
1258
|
+
* stream.set_cookie({
|
|
1259
|
+
* name: "sid",
|
|
1260
|
+
* value: session_id,
|
|
1261
|
+
* http_only: true,
|
|
1262
|
+
* secure: true,
|
|
1263
|
+
* same_site: "Lax",
|
|
1264
|
+
* path: "/",
|
|
1265
|
+
* max_age: 60 * 60 * 24 * 14,
|
|
1266
|
+
* });
|
|
1249
1267
|
* ```
|
|
1250
|
-
* @docs
|
|
1251
1268
|
*/
|
|
1252
1269
|
set_cookie(cookie) {
|
|
1253
|
-
cookie
|
|
1254
|
-
|
|
1270
|
+
if (typeof cookie === "string") {
|
|
1271
|
+
const cookie_str2 = cookie.trim();
|
|
1272
|
+
const name_end2 = cookie_str2.indexOf("=");
|
|
1273
|
+
if (name_end2 !== -1) {
|
|
1274
|
+
const name2 = cookie_str2.substring(0, name_end2);
|
|
1275
|
+
for (let i = 0; i < this.res_cookies.length; i++) {
|
|
1276
|
+
if (this.res_cookies[i].startsWith(name2)) {
|
|
1277
|
+
this.res_cookies[i] = cookie_str2;
|
|
1278
|
+
return this;
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
this.res_cookies.push(cookie_str2);
|
|
1283
|
+
return this;
|
|
1284
|
+
}
|
|
1285
|
+
const { name, value, path = "/", domain, max_age, expires, secure, http_only, same_site, prefix, extra } = cookie;
|
|
1286
|
+
if (!name || typeof name !== "string") {
|
|
1287
|
+
throw new Error("set_cookie: cookie.name must be a non-empty string");
|
|
1288
|
+
}
|
|
1289
|
+
const full_name = `${prefix ?? ""}${name}`;
|
|
1290
|
+
if (prefix === "__Host-") {
|
|
1291
|
+
if (domain) {
|
|
1292
|
+
throw new Error("__Host- cookies must not include a domain attribute");
|
|
1293
|
+
}
|
|
1294
|
+
if (path !== "/") {
|
|
1295
|
+
throw new Error("__Host- cookies must have path='/'");
|
|
1296
|
+
}
|
|
1297
|
+
if (!secure) {
|
|
1298
|
+
throw new Error("__Host- cookies require secure=true");
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
if (prefix === "__Secure-" && !secure) {
|
|
1302
|
+
throw new Error("__Secure- cookies require secure=true");
|
|
1303
|
+
}
|
|
1304
|
+
const encoded_value = value === null || typeof value === "undefined" ? "" : encodeURIComponent(String(value));
|
|
1305
|
+
const parts = [];
|
|
1306
|
+
parts.push(`${full_name}=${encoded_value}`);
|
|
1307
|
+
if (path)
|
|
1308
|
+
parts.push(`Path=${path}`);
|
|
1309
|
+
if (domain)
|
|
1310
|
+
parts.push(`Domain=${domain}`);
|
|
1311
|
+
if (typeof max_age === "number" && Number.isFinite(max_age)) {
|
|
1312
|
+
parts.push(`Max-Age=${Math.trunc(max_age)}`);
|
|
1313
|
+
}
|
|
1314
|
+
if (expires) {
|
|
1315
|
+
const exp = expires instanceof Date ? expires.toUTCString() : String(expires).trim();
|
|
1316
|
+
if (exp)
|
|
1317
|
+
parts.push(`Expires=${exp}`);
|
|
1318
|
+
}
|
|
1319
|
+
if (secure)
|
|
1320
|
+
parts.push("Secure");
|
|
1321
|
+
if (http_only)
|
|
1322
|
+
parts.push("HttpOnly");
|
|
1323
|
+
if (same_site)
|
|
1324
|
+
parts.push(`SameSite=${same_site}`);
|
|
1325
|
+
if (extra && Array.isArray(extra)) {
|
|
1326
|
+
for (const attr of extra) {
|
|
1327
|
+
const trimmed = String(attr).trim();
|
|
1328
|
+
if (trimmed)
|
|
1329
|
+
parts.push(trimmed);
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
const cookie_str = parts.join("; ");
|
|
1333
|
+
const name_end = cookie_str.indexOf("=");
|
|
1255
1334
|
if (name_end !== -1) {
|
|
1256
|
-
const
|
|
1335
|
+
const existing_name = cookie_str.substring(0, name_end);
|
|
1257
1336
|
for (let i = 0; i < this.res_cookies.length; i++) {
|
|
1258
|
-
if (this.res_cookies[i].startsWith(
|
|
1259
|
-
this.res_cookies[i] =
|
|
1337
|
+
if (this.res_cookies[i].startsWith(existing_name)) {
|
|
1338
|
+
this.res_cookies[i] = cookie_str;
|
|
1260
1339
|
return this;
|
|
1261
1340
|
}
|
|
1262
1341
|
}
|
|
1263
1342
|
}
|
|
1264
|
-
this.res_cookies.push(
|
|
1343
|
+
this.res_cookies.push(cookie_str);
|
|
1265
1344
|
return this;
|
|
1266
1345
|
}
|
|
1267
1346
|
// Set cookies.
|
|
@@ -259,24 +259,31 @@ export declare class Users {
|
|
|
259
259
|
}): Promise<void>;
|
|
260
260
|
/**
|
|
261
261
|
* Create the auth token cookie on the response.
|
|
262
|
+
* `T` is treated as a real authentication credential.
|
|
263
|
+
*
|
|
262
264
|
* @param stream The request stream.
|
|
263
265
|
* @param token The token string or Token object.
|
|
264
266
|
*/
|
|
265
267
|
_create_token_cookie(stream: Stream, token: string | User.Token): void;
|
|
266
268
|
/**
|
|
267
|
-
* Create user cookies (
|
|
269
|
+
* Create user cookies (ID and activation flag).
|
|
270
|
+
* These are user-state cookies, NOT auth credentials.
|
|
271
|
+
*
|
|
268
272
|
* @param stream The request stream.
|
|
269
273
|
* @param uid The user ID, or invalid to clear.
|
|
270
274
|
*/
|
|
271
|
-
_create_user_cookie(stream: Stream, uid: string): Promise<void>;
|
|
275
|
+
_create_user_cookie(stream: Stream, uid: string | null): Promise<void>;
|
|
272
276
|
/**
|
|
273
|
-
* Create non-
|
|
277
|
+
* Create non-HttpOnly cookies with detailed user info for frontend usage.
|
|
278
|
+
* These are UI convenience cookies only.
|
|
279
|
+
*
|
|
274
280
|
* @param stream The request stream.
|
|
275
281
|
* @param uid The user ID.
|
|
276
282
|
*/
|
|
277
283
|
_create_detailed_user_cookie(stream: Stream, uid: string): Promise<void>;
|
|
278
284
|
/**
|
|
279
|
-
* Clear all default auth
|
|
285
|
+
* Clear all default auth and user-related cookies.
|
|
286
|
+
*
|
|
280
287
|
* @param stream The request stream.
|
|
281
288
|
*/
|
|
282
289
|
_reset_cookies(stream: Stream): void;
|
|
@@ -428,60 +428,127 @@ class Users {
|
|
|
428
428
|
// ---------------------------------------------------------
|
|
429
429
|
/**
|
|
430
430
|
* Create the auth token cookie on the response.
|
|
431
|
+
* `T` is treated as a real authentication credential.
|
|
432
|
+
*
|
|
431
433
|
* @param stream The request stream.
|
|
432
434
|
* @param token The token string or Token object.
|
|
433
435
|
*/
|
|
434
436
|
_create_token_cookie(stream, token) {
|
|
435
437
|
stream.set_header("Cache-Control", "max-age=0, no-cache, no-store, must-revalidate, proxy-revalidate");
|
|
436
438
|
stream.set_header("Access-Control-Allow-Credentials", "true");
|
|
437
|
-
|
|
438
|
-
token = token.token;
|
|
439
|
-
}
|
|
439
|
+
const token_value = typeof token === "object" ? token.token : token;
|
|
440
440
|
const max_age = this.token_expiration;
|
|
441
|
-
|
|
442
|
-
|
|
441
|
+
stream.set_cookie({
|
|
442
|
+
name: "T",
|
|
443
|
+
value: token_value,
|
|
444
|
+
path: "/",
|
|
445
|
+
max_age,
|
|
446
|
+
secure: true,
|
|
447
|
+
http_only: true,
|
|
448
|
+
same_site: "Lax"
|
|
449
|
+
// REQUIRED for Stripe success/cancel redirects
|
|
450
|
+
});
|
|
443
451
|
}
|
|
444
452
|
/**
|
|
445
|
-
* Create user cookies (
|
|
453
|
+
* Create user cookies (ID and activation flag).
|
|
454
|
+
* These are user-state cookies, NOT auth credentials.
|
|
455
|
+
*
|
|
446
456
|
* @param stream The request stream.
|
|
447
457
|
* @param uid The user ID, or invalid to clear.
|
|
448
458
|
*/
|
|
449
459
|
async _create_user_cookie(stream, uid) {
|
|
450
|
-
if (typeof uid === "string") {
|
|
451
|
-
stream.set_cookie(
|
|
460
|
+
if (typeof uid === "string" && uid.length > 0) {
|
|
461
|
+
stream.set_cookie({
|
|
462
|
+
name: "UserID",
|
|
463
|
+
value: uid,
|
|
464
|
+
path: "/",
|
|
465
|
+
secure: true,
|
|
466
|
+
same_site: "Lax"
|
|
467
|
+
});
|
|
452
468
|
const is_activated = this.enable_account_activation ? await this.is_activated(uid) : true;
|
|
453
|
-
stream.set_cookie(
|
|
469
|
+
stream.set_cookie({
|
|
470
|
+
name: "UserActivated",
|
|
471
|
+
value: is_activated ? "1" : "0",
|
|
472
|
+
path: "/",
|
|
473
|
+
secure: true,
|
|
474
|
+
same_site: "Lax"
|
|
475
|
+
});
|
|
454
476
|
} else {
|
|
455
|
-
stream.set_cookie(
|
|
456
|
-
|
|
457
|
-
|
|
477
|
+
stream.set_cookie({
|
|
478
|
+
name: "UserID",
|
|
479
|
+
value: "",
|
|
480
|
+
path: "/",
|
|
481
|
+
max_age: 0,
|
|
482
|
+
secure: true,
|
|
483
|
+
same_site: "Lax"
|
|
484
|
+
});
|
|
485
|
+
stream.set_cookie({
|
|
486
|
+
name: "UserActivated",
|
|
487
|
+
value: "0",
|
|
488
|
+
path: "/",
|
|
489
|
+
max_age: 0,
|
|
490
|
+
secure: true,
|
|
491
|
+
same_site: "Lax"
|
|
492
|
+
});
|
|
458
493
|
}
|
|
459
494
|
}
|
|
460
495
|
/**
|
|
461
|
-
* Create non-
|
|
496
|
+
* Create non-HttpOnly cookies with detailed user info for frontend usage.
|
|
497
|
+
* These are UI convenience cookies only.
|
|
498
|
+
*
|
|
462
499
|
* @param stream The request stream.
|
|
463
500
|
* @param uid The user ID.
|
|
464
501
|
*/
|
|
465
502
|
async _create_detailed_user_cookie(stream, uid) {
|
|
466
503
|
const user = await this.get(uid);
|
|
467
|
-
stream.set_cookie(
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
504
|
+
stream.set_cookie({
|
|
505
|
+
name: "UserName",
|
|
506
|
+
value: user.username ?? "",
|
|
507
|
+
path: "/",
|
|
508
|
+
secure: true,
|
|
509
|
+
same_site: "Lax"
|
|
510
|
+
});
|
|
511
|
+
stream.set_cookie({
|
|
512
|
+
name: "UserFirstName",
|
|
513
|
+
value: user.first_name ?? "",
|
|
514
|
+
path: "/",
|
|
515
|
+
secure: true,
|
|
516
|
+
same_site: "Lax"
|
|
517
|
+
});
|
|
518
|
+
stream.set_cookie({
|
|
519
|
+
name: "UserLastName",
|
|
520
|
+
value: user.last_name ?? "",
|
|
521
|
+
path: "/",
|
|
522
|
+
secure: true,
|
|
523
|
+
same_site: "Lax"
|
|
524
|
+
});
|
|
525
|
+
stream.set_cookie({
|
|
526
|
+
name: "UserEmail",
|
|
527
|
+
value: user.email ?? "",
|
|
528
|
+
path: "/",
|
|
529
|
+
secure: true,
|
|
530
|
+
same_site: "Lax"
|
|
531
|
+
});
|
|
471
532
|
}
|
|
472
533
|
/**
|
|
473
|
-
* Clear all default auth
|
|
534
|
+
* Clear all default auth and user-related cookies.
|
|
535
|
+
*
|
|
474
536
|
* @param stream The request stream.
|
|
475
537
|
*/
|
|
476
538
|
_reset_cookies(stream) {
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
stream.set_cookie(
|
|
484
|
-
stream.set_cookie(
|
|
539
|
+
const clear = {
|
|
540
|
+
path: "/",
|
|
541
|
+
max_age: 0,
|
|
542
|
+
secure: true,
|
|
543
|
+
same_site: "Lax"
|
|
544
|
+
};
|
|
545
|
+
stream.set_cookie({ name: "T", value: "", http_only: true, ...clear });
|
|
546
|
+
stream.set_cookie({ name: "UserID", value: "", ...clear });
|
|
547
|
+
stream.set_cookie({ name: "UserActivated", value: "", ...clear });
|
|
548
|
+
stream.set_cookie({ name: "UserName", value: "", ...clear });
|
|
549
|
+
stream.set_cookie({ name: "UserFirstName", value: "", ...clear });
|
|
550
|
+
stream.set_cookie({ name: "UserLastName", value: "", ...clear });
|
|
551
|
+
stream.set_cookie({ name: "UserEmail", value: "", ...clear });
|
|
485
552
|
}
|
|
486
553
|
// ---------------------------------------------------------
|
|
487
554
|
// 2FA mail.
|
|
@@ -159,6 +159,23 @@ export declare class Collection<Data extends mongodb.Document = mongodb.Document
|
|
|
159
159
|
* - Plain object without '$' keys → NOT valid for updateOne/findOneAndUpdate.
|
|
160
160
|
*/
|
|
161
161
|
private _is_operator_update_or_pipeline;
|
|
162
|
+
private _index_key_signature;
|
|
163
|
+
private _keys_equal;
|
|
164
|
+
private _normalize_index_opts;
|
|
165
|
+
/**
|
|
166
|
+
* Drop all indexes that are NOT part of this._init_indexes, excluding _id_ (and TTL index if enabled).
|
|
167
|
+
*
|
|
168
|
+
* @note We match by key pattern rather than name because names can differ.
|
|
169
|
+
*/
|
|
170
|
+
private _drop_non_init_indexes;
|
|
171
|
+
/**
|
|
172
|
+
* Creates indexes on collections.
|
|
173
|
+
*
|
|
174
|
+
* @note When transaction mode is enabled, the session option will not be used.
|
|
175
|
+
*
|
|
176
|
+
* @param opts The index create options.
|
|
177
|
+
*/
|
|
178
|
+
private _create_index;
|
|
162
179
|
/**
|
|
163
180
|
* Initialize the collection, creating indexes and setting up TTL if needed.
|
|
164
181
|
* @returns The initialized collection instance.
|
|
@@ -215,16 +232,6 @@ export declare class Collection<Data extends mongodb.Document = mongodb.Document
|
|
|
215
232
|
* @docs
|
|
216
233
|
*/
|
|
217
234
|
has_index(index: string): Promise<boolean>;
|
|
218
|
-
/**
|
|
219
|
-
* Creates indexes on collections.
|
|
220
|
-
*
|
|
221
|
-
* @note When transaction mode is enabled, the session option will not be used.
|
|
222
|
-
*
|
|
223
|
-
* @param opts The index create options.
|
|
224
|
-
*
|
|
225
|
-
* @docs
|
|
226
|
-
*/
|
|
227
|
-
create_index(opts: string | Collection.IndexOpts): Promise<string>;
|
|
228
235
|
/**
|
|
229
236
|
* Standalone helper: merge `source` into `target` for missing keys only.
|
|
230
237
|
* Clones assigned nested objects/arrays/dates once (when `clone` is true).
|