@dyrected/core 2.5.44 → 2.5.46
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/LICENSE.md +1 -1
- package/dist/index.cjs +95 -86
- package/dist/index.js +10 -16
- package/dist/server.cjs +339 -411
- package/dist/server.js +235 -312
- package/package.json +1 -1
package/dist/server.cjs
CHANGED
|
@@ -30,7 +30,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
30
30
|
));
|
|
31
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
32
|
|
|
33
|
-
// src/utils/where-sanitizer.
|
|
33
|
+
// src/utils/where-sanitizer.ts
|
|
34
34
|
var where_sanitizer_exports = {};
|
|
35
35
|
__export(where_sanitizer_exports, {
|
|
36
36
|
sanitizeWhereClause: () => sanitizeWhereClause
|
|
@@ -67,12 +67,9 @@ function sanitizeWhereClause(where, fields) {
|
|
|
67
67
|
continue;
|
|
68
68
|
}
|
|
69
69
|
const fieldDef = fieldMap.get(key);
|
|
70
|
-
if (!fieldDef)
|
|
71
|
-
|
|
72
|
-
if (fieldDef.
|
|
73
|
-
continue;
|
|
74
|
-
if (NEVER_FILTERABLE_TYPES.includes(fieldDef.type))
|
|
75
|
-
continue;
|
|
70
|
+
if (!fieldDef) continue;
|
|
71
|
+
if (fieldDef.admin?.filterable === false) continue;
|
|
72
|
+
if (NEVER_FILTERABLE_TYPES.includes(fieldDef.type)) continue;
|
|
76
73
|
result[key] = value;
|
|
77
74
|
}
|
|
78
75
|
return result;
|
|
@@ -81,7 +78,7 @@ function sanitizeWhereClause(where, fields) {
|
|
|
81
78
|
}
|
|
82
79
|
var NEVER_FILTERABLE_TYPES;
|
|
83
80
|
var init_where_sanitizer = __esm({
|
|
84
|
-
"src/utils/where-sanitizer.
|
|
81
|
+
"src/utils/where-sanitizer.ts"() {
|
|
85
82
|
"use strict";
|
|
86
83
|
NEVER_FILTERABLE_TYPES = [
|
|
87
84
|
"password",
|
|
@@ -108,12 +105,12 @@ __export(server_exports, {
|
|
|
108
105
|
});
|
|
109
106
|
module.exports = __toCommonJS(server_exports);
|
|
110
107
|
|
|
111
|
-
// src/app.
|
|
108
|
+
// src/app.ts
|
|
112
109
|
var import_hono = require("hono");
|
|
113
110
|
var import_cors = require("hono/cors");
|
|
114
111
|
var import_request_id = require("hono/request-id");
|
|
115
112
|
|
|
116
|
-
// src/services/defaults.service.
|
|
113
|
+
// src/services/defaults.service.ts
|
|
117
114
|
var DefaultsService = class {
|
|
118
115
|
/**
|
|
119
116
|
* Recursively apply default values to a data object based on field definitions.
|
|
@@ -121,14 +118,12 @@ var DefaultsService = class {
|
|
|
121
118
|
static apply(fields, data = {}) {
|
|
122
119
|
const result = { ...data || {} };
|
|
123
120
|
fields.forEach((field) => {
|
|
124
|
-
if (field.type === "join")
|
|
125
|
-
return;
|
|
121
|
+
if (field.type === "join") return;
|
|
126
122
|
if (field.type === "row" && field.fields) {
|
|
127
123
|
Object.assign(result, this.apply(field.fields, data));
|
|
128
124
|
return;
|
|
129
125
|
}
|
|
130
|
-
if (!field.name)
|
|
131
|
-
return;
|
|
126
|
+
if (!field.name) return;
|
|
132
127
|
let value = result[field.name];
|
|
133
128
|
if ((value === void 0 || value === null) && field.renameTo) {
|
|
134
129
|
const legacyValue = result[field.renameTo];
|
|
@@ -141,12 +136,9 @@ var DefaultsService = class {
|
|
|
141
136
|
if (field.defaultValue !== void 0) {
|
|
142
137
|
result[field.name] = field.defaultValue;
|
|
143
138
|
} else {
|
|
144
|
-
if (field.type === "boolean")
|
|
145
|
-
|
|
146
|
-
else if (field.type === "
|
|
147
|
-
result[field.name] = [];
|
|
148
|
-
else if (field.type === "multiSelect")
|
|
149
|
-
result[field.name] = [];
|
|
139
|
+
if (field.type === "boolean") result[field.name] = false;
|
|
140
|
+
else if (field.type === "array") result[field.name] = [];
|
|
141
|
+
else if (field.type === "multiSelect") result[field.name] = [];
|
|
150
142
|
else if (field.type === "object") {
|
|
151
143
|
result[field.name] = this.apply(field.fields || [], {});
|
|
152
144
|
}
|
|
@@ -161,7 +153,7 @@ var DefaultsService = class {
|
|
|
161
153
|
}
|
|
162
154
|
};
|
|
163
155
|
|
|
164
|
-
// src/services/population.service.
|
|
156
|
+
// src/services/population.service.ts
|
|
165
157
|
var PopulationService = class {
|
|
166
158
|
db;
|
|
167
159
|
collections;
|
|
@@ -214,31 +206,29 @@ var PopulationService = class {
|
|
|
214
206
|
Object.assign(populatedDoc, rowPopulated);
|
|
215
207
|
continue;
|
|
216
208
|
}
|
|
217
|
-
if (!field.name)
|
|
218
|
-
continue;
|
|
209
|
+
if (!field.name) continue;
|
|
219
210
|
const value = populatedDoc[field.name];
|
|
220
211
|
if (field.type === "relationship" && field.relationTo && value) {
|
|
221
212
|
const relatedCollection = this.collections.find((c) => c.slug === field.relationTo);
|
|
222
|
-
if (!relatedCollection)
|
|
223
|
-
continue;
|
|
213
|
+
if (!relatedCollection) continue;
|
|
224
214
|
if (Array.isArray(value)) {
|
|
225
|
-
populatedDoc[field.name] = await Promise.all(
|
|
226
|
-
|
|
227
|
-
return id;
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
})
|
|
241
|
-
|
|
215
|
+
populatedDoc[field.name] = await Promise.all(
|
|
216
|
+
value.map(async (id) => {
|
|
217
|
+
if (!id) return id;
|
|
218
|
+
let doc = id;
|
|
219
|
+
if (typeof id === "string") {
|
|
220
|
+
doc = await this.db.findOne({ collection: field.relationTo, id });
|
|
221
|
+
}
|
|
222
|
+
if (!doc || typeof doc !== "object") return id;
|
|
223
|
+
const docWithDefaults = DefaultsService.apply(relatedCollection.fields, doc);
|
|
224
|
+
return this.populate({
|
|
225
|
+
data: docWithDefaults,
|
|
226
|
+
fields: relatedCollection.fields,
|
|
227
|
+
currentDepth: currentDepth + 1,
|
|
228
|
+
maxDepth
|
|
229
|
+
});
|
|
230
|
+
})
|
|
231
|
+
);
|
|
242
232
|
} else if (value) {
|
|
243
233
|
let doc = value;
|
|
244
234
|
if (typeof value === "string") {
|
|
@@ -287,17 +277,18 @@ var PopulationService = class {
|
|
|
287
277
|
});
|
|
288
278
|
}
|
|
289
279
|
if (field.type === "blocks" && field.blocks && Array.isArray(value)) {
|
|
290
|
-
populatedDoc[field.name] = await Promise.all(
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
return blockData;
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
280
|
+
populatedDoc[field.name] = await Promise.all(
|
|
281
|
+
value.map(async (blockData) => {
|
|
282
|
+
const blockConfig = field.blocks.find((b) => b.slug === blockData.blockType);
|
|
283
|
+
if (!blockConfig) return blockData;
|
|
284
|
+
return this.populate({
|
|
285
|
+
data: blockData,
|
|
286
|
+
fields: blockConfig.fields,
|
|
287
|
+
currentDepth,
|
|
288
|
+
maxDepth
|
|
289
|
+
});
|
|
290
|
+
})
|
|
291
|
+
);
|
|
301
292
|
}
|
|
302
293
|
}
|
|
303
294
|
return populatedDoc;
|
|
@@ -306,8 +297,7 @@ var PopulationService = class {
|
|
|
306
297
|
* Helper to populate a PaginatedResult
|
|
307
298
|
*/
|
|
308
299
|
async populateResult(result, fields, maxDepth) {
|
|
309
|
-
if (maxDepth <= 0)
|
|
310
|
-
return result;
|
|
300
|
+
if (maxDepth <= 0) return result;
|
|
311
301
|
const populatedDocs = await this.populate({
|
|
312
302
|
data: result.docs,
|
|
313
303
|
fields,
|
|
@@ -321,7 +311,7 @@ var PopulationService = class {
|
|
|
321
311
|
}
|
|
322
312
|
};
|
|
323
313
|
|
|
324
|
-
// src/services/audit.service.
|
|
314
|
+
// src/services/audit.service.ts
|
|
325
315
|
var AuditService = class {
|
|
326
316
|
/**
|
|
327
317
|
* Writes a single entry to the __audit collection.
|
|
@@ -349,7 +339,7 @@ var AuditService = class {
|
|
|
349
339
|
}
|
|
350
340
|
};
|
|
351
341
|
|
|
352
|
-
// src/auth/password.
|
|
342
|
+
// src/auth/password.ts
|
|
353
343
|
var import_node_util = require("util");
|
|
354
344
|
var import_node_crypto = require("crypto");
|
|
355
345
|
var scryptAsync = (0, import_node_util.promisify)(import_node_crypto.scrypt);
|
|
@@ -362,16 +352,14 @@ async function hashPassword(plain) {
|
|
|
362
352
|
}
|
|
363
353
|
async function verifyPassword(plain, stored) {
|
|
364
354
|
const [salt, storedHash] = stored.split(":");
|
|
365
|
-
if (!salt || !storedHash)
|
|
366
|
-
return false;
|
|
355
|
+
if (!salt || !storedHash) return false;
|
|
367
356
|
const derivedKey = await scryptAsync(plain, salt, KEY_LEN);
|
|
368
357
|
const storedBuffer = Buffer.from(storedHash, "hex");
|
|
369
|
-
if (derivedKey.length !== storedBuffer.length)
|
|
370
|
-
return false;
|
|
358
|
+
if (derivedKey.length !== storedBuffer.length) return false;
|
|
371
359
|
return (0, import_node_crypto.timingSafeEqual)(derivedKey, storedBuffer);
|
|
372
360
|
}
|
|
373
361
|
|
|
374
|
-
// src/utils/hooks.
|
|
362
|
+
// src/utils/hooks.ts
|
|
375
363
|
async function runCollectionHooks(hooks, args, options = {}) {
|
|
376
364
|
if (!hooks || hooks.length === 0) {
|
|
377
365
|
return args.data ?? args.doc ?? void 0;
|
|
@@ -398,12 +386,10 @@ async function runCollectionHooks(hooks, args, options = {}) {
|
|
|
398
386
|
return currentPayload;
|
|
399
387
|
}
|
|
400
388
|
async function executeFieldBeforeChange(fields, data, originalDoc, user, db) {
|
|
401
|
-
if (!data || typeof data !== "object")
|
|
402
|
-
return data;
|
|
389
|
+
if (!data || typeof data !== "object") return data;
|
|
403
390
|
const result = { ...data };
|
|
404
391
|
for (const field of fields) {
|
|
405
|
-
if (!field.name)
|
|
406
|
-
continue;
|
|
392
|
+
if (!field.name) continue;
|
|
407
393
|
const value = result[field.name];
|
|
408
394
|
const origValue = originalDoc?.[field.name];
|
|
409
395
|
let updatedValue = value;
|
|
@@ -423,13 +409,21 @@ async function executeFieldBeforeChange(fields, data, originalDoc, user, db) {
|
|
|
423
409
|
}
|
|
424
410
|
if (updatedValue !== void 0 && updatedValue !== null) {
|
|
425
411
|
if (field.type === "object" && field.fields) {
|
|
426
|
-
result[field.name] = await executeFieldBeforeChange(
|
|
412
|
+
result[field.name] = await executeFieldBeforeChange(
|
|
413
|
+
field.fields,
|
|
414
|
+
updatedValue,
|
|
415
|
+
origValue,
|
|
416
|
+
user,
|
|
417
|
+
db
|
|
418
|
+
);
|
|
427
419
|
} else if (field.type === "array" && field.fields && Array.isArray(updatedValue)) {
|
|
428
420
|
const arrayResult = [];
|
|
429
421
|
for (let i = 0; i < updatedValue.length; i++) {
|
|
430
422
|
const item = updatedValue[i];
|
|
431
423
|
const origItem = Array.isArray(origValue) ? origValue[i] : null;
|
|
432
|
-
arrayResult.push(
|
|
424
|
+
arrayResult.push(
|
|
425
|
+
await executeFieldBeforeChange(field.fields, item, origItem, user, db)
|
|
426
|
+
);
|
|
433
427
|
}
|
|
434
428
|
result[field.name] = arrayResult;
|
|
435
429
|
} else if (field.type === "blocks" && field.blocks && Array.isArray(updatedValue)) {
|
|
@@ -437,9 +431,19 @@ async function executeFieldBeforeChange(fields, data, originalDoc, user, db) {
|
|
|
437
431
|
for (let i = 0; i < updatedValue.length; i++) {
|
|
438
432
|
const blockData = updatedValue[i];
|
|
439
433
|
const origBlock = Array.isArray(origValue) ? origValue[i] : null;
|
|
440
|
-
const blockConfig = field.blocks.find(
|
|
434
|
+
const blockConfig = field.blocks.find(
|
|
435
|
+
(b) => b.slug === blockData.blockType
|
|
436
|
+
);
|
|
441
437
|
if (blockConfig) {
|
|
442
|
-
blocksResult.push(
|
|
438
|
+
blocksResult.push(
|
|
439
|
+
await executeFieldBeforeChange(
|
|
440
|
+
blockConfig.fields,
|
|
441
|
+
blockData,
|
|
442
|
+
origBlock,
|
|
443
|
+
user,
|
|
444
|
+
db
|
|
445
|
+
)
|
|
446
|
+
);
|
|
443
447
|
} else {
|
|
444
448
|
blocksResult.push(blockData);
|
|
445
449
|
}
|
|
@@ -451,12 +455,10 @@ async function executeFieldBeforeChange(fields, data, originalDoc, user, db) {
|
|
|
451
455
|
return result;
|
|
452
456
|
}
|
|
453
457
|
async function executeFieldAfterRead(fields, doc, user, db) {
|
|
454
|
-
if (!doc || typeof doc !== "object")
|
|
455
|
-
return doc;
|
|
458
|
+
if (!doc || typeof doc !== "object") return doc;
|
|
456
459
|
const result = { ...doc };
|
|
457
460
|
for (const field of fields) {
|
|
458
|
-
if (!field.name)
|
|
459
|
-
continue;
|
|
461
|
+
if (!field.name) continue;
|
|
460
462
|
const value = result[field.name];
|
|
461
463
|
let updatedValue = value;
|
|
462
464
|
if (field.hooks?.afterRead) {
|
|
@@ -474,20 +476,41 @@ async function executeFieldAfterRead(fields, doc, user, db) {
|
|
|
474
476
|
}
|
|
475
477
|
if (updatedValue !== void 0 && updatedValue !== null) {
|
|
476
478
|
if (field.type === "object" && field.fields) {
|
|
477
|
-
result[field.name] = await executeFieldAfterRead(
|
|
479
|
+
result[field.name] = await executeFieldAfterRead(
|
|
480
|
+
field.fields,
|
|
481
|
+
updatedValue,
|
|
482
|
+
user,
|
|
483
|
+
db
|
|
484
|
+
);
|
|
478
485
|
} else if (field.type === "array" && field.fields && Array.isArray(updatedValue)) {
|
|
479
486
|
const arrayResult = [];
|
|
480
487
|
for (const item of updatedValue) {
|
|
481
|
-
arrayResult.push(
|
|
488
|
+
arrayResult.push(
|
|
489
|
+
await executeFieldAfterRead(
|
|
490
|
+
field.fields,
|
|
491
|
+
item,
|
|
492
|
+
user,
|
|
493
|
+
db
|
|
494
|
+
)
|
|
495
|
+
);
|
|
482
496
|
}
|
|
483
497
|
result[field.name] = arrayResult;
|
|
484
498
|
} else if (field.type === "blocks" && field.blocks && Array.isArray(updatedValue)) {
|
|
485
499
|
const blocksResult = [];
|
|
486
500
|
for (const blockData of updatedValue) {
|
|
487
501
|
const typedBlock = blockData;
|
|
488
|
-
const blockConfig = field.blocks.find(
|
|
502
|
+
const blockConfig = field.blocks.find(
|
|
503
|
+
(b) => b.slug === typedBlock.blockType
|
|
504
|
+
);
|
|
489
505
|
if (blockConfig) {
|
|
490
|
-
blocksResult.push(
|
|
506
|
+
blocksResult.push(
|
|
507
|
+
await executeFieldAfterRead(
|
|
508
|
+
blockConfig.fields,
|
|
509
|
+
typedBlock,
|
|
510
|
+
user,
|
|
511
|
+
db
|
|
512
|
+
)
|
|
513
|
+
);
|
|
491
514
|
} else {
|
|
492
515
|
blocksResult.push(blockData);
|
|
493
516
|
}
|
|
@@ -499,14 +522,16 @@ async function executeFieldAfterRead(fields, doc, user, db) {
|
|
|
499
522
|
return result;
|
|
500
523
|
}
|
|
501
524
|
|
|
502
|
-
// src/utils/readonly-db.
|
|
525
|
+
// src/utils/readonly-db.ts
|
|
503
526
|
var WRITE_METHODS = ["create", "update", "delete", "updateGlobal", "execute"];
|
|
504
527
|
function createReadonlyDb(db) {
|
|
505
528
|
return new Proxy(db, {
|
|
506
529
|
get(target, prop) {
|
|
507
530
|
if (WRITE_METHODS.includes(prop)) {
|
|
508
531
|
return () => {
|
|
509
|
-
throw new Error(
|
|
532
|
+
throw new Error(
|
|
533
|
+
`[dyrected] Write operation "${String(prop)}" is not allowed in this hook phase. Use afterChange/afterDelete hooks for write operations.`
|
|
534
|
+
);
|
|
510
535
|
};
|
|
511
536
|
}
|
|
512
537
|
return Reflect.get(target, prop);
|
|
@@ -514,13 +539,11 @@ function createReadonlyDb(db) {
|
|
|
514
539
|
});
|
|
515
540
|
}
|
|
516
541
|
|
|
517
|
-
// src/auth/jexl.
|
|
542
|
+
// src/auth/jexl.ts
|
|
518
543
|
var import_jexl = __toESM(require("jexl"), 1);
|
|
519
544
|
async function evaluateAccess(expression, context) {
|
|
520
|
-
if (expression === void 0 || expression === null)
|
|
521
|
-
|
|
522
|
-
if (typeof expression === "boolean")
|
|
523
|
-
return expression;
|
|
545
|
+
if (expression === void 0 || expression === null) return false;
|
|
546
|
+
if (typeof expression === "boolean") return expression;
|
|
524
547
|
try {
|
|
525
548
|
const result = await import_jexl.default.eval(expression, context);
|
|
526
549
|
return !!result;
|
|
@@ -530,7 +553,35 @@ async function evaluateAccess(expression, context) {
|
|
|
530
553
|
}
|
|
531
554
|
}
|
|
532
555
|
|
|
533
|
-
// src/
|
|
556
|
+
// src/utils/admin-auth.ts
|
|
557
|
+
function getAdminAuthCollection(config) {
|
|
558
|
+
const requestedSlug = config.adminAuth?.collectionSlug;
|
|
559
|
+
if (requestedSlug) {
|
|
560
|
+
return config.collections.find((collection) => collection.slug === requestedSlug) ?? null;
|
|
561
|
+
}
|
|
562
|
+
return config.collections.find((collection) => collection.slug === "__admins") ?? config.collections.find((collection) => collection.auth) ?? null;
|
|
563
|
+
}
|
|
564
|
+
function getPublicAdminAuthConfig(adminAuth) {
|
|
565
|
+
const providers = (adminAuth?.providers ?? []).map((provider) => ({
|
|
566
|
+
id: provider.id,
|
|
567
|
+
type: provider.type,
|
|
568
|
+
displayName: provider.displayName || humanizeProviderName(provider.id, provider.type),
|
|
569
|
+
autoRedirect: provider.autoRedirect
|
|
570
|
+
}));
|
|
571
|
+
return {
|
|
572
|
+
mode: adminAuth?.mode ?? "local",
|
|
573
|
+
collectionSlug: adminAuth?.collectionSlug,
|
|
574
|
+
provisioningMode: adminAuth?.provisioningMode,
|
|
575
|
+
providers
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
function humanizeProviderName(id, type) {
|
|
579
|
+
const cleaned = id.replace(/[-_]+/g, " ").trim();
|
|
580
|
+
if (!cleaned) return type.toUpperCase();
|
|
581
|
+
return cleaned.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// src/workflows.ts
|
|
534
585
|
var WORKFLOW_HISTORY_COLLECTION = "__workflow_history";
|
|
535
586
|
var LIFECYCLE_EVENTS_COLLECTION = "__lifecycle_events";
|
|
536
587
|
function publicMetadata(meta) {
|
|
@@ -541,18 +592,17 @@ function workflowCapabilities(workflow, user) {
|
|
|
541
592
|
const capabilities = new Set(Array.isArray(user?.capabilities) ? user.capabilities : []);
|
|
542
593
|
const roles = Array.isArray(user?.roles) ? user.roles : [];
|
|
543
594
|
for (const mapping of workflow.roles ?? []) {
|
|
544
|
-
if (roles.includes(mapping.role))
|
|
545
|
-
mapping.capabilities.forEach((capability) => capabilities.add(capability));
|
|
595
|
+
if (roles.includes(mapping.role)) mapping.capabilities.forEach((capability) => capabilities.add(capability));
|
|
546
596
|
}
|
|
547
597
|
return capabilities;
|
|
548
598
|
}
|
|
549
599
|
function canViewWorkflowDraft(workflow, user) {
|
|
550
|
-
if (!user)
|
|
551
|
-
return false;
|
|
600
|
+
if (!user) return false;
|
|
552
601
|
const capabilities = workflowCapabilities(workflow, user);
|
|
553
|
-
if (capabilities.has("entry.edit"))
|
|
554
|
-
|
|
555
|
-
|
|
602
|
+
if (capabilities.has("entry.edit")) return true;
|
|
603
|
+
return workflow.transitions.some(
|
|
604
|
+
(transition) => (transition.requiredCapabilities ?? []).some((capability) => capabilities.has(capability))
|
|
605
|
+
);
|
|
556
606
|
}
|
|
557
607
|
function availableWorkflowTransitions(workflow, state, user) {
|
|
558
608
|
const capabilities = workflowCapabilities(workflow, user);
|
|
@@ -569,12 +619,10 @@ function initializeWorkflowDocument(data, workflow) {
|
|
|
569
619
|
}
|
|
570
620
|
function materializeWorkflowDocument(doc, workflow, user) {
|
|
571
621
|
const meta = doc.__workflow;
|
|
572
|
-
if (!meta)
|
|
573
|
-
return doc;
|
|
622
|
+
if (!meta) return doc;
|
|
574
623
|
const { __published, __workflow, ...working } = doc;
|
|
575
624
|
if (!canViewWorkflowDraft(workflow, user)) {
|
|
576
|
-
if (!__published || typeof __published !== "object")
|
|
577
|
-
return null;
|
|
625
|
+
if (!__published || typeof __published !== "object") return null;
|
|
578
626
|
return { id: doc.id, ...__published, _workflow: publicMetadata(meta) };
|
|
579
627
|
}
|
|
580
628
|
return {
|
|
@@ -606,15 +654,13 @@ async function persistEvent(db, event) {
|
|
|
606
654
|
}
|
|
607
655
|
async function dispatchLifecycleEvent(config, event) {
|
|
608
656
|
const db = config.db;
|
|
609
|
-
if (!db || !config.events?.handlers.length)
|
|
610
|
-
return;
|
|
657
|
+
if (!db || !config.events?.handlers.length) return;
|
|
611
658
|
const maxAttempts = config.events.maxAttempts ?? 8;
|
|
612
659
|
const retryDelayMs = config.events.retryDelayMs ?? 1e3;
|
|
613
660
|
const attempts = event.attempts + 1;
|
|
614
661
|
try {
|
|
615
662
|
await db.update({ collection: LIFECYCLE_EVENTS_COLLECTION, id: event.id, data: { status: "processing", attempts } });
|
|
616
|
-
for (const handler of config.events.handlers)
|
|
617
|
-
await handler({ ...event, status: "processing", attempts });
|
|
663
|
+
for (const handler of config.events.handlers) await handler({ ...event, status: "processing", attempts });
|
|
618
664
|
await db.update({
|
|
619
665
|
collection: LIFECYCLE_EVENTS_COLLECTION,
|
|
620
666
|
id: event.id,
|
|
@@ -638,8 +684,7 @@ async function saveWorkflowDraft(args) {
|
|
|
638
684
|
const { config, collection, id, originalDoc, data, user } = args;
|
|
639
685
|
const db = config.db;
|
|
640
686
|
const workflow = collection.workflow;
|
|
641
|
-
if (!db.transaction)
|
|
642
|
-
throw new Error(`The configured database adapter does not support workflow transactions.`);
|
|
687
|
+
if (!db.transaction) throw new Error(`The configured database adapter does not support workflow transactions.`);
|
|
643
688
|
const previous = originalDoc.__workflow;
|
|
644
689
|
const event = createLifecycleEvent({
|
|
645
690
|
name: "revision.created",
|
|
@@ -664,8 +709,7 @@ async function saveWorkflowDraft(args) {
|
|
|
664
709
|
async function createWorkflowDocument(args) {
|
|
665
710
|
const { config, collection, data, user } = args;
|
|
666
711
|
const db = config.db;
|
|
667
|
-
if (!db.transaction)
|
|
668
|
-
throw new Error(`The configured database adapter does not support workflow transactions.`);
|
|
712
|
+
if (!db.transaction) throw new Error(`The configured database adapter does not support workflow transactions.`);
|
|
669
713
|
let event;
|
|
670
714
|
const doc = await db.transaction(async (tx) => {
|
|
671
715
|
const created = await tx.create({ collection: collection.slug, data });
|
|
@@ -686,14 +730,11 @@ async function transitionWorkflow(args) {
|
|
|
686
730
|
const { config, collection, id, transitionName, expectedRevision, comment, user, req } = args;
|
|
687
731
|
const db = config.db;
|
|
688
732
|
const workflow = collection.workflow;
|
|
689
|
-
if (!db.transaction)
|
|
690
|
-
throw new Error(`The configured database adapter does not support workflow transactions.`);
|
|
733
|
+
if (!db.transaction) throw new Error(`The configured database adapter does not support workflow transactions.`);
|
|
691
734
|
const original = await db.findOne({ collection: collection.slug, id });
|
|
692
|
-
if (!original)
|
|
693
|
-
throw Object.assign(new Error("Not Found"), { statusCode: 404 });
|
|
735
|
+
if (!original) throw Object.assign(new Error("Not Found"), { statusCode: 404 });
|
|
694
736
|
const meta = original.__workflow;
|
|
695
|
-
if (!meta)
|
|
696
|
-
throw Object.assign(new Error("Entry has no workflow metadata"), { statusCode: 409 });
|
|
737
|
+
if (!meta) throw Object.assign(new Error("Entry has no workflow metadata"), { statusCode: 409 });
|
|
697
738
|
if (expectedRevision !== void 0 && expectedRevision !== meta.revision) {
|
|
698
739
|
throw Object.assign(new Error("This entry changed since it was loaded. Refresh before transitioning it."), { statusCode: 409 });
|
|
699
740
|
}
|
|
@@ -709,8 +750,7 @@ async function transitionWorkflow(args) {
|
|
|
709
750
|
throw Object.assign(new Error(`A comment is required for "${transition.label}".`), { statusCode: 400 });
|
|
710
751
|
}
|
|
711
752
|
const hookContext = { transition, from: meta.state, to: transition.to, doc: original, user, comment, req, db };
|
|
712
|
-
for (const hook of workflow.hooks?.beforeTransition ?? [])
|
|
713
|
-
await hook(hookContext);
|
|
753
|
+
for (const hook of workflow.hooks?.beforeTransition ?? []) await hook(hookContext);
|
|
714
754
|
const events = [createLifecycleEvent({
|
|
715
755
|
name: "workflow.transitioned",
|
|
716
756
|
collection: collection.slug,
|
|
@@ -726,8 +766,7 @@ async function transitionWorkflow(args) {
|
|
|
726
766
|
}
|
|
727
767
|
const updated = await db.transaction(async (tx) => {
|
|
728
768
|
const locked = await tx.findOne({ collection: collection.slug, id });
|
|
729
|
-
if (!locked)
|
|
730
|
-
throw Object.assign(new Error("Not Found"), { statusCode: 404 });
|
|
769
|
+
if (!locked) throw Object.assign(new Error("Not Found"), { statusCode: 404 });
|
|
731
770
|
const lockedMeta = locked.__workflow;
|
|
732
771
|
if (lockedMeta.revision !== meta.revision || lockedMeta.state !== meta.state || expectedRevision !== void 0 && lockedMeta.revision !== expectedRevision) {
|
|
733
772
|
throw Object.assign(new Error("This entry changed since it was loaded. Refresh before transitioning it."), { statusCode: 409 });
|
|
@@ -741,24 +780,20 @@ async function transitionWorkflow(args) {
|
|
|
741
780
|
...transition.unpublish ? { publishedRevision: void 0, publishedAt: void 0, publishedBy: void 0 } : {}
|
|
742
781
|
};
|
|
743
782
|
const data = { __workflow: nextMeta };
|
|
744
|
-
if (targetState.published)
|
|
745
|
-
|
|
746
|
-
if (transition.unpublish)
|
|
747
|
-
data.__published = null;
|
|
783
|
+
if (targetState.published) data.__published = working;
|
|
784
|
+
if (transition.unpublish) data.__published = null;
|
|
748
785
|
const next = await tx.update({ collection: collection.slug, id, data });
|
|
749
786
|
await tx.create({
|
|
750
787
|
collection: WORKFLOW_HISTORY_COLLECTION,
|
|
751
788
|
data: { collection: collection.slug, documentId: id, transition: transition.name, from: lockedMeta.state, to: transition.to, revision: lockedMeta.revision, comment: comment ?? null, actorId: user?.sub ?? null, createdAt: now }
|
|
752
789
|
});
|
|
753
|
-
for (const event of events)
|
|
754
|
-
await persistEvent(tx, event);
|
|
790
|
+
for (const event of events) await persistEvent(tx, event);
|
|
755
791
|
if (collection.audit) {
|
|
756
792
|
await tx.create({ collection: "__audit", data: { collection: collection.slug, documentId: id, operation: "workflow.transition", user: user?.sub ?? null, timestamp: now, changes: JSON.stringify({ transition: transition.name, from: lockedMeta.state, to: transition.to, revision: lockedMeta.revision }) } });
|
|
757
793
|
}
|
|
758
794
|
return next;
|
|
759
795
|
});
|
|
760
|
-
for (const event of events)
|
|
761
|
-
void dispatchLifecycleEvent(config, event);
|
|
796
|
+
for (const event of events) void dispatchLifecycleEvent(config, event);
|
|
762
797
|
for (const hook of workflow.hooks?.afterTransition ?? []) {
|
|
763
798
|
try {
|
|
764
799
|
await hook({ ...hookContext, doc: updated, event: events[0] });
|
|
@@ -769,7 +804,7 @@ async function transitionWorkflow(args) {
|
|
|
769
804
|
return updated;
|
|
770
805
|
}
|
|
771
806
|
|
|
772
|
-
// src/controllers/collection.controller.
|
|
807
|
+
// src/controllers/collection.controller.ts
|
|
773
808
|
var CollectionController = class {
|
|
774
809
|
collection;
|
|
775
810
|
constructor(collection) {
|
|
@@ -777,8 +812,8 @@ var CollectionController = class {
|
|
|
777
812
|
}
|
|
778
813
|
getDelegatedProvider(c) {
|
|
779
814
|
const config = c.get("config");
|
|
780
|
-
const
|
|
781
|
-
if (this.collection.slug !==
|
|
815
|
+
const authCollection = getAdminAuthCollection(config);
|
|
816
|
+
if (!authCollection || this.collection.slug !== authCollection.slug) {
|
|
782
817
|
return null;
|
|
783
818
|
}
|
|
784
819
|
return config.adminAuth?.providers?.find((p) => p.members) || null;
|
|
@@ -786,8 +821,7 @@ var CollectionController = class {
|
|
|
786
821
|
async find(c) {
|
|
787
822
|
const config = c.get("config");
|
|
788
823
|
const db = config.db;
|
|
789
|
-
if (!db)
|
|
790
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
824
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
791
825
|
const provider = this.getDelegatedProvider(c);
|
|
792
826
|
if (provider && provider.members?.list) {
|
|
793
827
|
const limit2 = Number(c.req.query("limit")) || 10;
|
|
@@ -905,13 +939,11 @@ var CollectionController = class {
|
|
|
905
939
|
async findOne(c) {
|
|
906
940
|
const config = c.get("config");
|
|
907
941
|
const db = config.db;
|
|
908
|
-
if (!db)
|
|
909
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
942
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
910
943
|
const provider = this.getDelegatedProvider(c);
|
|
911
944
|
if (provider && (provider.members?.get || provider.members?.list)) {
|
|
912
945
|
const id2 = c.req.param("id");
|
|
913
|
-
if (!id2)
|
|
914
|
-
return c.json({ message: "Missing ID" }, 400);
|
|
946
|
+
if (!id2) return c.json({ message: "Missing ID" }, 400);
|
|
915
947
|
const localDoc = await db.findOne({ collection: this.collection.slug, id: id2 });
|
|
916
948
|
const externalSubject = localDoc?.externalSubject || id2;
|
|
917
949
|
let member = null;
|
|
@@ -921,8 +953,7 @@ var CollectionController = class {
|
|
|
921
953
|
const listResult = await provider.members.list({ req: c.req });
|
|
922
954
|
member = listResult.docs.find((m) => m.id === externalSubject) || null;
|
|
923
955
|
}
|
|
924
|
-
if (!member)
|
|
925
|
-
return c.json({ message: "Not Found" }, 404);
|
|
956
|
+
if (!member) return c.json({ message: "Not Found" }, 404);
|
|
926
957
|
return c.json({
|
|
927
958
|
...member,
|
|
928
959
|
id: localDoc ? localDoc.id : member.id,
|
|
@@ -933,15 +964,12 @@ var CollectionController = class {
|
|
|
933
964
|
const id = c.req.param("id");
|
|
934
965
|
const depth = c.req.query("depth") !== void 0 ? Number(c.req.query("depth")) : 10;
|
|
935
966
|
const user = c.get("user");
|
|
936
|
-
if (!id)
|
|
937
|
-
return c.json({ message: "Missing ID" }, 400);
|
|
967
|
+
if (!id) return c.json({ message: "Missing ID" }, 400);
|
|
938
968
|
let doc = await db.findOne({ collection: this.collection.slug, id });
|
|
939
|
-
if (!doc)
|
|
940
|
-
return c.json({ message: "Not Found" }, 404);
|
|
969
|
+
if (!doc) return c.json({ message: "Not Found" }, 404);
|
|
941
970
|
if (this.collection.workflow) {
|
|
942
971
|
doc = materializeWorkflowDocument(doc, this.collection.workflow, user);
|
|
943
|
-
if (!doc)
|
|
944
|
-
return c.json({ message: "Not Found" }, 404);
|
|
972
|
+
if (!doc) return c.json({ message: "Not Found" }, 404);
|
|
945
973
|
}
|
|
946
974
|
const docWithDefaults = DefaultsService.apply(this.collection.fields, doc);
|
|
947
975
|
const docWithCollectionHooks = await runCollectionHooks(this.collection.hooks?.afterRead, {
|
|
@@ -966,8 +994,7 @@ var CollectionController = class {
|
|
|
966
994
|
async create(c) {
|
|
967
995
|
const config = c.get("config");
|
|
968
996
|
const db = config.db;
|
|
969
|
-
if (!db)
|
|
970
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
997
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
971
998
|
const provider = this.getDelegatedProvider(c);
|
|
972
999
|
if (provider && provider.members?.create) {
|
|
973
1000
|
const contentType2 = c.req.header("Content-Type") || "";
|
|
@@ -1042,16 +1069,13 @@ var CollectionController = class {
|
|
|
1042
1069
|
async upload(c) {
|
|
1043
1070
|
const config = c.get("config");
|
|
1044
1071
|
const storage = config.storage;
|
|
1045
|
-
if (!storage)
|
|
1046
|
-
return c.json({ message: "Storage not configured" }, 500);
|
|
1072
|
+
if (!storage) return c.json({ message: "Storage not configured" }, 500);
|
|
1047
1073
|
const db = config.db;
|
|
1048
|
-
if (!db)
|
|
1049
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
1074
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
1050
1075
|
const readonlyDb = createReadonlyDb(db);
|
|
1051
1076
|
const formData = await c.req.formData();
|
|
1052
1077
|
const file = formData.get("file");
|
|
1053
|
-
if (!file)
|
|
1054
|
-
return c.json({ message: "No file uploaded" }, 400);
|
|
1078
|
+
if (!file) return c.json({ message: "No file uploaded" }, 400);
|
|
1055
1079
|
const buffer = new Uint8Array(await file.arrayBuffer());
|
|
1056
1080
|
const siteId = c.get("siteId");
|
|
1057
1081
|
const workspaceId = c.get("workspaceId");
|
|
@@ -1110,13 +1134,11 @@ var CollectionController = class {
|
|
|
1110
1134
|
async update(c) {
|
|
1111
1135
|
const config = c.get("config");
|
|
1112
1136
|
const db = config.db;
|
|
1113
|
-
if (!db)
|
|
1114
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
1137
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
1115
1138
|
const provider = this.getDelegatedProvider(c);
|
|
1116
1139
|
if (provider && provider.members?.update) {
|
|
1117
1140
|
const id2 = c.req.param("id");
|
|
1118
|
-
if (!id2)
|
|
1119
|
-
return c.json({ message: "Missing ID" }, 400);
|
|
1141
|
+
if (!id2) return c.json({ message: "Missing ID" }, 400);
|
|
1120
1142
|
const body2 = await c.req.json();
|
|
1121
1143
|
const localDoc = await db.findOne({ collection: this.collection.slug, id: id2 });
|
|
1122
1144
|
const externalSubject = localDoc?.externalSubject || id2;
|
|
@@ -1129,8 +1151,7 @@ var CollectionController = class {
|
|
|
1129
1151
|
}
|
|
1130
1152
|
const readonlyDb = createReadonlyDb(db);
|
|
1131
1153
|
const id = c.req.param("id");
|
|
1132
|
-
if (!id)
|
|
1133
|
-
return c.json({ message: "Missing ID" }, 400);
|
|
1154
|
+
if (!id) return c.json({ message: "Missing ID" }, 400);
|
|
1134
1155
|
const body = await c.req.json();
|
|
1135
1156
|
const user = c.get("user");
|
|
1136
1157
|
let data = { ...body };
|
|
@@ -1144,8 +1165,7 @@ var CollectionController = class {
|
|
|
1144
1165
|
updatedBy: user?.sub ?? null
|
|
1145
1166
|
});
|
|
1146
1167
|
const originalDoc = await db.findOne({ collection: this.collection.slug, id });
|
|
1147
|
-
if (!originalDoc)
|
|
1148
|
-
return c.json({ message: "Not Found" }, 404);
|
|
1168
|
+
if (!originalDoc) return c.json({ message: "Not Found" }, 404);
|
|
1149
1169
|
let before = null;
|
|
1150
1170
|
if (this.collection.audit) {
|
|
1151
1171
|
before = originalDoc;
|
|
@@ -1189,10 +1209,8 @@ var CollectionController = class {
|
|
|
1189
1209
|
}
|
|
1190
1210
|
async transition(c) {
|
|
1191
1211
|
const config = c.get("config");
|
|
1192
|
-
if (!config.db)
|
|
1193
|
-
|
|
1194
|
-
if (!this.collection.workflow)
|
|
1195
|
-
return c.json({ message: "Workflows are not enabled for this collection" }, 404);
|
|
1212
|
+
if (!config.db) return c.json({ message: "Database not configured" }, 500);
|
|
1213
|
+
if (!this.collection.workflow) return c.json({ message: "Workflows are not enabled for this collection" }, 404);
|
|
1196
1214
|
const id = c.req.param("id");
|
|
1197
1215
|
const transitionName = c.req.param("transition");
|
|
1198
1216
|
const body = await c.req.json().catch(() => ({}));
|
|
@@ -1215,16 +1233,12 @@ var CollectionController = class {
|
|
|
1215
1233
|
}
|
|
1216
1234
|
async workflowHistory(c) {
|
|
1217
1235
|
const config = c.get("config");
|
|
1218
|
-
if (!config.db)
|
|
1219
|
-
|
|
1220
|
-
if (!this.collection.workflow)
|
|
1221
|
-
return c.json({ message: "Workflows are not enabled for this collection" }, 404);
|
|
1236
|
+
if (!config.db) return c.json({ message: "Database not configured" }, 500);
|
|
1237
|
+
if (!this.collection.workflow) return c.json({ message: "Workflows are not enabled for this collection" }, 404);
|
|
1222
1238
|
const documentId = c.req.param("id");
|
|
1223
|
-
if (!documentId)
|
|
1224
|
-
return c.json({ message: "Missing ID" }, 400);
|
|
1239
|
+
if (!documentId) return c.json({ message: "Missing ID" }, 400);
|
|
1225
1240
|
const document = await config.db.findOne({ collection: this.collection.slug, id: documentId });
|
|
1226
|
-
if (!document)
|
|
1227
|
-
return c.json({ message: "Not Found" }, 404);
|
|
1241
|
+
if (!document) return c.json({ message: "Not Found" }, 404);
|
|
1228
1242
|
const readAccess = this.collection.access?.read;
|
|
1229
1243
|
if (readAccess !== void 0 && readAccess !== null) {
|
|
1230
1244
|
const args = { user: c.get("user"), req: c.req, doc: document };
|
|
@@ -1238,8 +1252,7 @@ var CollectionController = class {
|
|
|
1238
1252
|
});
|
|
1239
1253
|
allowed = match.total > 0;
|
|
1240
1254
|
}
|
|
1241
|
-
if (!allowed)
|
|
1242
|
-
return c.json({ error: true, message: `Access denied: read on ${this.collection.slug}` }, 403);
|
|
1255
|
+
if (!allowed) return c.json({ error: true, message: `Access denied: read on ${this.collection.slug}` }, 403);
|
|
1243
1256
|
}
|
|
1244
1257
|
const result = await config.db.find({
|
|
1245
1258
|
collection: WORKFLOW_HISTORY_COLLECTION,
|
|
@@ -1263,17 +1276,14 @@ var CollectionController = class {
|
|
|
1263
1276
|
async changePassword(c) {
|
|
1264
1277
|
const config = c.get("config");
|
|
1265
1278
|
const db = config.db;
|
|
1266
|
-
if (!db)
|
|
1267
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
1279
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
1268
1280
|
if (!this.collection.auth) {
|
|
1269
1281
|
return c.json({ message: "This collection does not support authentication" }, 400);
|
|
1270
1282
|
}
|
|
1271
1283
|
const id = c.req.param("id");
|
|
1272
|
-
if (!id)
|
|
1273
|
-
return c.json({ message: "Missing ID" }, 400);
|
|
1284
|
+
if (!id) return c.json({ message: "Missing ID" }, 400);
|
|
1274
1285
|
const user = c.get("user");
|
|
1275
|
-
if (!user)
|
|
1276
|
-
return c.json({ message: "Authentication required" }, 401);
|
|
1286
|
+
if (!user) return c.json({ message: "Authentication required" }, 401);
|
|
1277
1287
|
const body = await c.req.json().catch(() => null);
|
|
1278
1288
|
const { oldPassword, newPassword, confirmPassword } = body ?? {};
|
|
1279
1289
|
if (!newPassword) {
|
|
@@ -1295,8 +1305,7 @@ var CollectionController = class {
|
|
|
1295
1305
|
return c.json({ message: "Current password is required" }, 400);
|
|
1296
1306
|
}
|
|
1297
1307
|
const existing = await db.findOne({ collection: this.collection.slug, id });
|
|
1298
|
-
if (!existing)
|
|
1299
|
-
return c.json({ message: "User not found" }, 404);
|
|
1308
|
+
if (!existing) return c.json({ message: "User not found" }, 404);
|
|
1300
1309
|
const valid = await verifyPassword(oldPassword, existing.password);
|
|
1301
1310
|
if (!valid) {
|
|
1302
1311
|
return c.json({ message: "Invalid current password" }, 400);
|
|
@@ -1327,13 +1336,11 @@ var CollectionController = class {
|
|
|
1327
1336
|
async delete(c) {
|
|
1328
1337
|
const config = c.get("config");
|
|
1329
1338
|
const db = config.db;
|
|
1330
|
-
if (!db)
|
|
1331
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
1339
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
1332
1340
|
const provider = this.getDelegatedProvider(c);
|
|
1333
1341
|
if (provider && provider.members?.delete) {
|
|
1334
1342
|
const id2 = c.req.param("id");
|
|
1335
|
-
if (!id2)
|
|
1336
|
-
return c.json({ message: "Missing ID" }, 400);
|
|
1343
|
+
if (!id2) return c.json({ message: "Missing ID" }, 400);
|
|
1337
1344
|
const localDoc = await db.findOne({ collection: this.collection.slug, id: id2 });
|
|
1338
1345
|
const externalSubject = localDoc?.externalSubject || id2;
|
|
1339
1346
|
await provider.members.delete({ externalSubject, req: c.req });
|
|
@@ -1341,12 +1348,10 @@ var CollectionController = class {
|
|
|
1341
1348
|
}
|
|
1342
1349
|
const readonlyDb = createReadonlyDb(db);
|
|
1343
1350
|
const id = c.req.param("id");
|
|
1344
|
-
if (!id)
|
|
1345
|
-
return c.json({ message: "Missing ID" }, 400);
|
|
1351
|
+
if (!id) return c.json({ message: "Missing ID" }, 400);
|
|
1346
1352
|
const user = c.get("user");
|
|
1347
1353
|
const doc = await db.findOne({ collection: this.collection.slug, id });
|
|
1348
|
-
if (!doc)
|
|
1349
|
-
return c.json({ message: "Not Found" }, 404);
|
|
1354
|
+
if (!doc) return c.json({ message: "Not Found" }, 404);
|
|
1350
1355
|
let before = null;
|
|
1351
1356
|
if (this.collection.audit) {
|
|
1352
1357
|
before = doc;
|
|
@@ -1381,8 +1386,7 @@ var CollectionController = class {
|
|
|
1381
1386
|
async deleteMany(c) {
|
|
1382
1387
|
const config = c.get("config");
|
|
1383
1388
|
const db = config.db;
|
|
1384
|
-
if (!db)
|
|
1385
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
1389
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
1386
1390
|
const readonlyDb = createReadonlyDb(db);
|
|
1387
1391
|
const user = c.get("user");
|
|
1388
1392
|
let ids = [];
|
|
@@ -1397,8 +1401,7 @@ var CollectionController = class {
|
|
|
1397
1401
|
const raw = c.req.queries("ids") ?? c.req.queries("ids[]") ?? [];
|
|
1398
1402
|
ids = raw.filter(Boolean);
|
|
1399
1403
|
}
|
|
1400
|
-
if (!ids.length)
|
|
1401
|
-
return c.json({ message: "No IDs provided" }, 400);
|
|
1404
|
+
if (!ids.length) return c.json({ message: "No IDs provided" }, 400);
|
|
1402
1405
|
const deleted = [];
|
|
1403
1406
|
const failed = [];
|
|
1404
1407
|
for (const id of ids) {
|
|
@@ -1451,8 +1454,7 @@ var CollectionController = class {
|
|
|
1451
1454
|
async seed(c) {
|
|
1452
1455
|
const config = c.get("config");
|
|
1453
1456
|
const db = config.db;
|
|
1454
|
-
if (!db)
|
|
1455
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
1457
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
1456
1458
|
const body = await c.req.json();
|
|
1457
1459
|
const initialData = body.data;
|
|
1458
1460
|
if (!initialData || !Array.isArray(initialData)) {
|
|
@@ -1472,7 +1474,7 @@ var CollectionController = class {
|
|
|
1472
1474
|
}
|
|
1473
1475
|
};
|
|
1474
1476
|
|
|
1475
|
-
// src/controllers/global.controller.
|
|
1477
|
+
// src/controllers/global.controller.ts
|
|
1476
1478
|
var GlobalController = class {
|
|
1477
1479
|
global;
|
|
1478
1480
|
constructor(global) {
|
|
@@ -1481,8 +1483,7 @@ var GlobalController = class {
|
|
|
1481
1483
|
async get(c) {
|
|
1482
1484
|
const config = c.get("config");
|
|
1483
1485
|
const db = config.db;
|
|
1484
|
-
if (!db)
|
|
1485
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
1486
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
1486
1487
|
const readonlyDb = createReadonlyDb(db);
|
|
1487
1488
|
const depth = c.req.query("depth") !== void 0 ? Number(c.req.query("depth")) : 10;
|
|
1488
1489
|
const user = c.get("user");
|
|
@@ -1526,8 +1527,7 @@ var GlobalController = class {
|
|
|
1526
1527
|
async update(c) {
|
|
1527
1528
|
const config = c.get("config");
|
|
1528
1529
|
const db = config.db;
|
|
1529
|
-
if (!db)
|
|
1530
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
1530
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
1531
1531
|
const readonlyDb = createReadonlyDb(db);
|
|
1532
1532
|
const body = await c.req.json();
|
|
1533
1533
|
const user = c.get("user");
|
|
@@ -1562,8 +1562,7 @@ var GlobalController = class {
|
|
|
1562
1562
|
async seed(c) {
|
|
1563
1563
|
const config = c.get("config");
|
|
1564
1564
|
const db = config.db;
|
|
1565
|
-
if (!db)
|
|
1566
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
1565
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
1567
1566
|
const body = await c.req.json();
|
|
1568
1567
|
const initialData = body.data;
|
|
1569
1568
|
if (!initialData) {
|
|
@@ -1579,23 +1578,20 @@ var GlobalController = class {
|
|
|
1579
1578
|
}
|
|
1580
1579
|
};
|
|
1581
1580
|
function isFunctionallyEmpty(obj) {
|
|
1582
|
-
if (obj === null || obj === void 0 || obj === "")
|
|
1583
|
-
return true;
|
|
1581
|
+
if (obj === null || obj === void 0 || obj === "") return true;
|
|
1584
1582
|
if (Array.isArray(obj)) {
|
|
1585
|
-
if (obj.length === 0)
|
|
1586
|
-
return true;
|
|
1583
|
+
if (obj.length === 0) return true;
|
|
1587
1584
|
return obj.every(isFunctionallyEmpty);
|
|
1588
1585
|
}
|
|
1589
1586
|
if (typeof obj === "object") {
|
|
1590
1587
|
const keys = Object.keys(obj);
|
|
1591
|
-
if (keys.length === 0)
|
|
1592
|
-
return true;
|
|
1588
|
+
if (keys.length === 0) return true;
|
|
1593
1589
|
return keys.every((key) => isFunctionallyEmpty(obj[key]));
|
|
1594
1590
|
}
|
|
1595
1591
|
return false;
|
|
1596
1592
|
}
|
|
1597
1593
|
|
|
1598
|
-
// src/controllers/media.controller.
|
|
1594
|
+
// src/controllers/media.controller.ts
|
|
1599
1595
|
var MediaController = class {
|
|
1600
1596
|
collection;
|
|
1601
1597
|
constructor(collection = "media") {
|
|
@@ -1675,8 +1671,7 @@ var MediaController = class {
|
|
|
1675
1671
|
}
|
|
1676
1672
|
}
|
|
1677
1673
|
const db = config.db;
|
|
1678
|
-
if (!db)
|
|
1679
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
1674
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
1680
1675
|
const doc = await db.create({
|
|
1681
1676
|
collection: this.collection,
|
|
1682
1677
|
data: finalFileData
|
|
@@ -1685,8 +1680,7 @@ var MediaController = class {
|
|
|
1685
1680
|
}
|
|
1686
1681
|
async find(c) {
|
|
1687
1682
|
const db = c.get("config").db;
|
|
1688
|
-
if (!db)
|
|
1689
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
1683
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
1690
1684
|
const limit = Number(c.req.query("limit")) || 10;
|
|
1691
1685
|
const page = Number(c.req.query("page")) || 1;
|
|
1692
1686
|
const result = await db.find({
|
|
@@ -1700,14 +1694,11 @@ var MediaController = class {
|
|
|
1700
1694
|
const config = c.get("config");
|
|
1701
1695
|
const storage = config.storage;
|
|
1702
1696
|
const db = config.db;
|
|
1703
|
-
if (!db)
|
|
1704
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
1697
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
1705
1698
|
const id = c.req.param("id");
|
|
1706
|
-
if (!id)
|
|
1707
|
-
return c.json({ message: "Missing ID" }, 400);
|
|
1699
|
+
if (!id) return c.json({ message: "Missing ID" }, 400);
|
|
1708
1700
|
const doc = await db.findOne({ collection: this.collection, id });
|
|
1709
|
-
if (!doc)
|
|
1710
|
-
return c.json({ message: "Not Found" }, 404);
|
|
1701
|
+
if (!doc) return c.json({ message: "Not Found" }, 404);
|
|
1711
1702
|
if (storage) {
|
|
1712
1703
|
await storage.delete({ filename: doc.filename });
|
|
1713
1704
|
if (doc.sizes) {
|
|
@@ -1728,26 +1719,26 @@ var MediaController = class {
|
|
|
1728
1719
|
return c.json({ message: "Storage not configured for serving" }, 404);
|
|
1729
1720
|
}
|
|
1730
1721
|
const filename = c.req.param("filename");
|
|
1731
|
-
if (!filename)
|
|
1732
|
-
return c.json({ message: "Missing filename" }, 400);
|
|
1722
|
+
if (!filename) return c.json({ message: "Missing filename" }, 400);
|
|
1733
1723
|
let res = await storage.resolve({ filename });
|
|
1734
1724
|
if (!res && !filename.includes("/")) {
|
|
1735
1725
|
res = await storage.resolve({ filename: `default/${filename}` });
|
|
1736
1726
|
}
|
|
1737
|
-
if (!res)
|
|
1738
|
-
return c.json({ message: "Not Found" }, 404);
|
|
1727
|
+
if (!res) return c.json({ message: "Not Found" }, 404);
|
|
1739
1728
|
c.header("Content-Type", res.mimeType);
|
|
1740
1729
|
return c.body(res.buffer);
|
|
1741
1730
|
}
|
|
1742
1731
|
};
|
|
1743
1732
|
|
|
1744
|
-
// src/auth/token.
|
|
1733
|
+
// src/auth/token.ts
|
|
1745
1734
|
var import_jose = require("jose");
|
|
1746
1735
|
var import_node_util2 = require("util");
|
|
1747
1736
|
function getSecret() {
|
|
1748
1737
|
const secret = process.env.DYRECTED_JWT_SECRET || process.env.JWT_SECRET;
|
|
1749
1738
|
if (!secret) {
|
|
1750
|
-
throw new Error(
|
|
1739
|
+
throw new Error(
|
|
1740
|
+
"[dyrected/core] DYRECTED_JWT_SECRET is not set. Add it to your environment variables to enable auth collections."
|
|
1741
|
+
);
|
|
1751
1742
|
}
|
|
1752
1743
|
return new import_node_util2.TextEncoder().encode(secret);
|
|
1753
1744
|
}
|
|
@@ -1760,7 +1751,7 @@ async function verifyCollectionToken(token) {
|
|
|
1760
1751
|
return payload;
|
|
1761
1752
|
}
|
|
1762
1753
|
|
|
1763
|
-
// src/services/email-template.
|
|
1754
|
+
// src/services/email-template.ts
|
|
1764
1755
|
var emailTokens = {
|
|
1765
1756
|
colors: {
|
|
1766
1757
|
canvas: "#f6f7f2",
|
|
@@ -1824,9 +1815,11 @@ function detailBox(content, monospace = false) {
|
|
|
1824
1815
|
}
|
|
1825
1816
|
function ctaButton(label, href) {
|
|
1826
1817
|
const safeHref = safeHttpUrl(href);
|
|
1827
|
-
if (!safeHref)
|
|
1828
|
-
|
|
1829
|
-
|
|
1818
|
+
if (!safeHref) return "";
|
|
1819
|
+
return table(row(
|
|
1820
|
+
`<a href="${safeHref}" style="display:inline-block;padding:13px 22px;border-radius:${emailTokens.radius.control};background:${emailTokens.colors.accent};font-family:${emailTokens.font};font-size:14px;line-height:1.2;font-weight:700;color:${emailTokens.colors.text};text-decoration:none">${escapeHtml(label)}</a>`,
|
|
1821
|
+
"padding:8px 0 24px"
|
|
1822
|
+
), "width:auto");
|
|
1830
1823
|
}
|
|
1831
1824
|
function alertBox(content) {
|
|
1832
1825
|
return table(row(escapeHtml(content), `padding:13px 16px;font-family:${emailTokens.font};font-size:13px;line-height:1.5;color:${emailTokens.colors.dangerText}`), `width:100%;background:${emailTokens.colors.dangerSurface};border:1px solid ${emailTokens.colors.dangerBorder};border-radius:${emailTokens.radius.control}`);
|
|
@@ -1837,19 +1830,23 @@ function layout({ preheader, title, content, footer }) {
|
|
|
1837
1830
|
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"></head>
|
|
1838
1831
|
<body style="margin:0;padding:0;background:${emailTokens.colors.canvas}">
|
|
1839
1832
|
<div style="display:none;max-height:0;overflow:hidden;opacity:0;color:transparent">${escapeHtml(preheader)}</div>
|
|
1840
|
-
${table(row(
|
|
1833
|
+
${table(row(
|
|
1834
|
+
table(
|
|
1835
|
+
row(" ", `height:5px;background:${emailTokens.colors.accent};font-size:0;line-height:0`) + row(`${sectionLabel("Dyrected")}${heading(title)}`, "padding:30px 32px 24px") + row(content, "padding:0 32px 32px") + row(`${divider()}${paragraph(footer, "20px 0 6px")}${paragraph("Privacy: this message contains account-related information; please avoid forwarding it.", "0")}`, "padding:0 32px 28px"),
|
|
1836
|
+
`width:100%;max-width:${emailTokens.width};background:${emailTokens.colors.surface};border:1px solid ${emailTokens.colors.border};border-radius:${emailTokens.radius.card};overflow:hidden`
|
|
1837
|
+
),
|
|
1838
|
+
"padding:32px 12px"
|
|
1839
|
+
), `width:100%;background:${emailTokens.colors.canvas}`)}
|
|
1841
1840
|
</body>
|
|
1842
1841
|
</html>`;
|
|
1843
1842
|
}
|
|
1844
1843
|
|
|
1845
|
-
// src/services/email.service.
|
|
1844
|
+
// src/services/email.service.ts
|
|
1846
1845
|
var _devSend = null;
|
|
1847
1846
|
var _devSendPromise = null;
|
|
1848
1847
|
async function getDevSend() {
|
|
1849
|
-
if (_devSend)
|
|
1850
|
-
|
|
1851
|
-
if (_devSendPromise)
|
|
1852
|
-
return _devSendPromise;
|
|
1848
|
+
if (_devSend) return _devSend;
|
|
1849
|
+
if (_devSendPromise) return _devSendPromise;
|
|
1853
1850
|
_devSendPromise = (async () => {
|
|
1854
1851
|
try {
|
|
1855
1852
|
const nodemailer = await import("nodemailer");
|
|
@@ -1933,7 +1930,7 @@ function buildPasswordChangedEmail(config, args) {
|
|
|
1933
1930
|
};
|
|
1934
1931
|
}
|
|
1935
1932
|
|
|
1936
|
-
// src/controllers/auth.controller.
|
|
1933
|
+
// src/controllers/auth.controller.ts
|
|
1937
1934
|
var AuthController = class {
|
|
1938
1935
|
collection;
|
|
1939
1936
|
constructor(collection) {
|
|
@@ -1945,8 +1942,7 @@ var AuthController = class {
|
|
|
1945
1942
|
// ---------------------------------------------------------------------------
|
|
1946
1943
|
async init(c) {
|
|
1947
1944
|
const db = c.get("config").db;
|
|
1948
|
-
if (!db)
|
|
1949
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
1945
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
1950
1946
|
const result = await db.find({
|
|
1951
1947
|
collection: this.collection.slug,
|
|
1952
1948
|
limit: 1
|
|
@@ -1962,8 +1958,7 @@ var AuthController = class {
|
|
|
1962
1958
|
async registerFirstUser(c) {
|
|
1963
1959
|
const config = c.get("config");
|
|
1964
1960
|
const db = config.db;
|
|
1965
|
-
if (!db)
|
|
1966
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
1961
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
1967
1962
|
const check = await db.find({
|
|
1968
1963
|
collection: this.collection.slug,
|
|
1969
1964
|
limit: 1
|
|
@@ -1991,7 +1986,9 @@ var AuthController = class {
|
|
|
1991
1986
|
collection: this.collection.slug
|
|
1992
1987
|
});
|
|
1993
1988
|
const { subject, html } = buildWelcomeEmail(config, { email: body.email });
|
|
1994
|
-
sendEmail(config, { to: body.email, subject, html }).catch(
|
|
1989
|
+
sendEmail(config, { to: body.email, subject, html }).catch(
|
|
1990
|
+
(err) => console.error("[dyrected/core] Failed to send welcome email:", err)
|
|
1991
|
+
);
|
|
1995
1992
|
const { password: _, ...safeUser } = user;
|
|
1996
1993
|
return c.json({ token, user: safeUser });
|
|
1997
1994
|
}
|
|
@@ -2000,8 +1997,7 @@ var AuthController = class {
|
|
|
2000
1997
|
// ---------------------------------------------------------------------------
|
|
2001
1998
|
async login(c) {
|
|
2002
1999
|
const db = c.get("config").db;
|
|
2003
|
-
if (!db)
|
|
2004
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
2000
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
2005
2001
|
const body = await c.req.json().catch(() => null);
|
|
2006
2002
|
if (!body?.email || !body?.password) {
|
|
2007
2003
|
return c.json({ error: true, message: "email and password are required." }, 400);
|
|
@@ -2040,8 +2036,7 @@ var AuthController = class {
|
|
|
2040
2036
|
// ---------------------------------------------------------------------------
|
|
2041
2037
|
async me(c) {
|
|
2042
2038
|
const db = c.get("config").db;
|
|
2043
|
-
if (!db)
|
|
2044
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
2039
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
2045
2040
|
const requestUser = c.get("user");
|
|
2046
2041
|
if (!requestUser) {
|
|
2047
2042
|
return c.json({ error: true, message: "Authentication required." }, 401);
|
|
@@ -2076,8 +2071,7 @@ var AuthController = class {
|
|
|
2076
2071
|
async forgotPassword(c) {
|
|
2077
2072
|
const config = c.get("config");
|
|
2078
2073
|
const db = config.db;
|
|
2079
|
-
if (!db)
|
|
2080
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
2074
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
2081
2075
|
const body = await c.req.json().catch(() => null);
|
|
2082
2076
|
if (!body?.email) {
|
|
2083
2077
|
return c.json({ error: true, message: "email is required." }, 400);
|
|
@@ -2089,7 +2083,10 @@ var AuthController = class {
|
|
|
2089
2083
|
});
|
|
2090
2084
|
const user = result.docs[0];
|
|
2091
2085
|
if (user) {
|
|
2092
|
-
const resetToken = await signCollectionToken(
|
|
2086
|
+
const resetToken = await signCollectionToken(
|
|
2087
|
+
{ sub: user.id, email: user.email, collection: this.collection.slug, purpose: "reset" },
|
|
2088
|
+
"1h"
|
|
2089
|
+
);
|
|
2093
2090
|
const resetUrl = body?.resetUrl;
|
|
2094
2091
|
const url = resetUrl ? `${resetUrl}${resetUrl.includes("?") ? "&" : "?"}token=${encodeURIComponent(resetToken)}` : void 0;
|
|
2095
2092
|
try {
|
|
@@ -2112,8 +2109,7 @@ var AuthController = class {
|
|
|
2112
2109
|
async resetPassword(c) {
|
|
2113
2110
|
const config = c.get("config");
|
|
2114
2111
|
const db = config.db;
|
|
2115
|
-
if (!db)
|
|
2116
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
2112
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
2117
2113
|
const body = await c.req.json().catch(() => null);
|
|
2118
2114
|
if (!body?.token || !body?.password) {
|
|
2119
2115
|
return c.json({ error: true, message: "token and password are required." }, 400);
|
|
@@ -2134,7 +2130,9 @@ var AuthController = class {
|
|
|
2134
2130
|
data: { password: hashedPassword }
|
|
2135
2131
|
});
|
|
2136
2132
|
const { subject, html } = buildPasswordChangedEmail(config, { email: payload.email });
|
|
2137
|
-
sendEmail(config, { to: payload.email, subject, html }).catch(
|
|
2133
|
+
sendEmail(config, { to: payload.email, subject, html }).catch(
|
|
2134
|
+
(err) => console.error("[dyrected/core] Failed to send password-changed email:", err)
|
|
2135
|
+
);
|
|
2138
2136
|
return c.json({ success: true, message: "Password has been reset. You can now log in." });
|
|
2139
2137
|
}
|
|
2140
2138
|
// ---------------------------------------------------------------------------
|
|
@@ -2144,8 +2142,7 @@ var AuthController = class {
|
|
|
2144
2142
|
async invite(c) {
|
|
2145
2143
|
const config = c.get("config");
|
|
2146
2144
|
const db = config.db;
|
|
2147
|
-
if (!db)
|
|
2148
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
2145
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
2149
2146
|
const requestUser = c.get("user");
|
|
2150
2147
|
if (!requestUser) {
|
|
2151
2148
|
return c.json({ error: true, message: "Authentication required." }, 401);
|
|
@@ -2162,7 +2159,10 @@ var AuthController = class {
|
|
|
2162
2159
|
if (existing.total > 0) {
|
|
2163
2160
|
return c.json({ error: true, message: "An account with that email already exists." }, 409);
|
|
2164
2161
|
}
|
|
2165
|
-
const inviteToken = await signCollectionToken(
|
|
2162
|
+
const inviteToken = await signCollectionToken(
|
|
2163
|
+
{ sub: body.email, email: body.email, collection: this.collection.slug, purpose: "invite" },
|
|
2164
|
+
"7d"
|
|
2165
|
+
);
|
|
2166
2166
|
try {
|
|
2167
2167
|
const { subject, html } = buildInviteEmail(config, {
|
|
2168
2168
|
token: inviteToken,
|
|
@@ -2182,8 +2182,7 @@ var AuthController = class {
|
|
|
2182
2182
|
async acceptInvite(c) {
|
|
2183
2183
|
const config = c.get("config");
|
|
2184
2184
|
const db = config.db;
|
|
2185
|
-
if (!db)
|
|
2186
|
-
return c.json({ message: "Database not configured" }, 500);
|
|
2185
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
2187
2186
|
const body = await c.req.json().catch(() => null);
|
|
2188
2187
|
if (!body?.token || !body?.password) {
|
|
2189
2188
|
return c.json({ error: true, message: "token and password are required." }, 400);
|
|
@@ -2218,52 +2217,23 @@ var AuthController = class {
|
|
|
2218
2217
|
collection: this.collection.slug
|
|
2219
2218
|
});
|
|
2220
2219
|
const { subject, html } = buildWelcomeEmail(config, { email: inviteeEmail });
|
|
2221
|
-
sendEmail(config, { to: inviteeEmail, subject, html }).catch(
|
|
2220
|
+
sendEmail(config, { to: inviteeEmail, subject, html }).catch(
|
|
2221
|
+
(err) => console.error("[dyrected/core] Failed to send welcome email:", err)
|
|
2222
|
+
);
|
|
2222
2223
|
const { password: _, ...safeUser } = user;
|
|
2223
2224
|
return c.json({ token: sessionToken, user: safeUser }, 201);
|
|
2224
2225
|
}
|
|
2225
2226
|
};
|
|
2226
2227
|
|
|
2227
|
-
// src/controllers/admin-auth.controller.
|
|
2228
|
+
// src/controllers/admin-auth.controller.ts
|
|
2228
2229
|
var import_node_crypto2 = require("crypto");
|
|
2229
2230
|
var import_node_util3 = require("util");
|
|
2230
2231
|
var import_jose2 = require("jose");
|
|
2231
|
-
|
|
2232
|
-
// src/utils/admin-auth.js
|
|
2233
|
-
function getAdminAuthCollection(config) {
|
|
2234
|
-
const requestedSlug = config.adminAuth?.collectionSlug;
|
|
2235
|
-
if (requestedSlug) {
|
|
2236
|
-
return config.collections.find((collection) => collection.slug === requestedSlug) ?? null;
|
|
2237
|
-
}
|
|
2238
|
-
return config.collections.find((collection) => collection.slug === "__admins") ?? config.collections.find((collection) => collection.auth) ?? null;
|
|
2239
|
-
}
|
|
2240
|
-
function getPublicAdminAuthConfig(adminAuth) {
|
|
2241
|
-
const providers = (adminAuth?.providers ?? []).map((provider) => ({
|
|
2242
|
-
id: provider.id,
|
|
2243
|
-
type: provider.type,
|
|
2244
|
-
displayName: provider.displayName || humanizeProviderName(provider.id, provider.type),
|
|
2245
|
-
autoRedirect: provider.autoRedirect
|
|
2246
|
-
}));
|
|
2247
|
-
return {
|
|
2248
|
-
mode: adminAuth?.mode ?? "local",
|
|
2249
|
-
collectionSlug: adminAuth?.collectionSlug,
|
|
2250
|
-
provisioningMode: adminAuth?.provisioningMode,
|
|
2251
|
-
providers
|
|
2252
|
-
};
|
|
2253
|
-
}
|
|
2254
|
-
function humanizeProviderName(id, type) {
|
|
2255
|
-
const cleaned = id.replace(/[-_]+/g, " ").trim();
|
|
2256
|
-
if (!cleaned)
|
|
2257
|
-
return type.toUpperCase();
|
|
2258
|
-
return cleaned.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
2259
|
-
}
|
|
2260
|
-
|
|
2261
|
-
// src/controllers/admin-auth.controller.js
|
|
2262
2232
|
var AdminAuthController = class {
|
|
2263
|
-
config;
|
|
2264
2233
|
constructor(config) {
|
|
2265
2234
|
this.config = config;
|
|
2266
2235
|
}
|
|
2236
|
+
config;
|
|
2267
2237
|
providers(c) {
|
|
2268
2238
|
return c.json(getPublicAdminAuthConfig(this.config.adminAuth));
|
|
2269
2239
|
}
|
|
@@ -2276,7 +2246,12 @@ var AdminAuthController = class {
|
|
|
2276
2246
|
const siteId = this.getSiteId(c);
|
|
2277
2247
|
const returnTo = this.normalizeReturnTo(c.req.query("returnTo"), c);
|
|
2278
2248
|
if (provider.type === "oidc") {
|
|
2279
|
-
const redirectUrl = await this.buildOidcStartUrl(
|
|
2249
|
+
const redirectUrl = await this.buildOidcStartUrl(
|
|
2250
|
+
provider,
|
|
2251
|
+
returnTo,
|
|
2252
|
+
siteId,
|
|
2253
|
+
new URL(c.req.url).origin
|
|
2254
|
+
);
|
|
2280
2255
|
return c.redirect(redirectUrl, 302);
|
|
2281
2256
|
}
|
|
2282
2257
|
if (!provider.startUrl) {
|
|
@@ -2284,14 +2259,16 @@ var AdminAuthController = class {
|
|
|
2284
2259
|
}
|
|
2285
2260
|
const url = this.buildCustomProviderStartUrl(provider.startUrl, new URL(c.req.url).origin);
|
|
2286
2261
|
if (!url) {
|
|
2287
|
-
return c.json(
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2262
|
+
return c.json(
|
|
2263
|
+
{
|
|
2264
|
+
error: true,
|
|
2265
|
+
message: `Admin auth provider "${provider.id}" has an invalid start URL.`
|
|
2266
|
+
},
|
|
2267
|
+
500
|
|
2268
|
+
);
|
|
2291
2269
|
}
|
|
2292
2270
|
url.searchParams.set("returnTo", returnTo);
|
|
2293
|
-
if (siteId)
|
|
2294
|
-
url.searchParams.set("siteId", siteId);
|
|
2271
|
+
if (siteId) url.searchParams.set("siteId", siteId);
|
|
2295
2272
|
url.searchParams.set("provider", provider.id);
|
|
2296
2273
|
return c.redirect(url.toString(), 302);
|
|
2297
2274
|
}
|
|
@@ -2338,8 +2315,7 @@ var AdminAuthController = class {
|
|
|
2338
2315
|
return c.json({ success: true, message: "Logged out. Discard your token." });
|
|
2339
2316
|
}
|
|
2340
2317
|
getProvider(config, id) {
|
|
2341
|
-
if (config.adminAuth?.mode !== "external")
|
|
2342
|
-
return void 0;
|
|
2318
|
+
if (config.adminAuth?.mode !== "external") return void 0;
|
|
2343
2319
|
return config.adminAuth.providers.find((provider) => provider.id === id);
|
|
2344
2320
|
}
|
|
2345
2321
|
async completeProviderAuth(requestConfig, provider, c) {
|
|
@@ -2351,8 +2327,7 @@ var AdminAuthController = class {
|
|
|
2351
2327
|
throw new Error("Admin auth collection is not configured.");
|
|
2352
2328
|
}
|
|
2353
2329
|
const db = c.get("config").db;
|
|
2354
|
-
if (!db)
|
|
2355
|
-
throw new Error("Database not configured.");
|
|
2330
|
+
if (!db) throw new Error("Database not configured.");
|
|
2356
2331
|
let user = await this.findUserByExternalIdentity(db, adminCollection.slug, provider.id, identity.sub) ?? (identity.email ? (await db.find({
|
|
2357
2332
|
collection: adminCollection.slug,
|
|
2358
2333
|
where: { email: identity.email },
|
|
@@ -2412,8 +2387,7 @@ var AdminAuthController = class {
|
|
|
2412
2387
|
},
|
|
2413
2388
|
user
|
|
2414
2389
|
});
|
|
2415
|
-
if (resolved)
|
|
2416
|
-
return resolved;
|
|
2390
|
+
if (resolved) return resolved;
|
|
2417
2391
|
return { allowed: true, roles: identity.roles, data: {} };
|
|
2418
2392
|
}
|
|
2419
2393
|
async buildUserData(identity, providerId, roles, extraData) {
|
|
@@ -2444,8 +2418,7 @@ var AdminAuthController = class {
|
|
|
2444
2418
|
}
|
|
2445
2419
|
async getRequestConfig(c) {
|
|
2446
2420
|
const siteId = this.getSiteId(c);
|
|
2447
|
-
if (!siteId || !this.config.onSchemaFetch)
|
|
2448
|
-
return this.config;
|
|
2421
|
+
if (!siteId || !this.config.onSchemaFetch) return this.config;
|
|
2449
2422
|
const dynamic = await this.config.onSchemaFetch(siteId);
|
|
2450
2423
|
return {
|
|
2451
2424
|
...this.config,
|
|
@@ -2476,8 +2449,7 @@ var AdminAuthController = class {
|
|
|
2476
2449
|
async resolveOidcIdentity(provider, c) {
|
|
2477
2450
|
const code = c.req.query("code");
|
|
2478
2451
|
const state = c.req.query("state");
|
|
2479
|
-
if (!code || !state)
|
|
2480
|
-
throw new Error("Missing OIDC callback parameters.");
|
|
2452
|
+
if (!code || !state) throw new Error("Missing OIDC callback parameters.");
|
|
2481
2453
|
const parsedState = await this.verifyState(state, provider.id);
|
|
2482
2454
|
const discovery = await this.getOidcDiscovery(provider);
|
|
2483
2455
|
const redirectUri = provider.redirectUri || this.defaultCallbackPath(provider.id, new URL(c.req.url).origin);
|
|
@@ -2512,8 +2484,7 @@ var AdminAuthController = class {
|
|
|
2512
2484
|
const userInfo = await fetch(provider.userInfoEndpoint || discovery.userinfo_endpoint, {
|
|
2513
2485
|
headers: { Authorization: `Bearer ${tokenBody.access_token}` }
|
|
2514
2486
|
});
|
|
2515
|
-
if (!userInfo.ok)
|
|
2516
|
-
throw new Error("OIDC userinfo request failed.");
|
|
2487
|
+
if (!userInfo.ok) throw new Error("OIDC userinfo request failed.");
|
|
2517
2488
|
claims = await userInfo.json();
|
|
2518
2489
|
} else {
|
|
2519
2490
|
throw new Error("OIDC provider did not return usable identity claims.");
|
|
@@ -2553,13 +2524,15 @@ var AdminAuthController = class {
|
|
|
2553
2524
|
if (provider.audience) {
|
|
2554
2525
|
const aud = claims.aud;
|
|
2555
2526
|
const matches = Array.isArray(aud) ? aud.includes(provider.audience) : aud === provider.audience;
|
|
2556
|
-
if (!matches)
|
|
2557
|
-
throw new Error("External auth audience mismatch.");
|
|
2527
|
+
if (!matches) throw new Error("External auth audience mismatch.");
|
|
2558
2528
|
}
|
|
2559
2529
|
}
|
|
2560
2530
|
return {
|
|
2561
2531
|
identity: this.mapClaims(claims, provider.claimMapping),
|
|
2562
|
-
returnTo: this.normalizeReturnTo(
|
|
2532
|
+
returnTo: this.normalizeReturnTo(
|
|
2533
|
+
body?.returnTo || c.req.query("returnTo"),
|
|
2534
|
+
c
|
|
2535
|
+
)
|
|
2563
2536
|
};
|
|
2564
2537
|
}
|
|
2565
2538
|
mapClaims(claims, mapping) {
|
|
@@ -2589,20 +2562,19 @@ var AdminAuthController = class {
|
|
|
2589
2562
|
return typeof value === "string" ? value : void 0;
|
|
2590
2563
|
}
|
|
2591
2564
|
readStringArrayClaim(value) {
|
|
2592
|
-
if (!value)
|
|
2593
|
-
return void 0;
|
|
2565
|
+
if (!value) return void 0;
|
|
2594
2566
|
if (Array.isArray(value)) {
|
|
2595
2567
|
return value.filter((entry) => typeof entry === "string");
|
|
2596
2568
|
}
|
|
2597
|
-
if (typeof value === "string")
|
|
2598
|
-
return [value];
|
|
2569
|
+
if (typeof value === "string") return [value];
|
|
2599
2570
|
return void 0;
|
|
2600
2571
|
}
|
|
2601
2572
|
async getOidcDiscovery(provider) {
|
|
2602
|
-
const discoveryUrl = new URL(
|
|
2573
|
+
const discoveryUrl = new URL(
|
|
2574
|
+
provider.issuer.replace(/\/$/, "") + "/.well-known/openid-configuration"
|
|
2575
|
+
);
|
|
2603
2576
|
const response = await fetch(discoveryUrl.toString());
|
|
2604
|
-
if (!response.ok)
|
|
2605
|
-
throw new Error("Failed to load OIDC discovery document.");
|
|
2577
|
+
if (!response.ok) throw new Error("Failed to load OIDC discovery document.");
|
|
2606
2578
|
return response.json();
|
|
2607
2579
|
}
|
|
2608
2580
|
defaultCallbackPath(providerId, origin = "") {
|
|
@@ -2610,12 +2582,10 @@ var AdminAuthController = class {
|
|
|
2610
2582
|
}
|
|
2611
2583
|
buildCustomProviderStartUrl(startUrl, origin) {
|
|
2612
2584
|
const value = startUrl.trim();
|
|
2613
|
-
if (!value || /^(undefined|null)(\/|$)/i.test(value))
|
|
2614
|
-
return null;
|
|
2585
|
+
if (!value || /^(undefined|null)(\/|$)/i.test(value)) return null;
|
|
2615
2586
|
try {
|
|
2616
2587
|
const url = new URL(value, origin);
|
|
2617
|
-
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
2618
|
-
return null;
|
|
2588
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return null;
|
|
2619
2589
|
return url;
|
|
2620
2590
|
} catch {
|
|
2621
2591
|
return null;
|
|
@@ -2640,14 +2610,13 @@ var AdminAuthController = class {
|
|
|
2640
2610
|
return new import_node_util3.TextEncoder().encode(secret);
|
|
2641
2611
|
}
|
|
2642
2612
|
normalizeReturnTo(returnTo, c) {
|
|
2643
|
-
if (returnTo)
|
|
2644
|
-
return returnTo;
|
|
2613
|
+
if (returnTo) return returnTo;
|
|
2645
2614
|
const url = new URL(c.req.url);
|
|
2646
2615
|
return `${url.origin}/admin`;
|
|
2647
2616
|
}
|
|
2648
2617
|
};
|
|
2649
2618
|
|
|
2650
|
-
// src/controllers/preview.controller.
|
|
2619
|
+
// src/controllers/preview.controller.ts
|
|
2651
2620
|
var import_jose3 = require("jose");
|
|
2652
2621
|
var import_node_util4 = require("util");
|
|
2653
2622
|
var PreviewController = class {
|
|
@@ -2690,7 +2659,7 @@ var PreviewController = class {
|
|
|
2690
2659
|
}
|
|
2691
2660
|
};
|
|
2692
2661
|
|
|
2693
|
-
// src/middleware/auth.
|
|
2662
|
+
// src/middleware/auth.ts
|
|
2694
2663
|
function requireAuth() {
|
|
2695
2664
|
return async (c, next) => {
|
|
2696
2665
|
const authHeader = c.req.header("Authorization");
|
|
@@ -2722,7 +2691,7 @@ function optionalAuth() {
|
|
|
2722
2691
|
};
|
|
2723
2692
|
}
|
|
2724
2693
|
|
|
2725
|
-
// src/utils/openapi.
|
|
2694
|
+
// src/utils/openapi.ts
|
|
2726
2695
|
function generateOpenApi(config) {
|
|
2727
2696
|
const spec = {
|
|
2728
2697
|
openapi: "3.0.0",
|
|
@@ -3431,8 +3400,7 @@ function fieldsToProperties(fields) {
|
|
|
3431
3400
|
required.push(...nested.required);
|
|
3432
3401
|
continue;
|
|
3433
3402
|
}
|
|
3434
|
-
if (!field.name)
|
|
3435
|
-
continue;
|
|
3403
|
+
if (!field.name) continue;
|
|
3436
3404
|
props[field.name] = fieldToSchema(field);
|
|
3437
3405
|
if (field.required) {
|
|
3438
3406
|
required.push(field.name);
|
|
@@ -3544,12 +3512,11 @@ function fieldToSchema(field) {
|
|
|
3544
3512
|
default:
|
|
3545
3513
|
schema = { type: "string" };
|
|
3546
3514
|
}
|
|
3547
|
-
if (field.label)
|
|
3548
|
-
schema.description = field.label;
|
|
3515
|
+
if (field.label) schema.description = field.label;
|
|
3549
3516
|
return schema;
|
|
3550
3517
|
}
|
|
3551
3518
|
|
|
3552
|
-
// src/utils/swagger.
|
|
3519
|
+
// src/utils/swagger.ts
|
|
3553
3520
|
function getSwaggerHtml(specUrl = "/api/openapi.json") {
|
|
3554
3521
|
return `
|
|
3555
3522
|
<!DOCTYPE html>
|
|
@@ -3598,7 +3565,7 @@ function getSwaggerHtml(specUrl = "/api/openapi.json") {
|
|
|
3598
3565
|
`;
|
|
3599
3566
|
}
|
|
3600
3567
|
|
|
3601
|
-
// src/router.
|
|
3568
|
+
// src/router.ts
|
|
3602
3569
|
function accessGate(target, action) {
|
|
3603
3570
|
return async (c, next) => {
|
|
3604
3571
|
const user = c.get("user");
|
|
@@ -3615,8 +3582,7 @@ function accessGate(target, action) {
|
|
|
3615
3582
|
};
|
|
3616
3583
|
}
|
|
3617
3584
|
async function checkAccess(access, accessArgs) {
|
|
3618
|
-
if (access === void 0 || access === null)
|
|
3619
|
-
return true;
|
|
3585
|
+
if (access === void 0 || access === null) return true;
|
|
3620
3586
|
if (typeof access === "function") {
|
|
3621
3587
|
try {
|
|
3622
3588
|
const result = await access(accessArgs);
|
|
@@ -3632,8 +3598,7 @@ async function checkAccess(access, accessArgs) {
|
|
|
3632
3598
|
return true;
|
|
3633
3599
|
}
|
|
3634
3600
|
function serializeFieldForApi(f) {
|
|
3635
|
-
if (!f)
|
|
3636
|
-
return f;
|
|
3601
|
+
if (!f) return f;
|
|
3637
3602
|
const serialized = { ...f };
|
|
3638
3603
|
if (serialized.admin?.hooks) {
|
|
3639
3604
|
const hooks = { ...serialized.admin.hooks };
|
|
@@ -3666,10 +3631,8 @@ function registerRoutes(app, config) {
|
|
|
3666
3631
|
let globals = [...config.globals];
|
|
3667
3632
|
if (siteId && config.onSchemaFetch) {
|
|
3668
3633
|
const dynamic = await config.onSchemaFetch(siteId);
|
|
3669
|
-
if (dynamic.collections)
|
|
3670
|
-
|
|
3671
|
-
if (dynamic.globals)
|
|
3672
|
-
globals = [...globals, ...dynamic.globals];
|
|
3634
|
+
if (dynamic.collections) collections = [...collections, ...dynamic.collections];
|
|
3635
|
+
if (dynamic.globals) globals = [...globals, ...dynamic.globals];
|
|
3673
3636
|
if (dynamic.admin) {
|
|
3674
3637
|
config.admin = { ...config.admin, ...dynamic.admin };
|
|
3675
3638
|
}
|
|
@@ -3680,10 +3643,8 @@ function registerRoutes(app, config) {
|
|
|
3680
3643
|
const user = c.get("user");
|
|
3681
3644
|
const accessArgs = { user, req: c.req, doc: null };
|
|
3682
3645
|
const serializeAccess = async (access) => {
|
|
3683
|
-
if (typeof access === "string")
|
|
3684
|
-
|
|
3685
|
-
if (typeof access === "boolean")
|
|
3686
|
-
return access;
|
|
3646
|
+
if (typeof access === "string") return access;
|
|
3647
|
+
if (typeof access === "boolean") return access;
|
|
3687
3648
|
return checkAccess(access, accessArgs);
|
|
3688
3649
|
};
|
|
3689
3650
|
const filteredCollections = await Promise.all(collections.filter((col) => !siteId || col.shared || !col.siteId || col.siteId === siteId).map(async (col) => ({
|
|
@@ -3776,8 +3737,7 @@ function registerRoutes(app, config) {
|
|
|
3776
3737
|
let collections = [...config.collections];
|
|
3777
3738
|
if (siteId && config.onSchemaFetch) {
|
|
3778
3739
|
const dynamic = await config.onSchemaFetch(siteId);
|
|
3779
|
-
if (dynamic.collections)
|
|
3780
|
-
collections = [...collections, ...dynamic.collections];
|
|
3740
|
+
if (dynamic.collections) collections = [...collections, ...dynamic.collections];
|
|
3781
3741
|
}
|
|
3782
3742
|
const user = c.get("user");
|
|
3783
3743
|
let collection = collections.find((col) => col.slug === colSlug);
|
|
@@ -3796,8 +3756,7 @@ function registerRoutes(app, config) {
|
|
|
3796
3756
|
let globals = [...config.globals];
|
|
3797
3757
|
if (siteId && config.onSchemaFetch) {
|
|
3798
3758
|
const dynamic = await config.onSchemaFetch(siteId);
|
|
3799
|
-
if (dynamic.globals)
|
|
3800
|
-
globals = [...globals, ...dynamic.globals];
|
|
3759
|
+
if (dynamic.globals) globals = [...globals, ...dynamic.globals];
|
|
3801
3760
|
}
|
|
3802
3761
|
const glb = globals.find((g) => g.slug === colSlug);
|
|
3803
3762
|
if (!glb) {
|
|
@@ -3855,12 +3814,9 @@ function registerRoutes(app, config) {
|
|
|
3855
3814
|
const user = c.get("user");
|
|
3856
3815
|
const key = c.req.param("key");
|
|
3857
3816
|
const scope = c.req.query("scope");
|
|
3858
|
-
if (!db)
|
|
3859
|
-
|
|
3860
|
-
if (!
|
|
3861
|
-
return c.json({ error: true, message: "Authentication required." }, 401);
|
|
3862
|
-
if (!key)
|
|
3863
|
-
return c.json({ error: true, message: "Preference key is required." }, 400);
|
|
3817
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
3818
|
+
if (!user?.collection || !user.sub) return c.json({ error: true, message: "Authentication required." }, 401);
|
|
3819
|
+
if (!key) return c.json({ error: true, message: "Preference key is required." }, 400);
|
|
3864
3820
|
const getGlobalPreference = async () => {
|
|
3865
3821
|
const globalDoc = await db.findOne({ collection: "__global_preferences", id: key });
|
|
3866
3822
|
return globalDoc ? globalDoc.value : null;
|
|
@@ -3870,8 +3826,7 @@ function registerRoutes(app, config) {
|
|
|
3870
3826
|
return c.json({ key, value: globalValue2 });
|
|
3871
3827
|
}
|
|
3872
3828
|
const doc = await db.findOne({ collection: user.collection, id: user.sub });
|
|
3873
|
-
if (!doc)
|
|
3874
|
-
return c.json({ error: true, message: "User not found." }, 404);
|
|
3829
|
+
if (!doc) return c.json({ error: true, message: "User not found." }, 404);
|
|
3875
3830
|
const preferences = typeof doc.__preferences === "object" && doc.__preferences !== null ? doc.__preferences : {};
|
|
3876
3831
|
if (key in preferences) {
|
|
3877
3832
|
return c.json({ key, value: preferences[key] ?? null });
|
|
@@ -3884,12 +3839,9 @@ function registerRoutes(app, config) {
|
|
|
3884
3839
|
const user = c.get("user");
|
|
3885
3840
|
const key = c.req.param("key");
|
|
3886
3841
|
const scope = c.req.query("scope");
|
|
3887
|
-
if (!db)
|
|
3888
|
-
|
|
3889
|
-
if (!
|
|
3890
|
-
return c.json({ error: true, message: "Authentication required." }, 401);
|
|
3891
|
-
if (!key)
|
|
3892
|
-
return c.json({ error: true, message: "Preference key is required." }, 400);
|
|
3842
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
3843
|
+
if (!user?.collection || !user.sub) return c.json({ error: true, message: "Authentication required." }, 401);
|
|
3844
|
+
if (!key) return c.json({ error: true, message: "Preference key is required." }, 400);
|
|
3893
3845
|
const body = await c.req.json().catch(() => ({}));
|
|
3894
3846
|
if (scope === "global") {
|
|
3895
3847
|
const isAdminUser = Array.isArray(user?.roles) && user.roles.includes("admin");
|
|
@@ -3912,8 +3864,7 @@ function registerRoutes(app, config) {
|
|
|
3912
3864
|
return c.json({ key, value: body.value });
|
|
3913
3865
|
}
|
|
3914
3866
|
const doc = await db.findOne({ collection: user.collection, id: user.sub });
|
|
3915
|
-
if (!doc)
|
|
3916
|
-
return c.json({ error: true, message: "User not found." }, 404);
|
|
3867
|
+
if (!doc) return c.json({ error: true, message: "User not found." }, 404);
|
|
3917
3868
|
const preferences = typeof doc.__preferences === "object" && doc.__preferences !== null ? doc.__preferences : {};
|
|
3918
3869
|
const nextPreferences = { ...preferences, [key]: body.value };
|
|
3919
3870
|
await db.update({
|
|
@@ -3928,12 +3879,9 @@ function registerRoutes(app, config) {
|
|
|
3928
3879
|
const user = c.get("user");
|
|
3929
3880
|
const key = c.req.param("key");
|
|
3930
3881
|
const scope = c.req.query("scope");
|
|
3931
|
-
if (!db)
|
|
3932
|
-
|
|
3933
|
-
if (!
|
|
3934
|
-
return c.json({ error: true, message: "Authentication required." }, 401);
|
|
3935
|
-
if (!key)
|
|
3936
|
-
return c.json({ error: true, message: "Preference key is required." }, 400);
|
|
3882
|
+
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
3883
|
+
if (!user?.collection || !user.sub) return c.json({ error: true, message: "Authentication required." }, 401);
|
|
3884
|
+
if (!key) return c.json({ error: true, message: "Preference key is required." }, 400);
|
|
3937
3885
|
if (scope === "global") {
|
|
3938
3886
|
const isAdminUser = Array.isArray(user?.roles) && user.roles.includes("admin");
|
|
3939
3887
|
if (!isAdminUser) {
|
|
@@ -3943,8 +3891,7 @@ function registerRoutes(app, config) {
|
|
|
3943
3891
|
return c.json({ success: true });
|
|
3944
3892
|
}
|
|
3945
3893
|
const doc = await db.findOne({ collection: user.collection, id: user.sub });
|
|
3946
|
-
if (!doc)
|
|
3947
|
-
return c.json({ error: true, message: "User not found." }, 404);
|
|
3894
|
+
if (!doc) return c.json({ error: true, message: "User not found." }, 404);
|
|
3948
3895
|
const preferences = typeof doc.__preferences === "object" && doc.__preferences !== null ? { ...doc.__preferences } : {};
|
|
3949
3896
|
delete preferences[key];
|
|
3950
3897
|
await db.update({
|
|
@@ -3980,8 +3927,7 @@ function registerRoutes(app, config) {
|
|
|
3980
3927
|
app.post("/api/admin/auth/:provider/exchange", (c) => adminAuthController.exchange(c));
|
|
3981
3928
|
app.post("/api/admin/logout", (c) => adminAuthController.logout(c));
|
|
3982
3929
|
for (const collection of config.collections) {
|
|
3983
|
-
if (!collection.auth)
|
|
3984
|
-
continue;
|
|
3930
|
+
if (!collection.auth) continue;
|
|
3985
3931
|
const path = `/api/collections/${collection.slug}`;
|
|
3986
3932
|
const authController = new AuthController(collection);
|
|
3987
3933
|
app.post(`${path}/login`, (c) => authController.login(c));
|
|
@@ -4083,39 +4029,25 @@ function registerRoutes(app, config) {
|
|
|
4083
4029
|
if (collection.auth && id) {
|
|
4084
4030
|
const authController = new AuthController(collection);
|
|
4085
4031
|
const method2 = c.req.method;
|
|
4086
|
-
if (method2 === "POST" && id === "login")
|
|
4087
|
-
|
|
4088
|
-
if (method2 === "
|
|
4089
|
-
|
|
4090
|
-
if (method2 === "
|
|
4091
|
-
|
|
4092
|
-
if (method2 === "POST" && id === "refresh-token")
|
|
4093
|
-
return authController.refreshToken(c);
|
|
4094
|
-
if (method2 === "POST" && id === "forgot-password")
|
|
4095
|
-
return authController.forgotPassword(c);
|
|
4096
|
-
if (method2 === "POST" && id === "reset-password")
|
|
4097
|
-
return authController.resetPassword(c);
|
|
4032
|
+
if (method2 === "POST" && id === "login") return authController.login(c);
|
|
4033
|
+
if (method2 === "POST" && id === "logout") return authController.logout(c);
|
|
4034
|
+
if (method2 === "GET" && id === "me") return authController.me(c);
|
|
4035
|
+
if (method2 === "POST" && id === "refresh-token") return authController.refreshToken(c);
|
|
4036
|
+
if (method2 === "POST" && id === "forgot-password") return authController.forgotPassword(c);
|
|
4037
|
+
if (method2 === "POST" && id === "reset-password") return authController.resetPassword(c);
|
|
4098
4038
|
}
|
|
4099
4039
|
const controller = new CollectionController(collection);
|
|
4100
4040
|
const method = c.req.method;
|
|
4101
4041
|
if (id) {
|
|
4102
|
-
if (method === "GET")
|
|
4103
|
-
|
|
4104
|
-
if (method === "
|
|
4105
|
-
|
|
4106
|
-
if (method === "
|
|
4107
|
-
|
|
4108
|
-
if (method === "DELETE")
|
|
4109
|
-
return controller.delete(c);
|
|
4110
|
-
if (method === "POST" && id === "media")
|
|
4111
|
-
return controller.create(c);
|
|
4112
|
-
if (method === "POST" && id === "seed")
|
|
4113
|
-
return controller.seed(c);
|
|
4042
|
+
if (method === "GET") return controller.findOne(c);
|
|
4043
|
+
if (method === "PATCH") return controller.update(c);
|
|
4044
|
+
if (method === "DELETE" && id === "delete-many") return controller.deleteMany(c);
|
|
4045
|
+
if (method === "DELETE") return controller.delete(c);
|
|
4046
|
+
if (method === "POST" && id === "media") return controller.create(c);
|
|
4047
|
+
if (method === "POST" && id === "seed") return controller.seed(c);
|
|
4114
4048
|
} else {
|
|
4115
|
-
if (method === "GET")
|
|
4116
|
-
|
|
4117
|
-
if (method === "POST")
|
|
4118
|
-
return controller.create(c);
|
|
4049
|
+
if (method === "GET") return controller.find(c);
|
|
4050
|
+
if (method === "POST") return controller.create(c);
|
|
4119
4051
|
}
|
|
4120
4052
|
}
|
|
4121
4053
|
}
|
|
@@ -4136,13 +4068,10 @@ function registerRoutes(app, config) {
|
|
|
4136
4068
|
const controller = new GlobalController(global);
|
|
4137
4069
|
const method = c.req.method;
|
|
4138
4070
|
if (id) {
|
|
4139
|
-
if (method === "POST" && id === "seed")
|
|
4140
|
-
return controller.seed(c);
|
|
4071
|
+
if (method === "POST" && id === "seed") return controller.seed(c);
|
|
4141
4072
|
} else {
|
|
4142
|
-
if (method === "GET")
|
|
4143
|
-
|
|
4144
|
-
if (method === "PATCH")
|
|
4145
|
-
return controller.update(c);
|
|
4073
|
+
if (method === "GET") return controller.get(c);
|
|
4074
|
+
if (method === "PATCH") return controller.update(c);
|
|
4146
4075
|
}
|
|
4147
4076
|
}
|
|
4148
4077
|
}
|
|
@@ -4150,7 +4079,7 @@ function registerRoutes(app, config) {
|
|
|
4150
4079
|
});
|
|
4151
4080
|
}
|
|
4152
4081
|
|
|
4153
|
-
// src/utils/config.
|
|
4082
|
+
// src/utils/config.ts
|
|
4154
4083
|
var AUDIT_COLLECTION_SLUG = "__audit";
|
|
4155
4084
|
var SYSTEM_FIELDS = [
|
|
4156
4085
|
{
|
|
@@ -4364,8 +4293,7 @@ function normalizeConfig(config) {
|
|
|
4364
4293
|
});
|
|
4365
4294
|
const hasAuditCollection = normalizedCollections.some((col) => col.slug === AUDIT_COLLECTION_SLUG);
|
|
4366
4295
|
const systemCollections = [];
|
|
4367
|
-
if (needsAudit && !hasAuditCollection)
|
|
4368
|
-
systemCollections.push(AUDIT_COLLECTION);
|
|
4296
|
+
if (needsAudit && !hasAuditCollection) systemCollections.push(AUDIT_COLLECTION);
|
|
4369
4297
|
if (needsWorkflow && !normalizedCollections.some((col) => col.slug === WORKFLOW_HISTORY_COLLECTION)) {
|
|
4370
4298
|
systemCollections.push(WORKFLOW_HISTORY_COLLECTION_CONFIG);
|
|
4371
4299
|
}
|
|
@@ -4379,7 +4307,7 @@ function normalizeConfig(config) {
|
|
|
4379
4307
|
};
|
|
4380
4308
|
}
|
|
4381
4309
|
|
|
4382
|
-
// src/app.
|
|
4310
|
+
// src/app.ts
|
|
4383
4311
|
async function createDyrectedApp(rawConfig) {
|
|
4384
4312
|
const config = normalizeConfig(rawConfig);
|
|
4385
4313
|
const app = new import_hono.Hono();
|