@dyrected/core 2.5.43 → 2.5.44
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/dist/app-config-3XBnu9Xz.d.cts +1379 -0
- package/dist/app-config-3XBnu9Xz.d.ts +1379 -0
- package/dist/chunk-44O2LDPT.js +1513 -0
- package/dist/index.cjs +86 -95
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +16 -10
- package/dist/server.cjs +483 -310
- package/dist/server.d.cts +34 -1
- package/dist/server.d.ts +34 -1
- package/dist/server.js +411 -231
- package/dist/where-sanitizer-7Q4JXMX6.js +57 -0
- 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.js
|
|
34
34
|
var where_sanitizer_exports = {};
|
|
35
35
|
__export(where_sanitizer_exports, {
|
|
36
36
|
sanitizeWhereClause: () => sanitizeWhereClause
|
|
@@ -67,9 +67,12 @@ function sanitizeWhereClause(where, fields) {
|
|
|
67
67
|
continue;
|
|
68
68
|
}
|
|
69
69
|
const fieldDef = fieldMap.get(key);
|
|
70
|
-
if (!fieldDef)
|
|
71
|
-
|
|
72
|
-
if (
|
|
70
|
+
if (!fieldDef)
|
|
71
|
+
continue;
|
|
72
|
+
if (fieldDef.admin?.filterable === false)
|
|
73
|
+
continue;
|
|
74
|
+
if (NEVER_FILTERABLE_TYPES.includes(fieldDef.type))
|
|
75
|
+
continue;
|
|
73
76
|
result[key] = value;
|
|
74
77
|
}
|
|
75
78
|
return result;
|
|
@@ -78,7 +81,7 @@ function sanitizeWhereClause(where, fields) {
|
|
|
78
81
|
}
|
|
79
82
|
var NEVER_FILTERABLE_TYPES;
|
|
80
83
|
var init_where_sanitizer = __esm({
|
|
81
|
-
"src/utils/where-sanitizer.
|
|
84
|
+
"src/utils/where-sanitizer.js"() {
|
|
82
85
|
"use strict";
|
|
83
86
|
NEVER_FILTERABLE_TYPES = [
|
|
84
87
|
"password",
|
|
@@ -105,12 +108,12 @@ __export(server_exports, {
|
|
|
105
108
|
});
|
|
106
109
|
module.exports = __toCommonJS(server_exports);
|
|
107
110
|
|
|
108
|
-
// src/app.
|
|
111
|
+
// src/app.js
|
|
109
112
|
var import_hono = require("hono");
|
|
110
113
|
var import_cors = require("hono/cors");
|
|
111
114
|
var import_request_id = require("hono/request-id");
|
|
112
115
|
|
|
113
|
-
// src/services/defaults.service.
|
|
116
|
+
// src/services/defaults.service.js
|
|
114
117
|
var DefaultsService = class {
|
|
115
118
|
/**
|
|
116
119
|
* Recursively apply default values to a data object based on field definitions.
|
|
@@ -118,12 +121,14 @@ var DefaultsService = class {
|
|
|
118
121
|
static apply(fields, data = {}) {
|
|
119
122
|
const result = { ...data || {} };
|
|
120
123
|
fields.forEach((field) => {
|
|
121
|
-
if (field.type === "join")
|
|
124
|
+
if (field.type === "join")
|
|
125
|
+
return;
|
|
122
126
|
if (field.type === "row" && field.fields) {
|
|
123
127
|
Object.assign(result, this.apply(field.fields, data));
|
|
124
128
|
return;
|
|
125
129
|
}
|
|
126
|
-
if (!field.name)
|
|
130
|
+
if (!field.name)
|
|
131
|
+
return;
|
|
127
132
|
let value = result[field.name];
|
|
128
133
|
if ((value === void 0 || value === null) && field.renameTo) {
|
|
129
134
|
const legacyValue = result[field.renameTo];
|
|
@@ -136,9 +141,12 @@ var DefaultsService = class {
|
|
|
136
141
|
if (field.defaultValue !== void 0) {
|
|
137
142
|
result[field.name] = field.defaultValue;
|
|
138
143
|
} else {
|
|
139
|
-
if (field.type === "boolean")
|
|
140
|
-
|
|
141
|
-
else if (field.type === "
|
|
144
|
+
if (field.type === "boolean")
|
|
145
|
+
result[field.name] = false;
|
|
146
|
+
else if (field.type === "array")
|
|
147
|
+
result[field.name] = [];
|
|
148
|
+
else if (field.type === "multiSelect")
|
|
149
|
+
result[field.name] = [];
|
|
142
150
|
else if (field.type === "object") {
|
|
143
151
|
result[field.name] = this.apply(field.fields || [], {});
|
|
144
152
|
}
|
|
@@ -153,7 +161,7 @@ var DefaultsService = class {
|
|
|
153
161
|
}
|
|
154
162
|
};
|
|
155
163
|
|
|
156
|
-
// src/services/population.service.
|
|
164
|
+
// src/services/population.service.js
|
|
157
165
|
var PopulationService = class {
|
|
158
166
|
db;
|
|
159
167
|
collections;
|
|
@@ -206,29 +214,31 @@ var PopulationService = class {
|
|
|
206
214
|
Object.assign(populatedDoc, rowPopulated);
|
|
207
215
|
continue;
|
|
208
216
|
}
|
|
209
|
-
if (!field.name)
|
|
217
|
+
if (!field.name)
|
|
218
|
+
continue;
|
|
210
219
|
const value = populatedDoc[field.name];
|
|
211
220
|
if (field.type === "relationship" && field.relationTo && value) {
|
|
212
221
|
const relatedCollection = this.collections.find((c) => c.slug === field.relationTo);
|
|
213
|
-
if (!relatedCollection)
|
|
222
|
+
if (!relatedCollection)
|
|
223
|
+
continue;
|
|
214
224
|
if (Array.isArray(value)) {
|
|
215
|
-
populatedDoc[field.name] = await Promise.all(
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
})
|
|
231
|
-
);
|
|
225
|
+
populatedDoc[field.name] = await Promise.all(value.map(async (id) => {
|
|
226
|
+
if (!id)
|
|
227
|
+
return id;
|
|
228
|
+
let doc = id;
|
|
229
|
+
if (typeof id === "string") {
|
|
230
|
+
doc = await this.db.findOne({ collection: field.relationTo, id });
|
|
231
|
+
}
|
|
232
|
+
if (!doc || typeof doc !== "object")
|
|
233
|
+
return id;
|
|
234
|
+
const docWithDefaults = DefaultsService.apply(relatedCollection.fields, doc);
|
|
235
|
+
return this.populate({
|
|
236
|
+
data: docWithDefaults,
|
|
237
|
+
fields: relatedCollection.fields,
|
|
238
|
+
currentDepth: currentDepth + 1,
|
|
239
|
+
maxDepth
|
|
240
|
+
});
|
|
241
|
+
}));
|
|
232
242
|
} else if (value) {
|
|
233
243
|
let doc = value;
|
|
234
244
|
if (typeof value === "string") {
|
|
@@ -277,18 +287,17 @@ var PopulationService = class {
|
|
|
277
287
|
});
|
|
278
288
|
}
|
|
279
289
|
if (field.type === "blocks" && field.blocks && Array.isArray(value)) {
|
|
280
|
-
populatedDoc[field.name] = await Promise.all(
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
);
|
|
290
|
+
populatedDoc[field.name] = await Promise.all(value.map(async (blockData) => {
|
|
291
|
+
const blockConfig = field.blocks.find((b) => b.slug === blockData.blockType);
|
|
292
|
+
if (!blockConfig)
|
|
293
|
+
return blockData;
|
|
294
|
+
return this.populate({
|
|
295
|
+
data: blockData,
|
|
296
|
+
fields: blockConfig.fields,
|
|
297
|
+
currentDepth,
|
|
298
|
+
maxDepth
|
|
299
|
+
});
|
|
300
|
+
}));
|
|
292
301
|
}
|
|
293
302
|
}
|
|
294
303
|
return populatedDoc;
|
|
@@ -297,7 +306,8 @@ var PopulationService = class {
|
|
|
297
306
|
* Helper to populate a PaginatedResult
|
|
298
307
|
*/
|
|
299
308
|
async populateResult(result, fields, maxDepth) {
|
|
300
|
-
if (maxDepth <= 0)
|
|
309
|
+
if (maxDepth <= 0)
|
|
310
|
+
return result;
|
|
301
311
|
const populatedDocs = await this.populate({
|
|
302
312
|
data: result.docs,
|
|
303
313
|
fields,
|
|
@@ -311,7 +321,7 @@ var PopulationService = class {
|
|
|
311
321
|
}
|
|
312
322
|
};
|
|
313
323
|
|
|
314
|
-
// src/services/audit.service.
|
|
324
|
+
// src/services/audit.service.js
|
|
315
325
|
var AuditService = class {
|
|
316
326
|
/**
|
|
317
327
|
* Writes a single entry to the __audit collection.
|
|
@@ -339,7 +349,7 @@ var AuditService = class {
|
|
|
339
349
|
}
|
|
340
350
|
};
|
|
341
351
|
|
|
342
|
-
// src/auth/password.
|
|
352
|
+
// src/auth/password.js
|
|
343
353
|
var import_node_util = require("util");
|
|
344
354
|
var import_node_crypto = require("crypto");
|
|
345
355
|
var scryptAsync = (0, import_node_util.promisify)(import_node_crypto.scrypt);
|
|
@@ -352,14 +362,16 @@ async function hashPassword(plain) {
|
|
|
352
362
|
}
|
|
353
363
|
async function verifyPassword(plain, stored) {
|
|
354
364
|
const [salt, storedHash] = stored.split(":");
|
|
355
|
-
if (!salt || !storedHash)
|
|
365
|
+
if (!salt || !storedHash)
|
|
366
|
+
return false;
|
|
356
367
|
const derivedKey = await scryptAsync(plain, salt, KEY_LEN);
|
|
357
368
|
const storedBuffer = Buffer.from(storedHash, "hex");
|
|
358
|
-
if (derivedKey.length !== storedBuffer.length)
|
|
369
|
+
if (derivedKey.length !== storedBuffer.length)
|
|
370
|
+
return false;
|
|
359
371
|
return (0, import_node_crypto.timingSafeEqual)(derivedKey, storedBuffer);
|
|
360
372
|
}
|
|
361
373
|
|
|
362
|
-
// src/utils/hooks.
|
|
374
|
+
// src/utils/hooks.js
|
|
363
375
|
async function runCollectionHooks(hooks, args, options = {}) {
|
|
364
376
|
if (!hooks || hooks.length === 0) {
|
|
365
377
|
return args.data ?? args.doc ?? void 0;
|
|
@@ -386,10 +398,12 @@ async function runCollectionHooks(hooks, args, options = {}) {
|
|
|
386
398
|
return currentPayload;
|
|
387
399
|
}
|
|
388
400
|
async function executeFieldBeforeChange(fields, data, originalDoc, user, db) {
|
|
389
|
-
if (!data || typeof data !== "object")
|
|
401
|
+
if (!data || typeof data !== "object")
|
|
402
|
+
return data;
|
|
390
403
|
const result = { ...data };
|
|
391
404
|
for (const field of fields) {
|
|
392
|
-
if (!field.name)
|
|
405
|
+
if (!field.name)
|
|
406
|
+
continue;
|
|
393
407
|
const value = result[field.name];
|
|
394
408
|
const origValue = originalDoc?.[field.name];
|
|
395
409
|
let updatedValue = value;
|
|
@@ -409,21 +423,13 @@ async function executeFieldBeforeChange(fields, data, originalDoc, user, db) {
|
|
|
409
423
|
}
|
|
410
424
|
if (updatedValue !== void 0 && updatedValue !== null) {
|
|
411
425
|
if (field.type === "object" && field.fields) {
|
|
412
|
-
result[field.name] = await executeFieldBeforeChange(
|
|
413
|
-
field.fields,
|
|
414
|
-
updatedValue,
|
|
415
|
-
origValue,
|
|
416
|
-
user,
|
|
417
|
-
db
|
|
418
|
-
);
|
|
426
|
+
result[field.name] = await executeFieldBeforeChange(field.fields, updatedValue, origValue, user, db);
|
|
419
427
|
} else if (field.type === "array" && field.fields && Array.isArray(updatedValue)) {
|
|
420
428
|
const arrayResult = [];
|
|
421
429
|
for (let i = 0; i < updatedValue.length; i++) {
|
|
422
430
|
const item = updatedValue[i];
|
|
423
431
|
const origItem = Array.isArray(origValue) ? origValue[i] : null;
|
|
424
|
-
arrayResult.push(
|
|
425
|
-
await executeFieldBeforeChange(field.fields, item, origItem, user, db)
|
|
426
|
-
);
|
|
432
|
+
arrayResult.push(await executeFieldBeforeChange(field.fields, item, origItem, user, db));
|
|
427
433
|
}
|
|
428
434
|
result[field.name] = arrayResult;
|
|
429
435
|
} else if (field.type === "blocks" && field.blocks && Array.isArray(updatedValue)) {
|
|
@@ -431,19 +437,9 @@ async function executeFieldBeforeChange(fields, data, originalDoc, user, db) {
|
|
|
431
437
|
for (let i = 0; i < updatedValue.length; i++) {
|
|
432
438
|
const blockData = updatedValue[i];
|
|
433
439
|
const origBlock = Array.isArray(origValue) ? origValue[i] : null;
|
|
434
|
-
const blockConfig = field.blocks.find(
|
|
435
|
-
(b) => b.slug === blockData.blockType
|
|
436
|
-
);
|
|
440
|
+
const blockConfig = field.blocks.find((b) => b.slug === blockData.blockType);
|
|
437
441
|
if (blockConfig) {
|
|
438
|
-
blocksResult.push(
|
|
439
|
-
await executeFieldBeforeChange(
|
|
440
|
-
blockConfig.fields,
|
|
441
|
-
blockData,
|
|
442
|
-
origBlock,
|
|
443
|
-
user,
|
|
444
|
-
db
|
|
445
|
-
)
|
|
446
|
-
);
|
|
442
|
+
blocksResult.push(await executeFieldBeforeChange(blockConfig.fields, blockData, origBlock, user, db));
|
|
447
443
|
} else {
|
|
448
444
|
blocksResult.push(blockData);
|
|
449
445
|
}
|
|
@@ -455,10 +451,12 @@ async function executeFieldBeforeChange(fields, data, originalDoc, user, db) {
|
|
|
455
451
|
return result;
|
|
456
452
|
}
|
|
457
453
|
async function executeFieldAfterRead(fields, doc, user, db) {
|
|
458
|
-
if (!doc || typeof doc !== "object")
|
|
454
|
+
if (!doc || typeof doc !== "object")
|
|
455
|
+
return doc;
|
|
459
456
|
const result = { ...doc };
|
|
460
457
|
for (const field of fields) {
|
|
461
|
-
if (!field.name)
|
|
458
|
+
if (!field.name)
|
|
459
|
+
continue;
|
|
462
460
|
const value = result[field.name];
|
|
463
461
|
let updatedValue = value;
|
|
464
462
|
if (field.hooks?.afterRead) {
|
|
@@ -476,41 +474,20 @@ async function executeFieldAfterRead(fields, doc, user, db) {
|
|
|
476
474
|
}
|
|
477
475
|
if (updatedValue !== void 0 && updatedValue !== null) {
|
|
478
476
|
if (field.type === "object" && field.fields) {
|
|
479
|
-
result[field.name] = await executeFieldAfterRead(
|
|
480
|
-
field.fields,
|
|
481
|
-
updatedValue,
|
|
482
|
-
user,
|
|
483
|
-
db
|
|
484
|
-
);
|
|
477
|
+
result[field.name] = await executeFieldAfterRead(field.fields, updatedValue, user, db);
|
|
485
478
|
} else if (field.type === "array" && field.fields && Array.isArray(updatedValue)) {
|
|
486
479
|
const arrayResult = [];
|
|
487
480
|
for (const item of updatedValue) {
|
|
488
|
-
arrayResult.push(
|
|
489
|
-
await executeFieldAfterRead(
|
|
490
|
-
field.fields,
|
|
491
|
-
item,
|
|
492
|
-
user,
|
|
493
|
-
db
|
|
494
|
-
)
|
|
495
|
-
);
|
|
481
|
+
arrayResult.push(await executeFieldAfterRead(field.fields, item, user, db));
|
|
496
482
|
}
|
|
497
483
|
result[field.name] = arrayResult;
|
|
498
484
|
} else if (field.type === "blocks" && field.blocks && Array.isArray(updatedValue)) {
|
|
499
485
|
const blocksResult = [];
|
|
500
486
|
for (const blockData of updatedValue) {
|
|
501
487
|
const typedBlock = blockData;
|
|
502
|
-
const blockConfig = field.blocks.find(
|
|
503
|
-
(b) => b.slug === typedBlock.blockType
|
|
504
|
-
);
|
|
488
|
+
const blockConfig = field.blocks.find((b) => b.slug === typedBlock.blockType);
|
|
505
489
|
if (blockConfig) {
|
|
506
|
-
blocksResult.push(
|
|
507
|
-
await executeFieldAfterRead(
|
|
508
|
-
blockConfig.fields,
|
|
509
|
-
typedBlock,
|
|
510
|
-
user,
|
|
511
|
-
db
|
|
512
|
-
)
|
|
513
|
-
);
|
|
490
|
+
blocksResult.push(await executeFieldAfterRead(blockConfig.fields, typedBlock, user, db));
|
|
514
491
|
} else {
|
|
515
492
|
blocksResult.push(blockData);
|
|
516
493
|
}
|
|
@@ -522,16 +499,14 @@ async function executeFieldAfterRead(fields, doc, user, db) {
|
|
|
522
499
|
return result;
|
|
523
500
|
}
|
|
524
501
|
|
|
525
|
-
// src/utils/readonly-db.
|
|
502
|
+
// src/utils/readonly-db.js
|
|
526
503
|
var WRITE_METHODS = ["create", "update", "delete", "updateGlobal", "execute"];
|
|
527
504
|
function createReadonlyDb(db) {
|
|
528
505
|
return new Proxy(db, {
|
|
529
506
|
get(target, prop) {
|
|
530
507
|
if (WRITE_METHODS.includes(prop)) {
|
|
531
508
|
return () => {
|
|
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
|
-
);
|
|
509
|
+
throw new Error(`[dyrected] Write operation "${String(prop)}" is not allowed in this hook phase. Use afterChange/afterDelete hooks for write operations.`);
|
|
535
510
|
};
|
|
536
511
|
}
|
|
537
512
|
return Reflect.get(target, prop);
|
|
@@ -539,11 +514,13 @@ function createReadonlyDb(db) {
|
|
|
539
514
|
});
|
|
540
515
|
}
|
|
541
516
|
|
|
542
|
-
// src/auth/jexl.
|
|
517
|
+
// src/auth/jexl.js
|
|
543
518
|
var import_jexl = __toESM(require("jexl"), 1);
|
|
544
519
|
async function evaluateAccess(expression, context) {
|
|
545
|
-
if (expression === void 0 || expression === null)
|
|
546
|
-
|
|
520
|
+
if (expression === void 0 || expression === null)
|
|
521
|
+
return false;
|
|
522
|
+
if (typeof expression === "boolean")
|
|
523
|
+
return expression;
|
|
547
524
|
try {
|
|
548
525
|
const result = await import_jexl.default.eval(expression, context);
|
|
549
526
|
return !!result;
|
|
@@ -553,7 +530,7 @@ async function evaluateAccess(expression, context) {
|
|
|
553
530
|
}
|
|
554
531
|
}
|
|
555
532
|
|
|
556
|
-
// src/workflows.
|
|
533
|
+
// src/workflows.js
|
|
557
534
|
var WORKFLOW_HISTORY_COLLECTION = "__workflow_history";
|
|
558
535
|
var LIFECYCLE_EVENTS_COLLECTION = "__lifecycle_events";
|
|
559
536
|
function publicMetadata(meta) {
|
|
@@ -564,17 +541,18 @@ function workflowCapabilities(workflow, user) {
|
|
|
564
541
|
const capabilities = new Set(Array.isArray(user?.capabilities) ? user.capabilities : []);
|
|
565
542
|
const roles = Array.isArray(user?.roles) ? user.roles : [];
|
|
566
543
|
for (const mapping of workflow.roles ?? []) {
|
|
567
|
-
if (roles.includes(mapping.role))
|
|
544
|
+
if (roles.includes(mapping.role))
|
|
545
|
+
mapping.capabilities.forEach((capability) => capabilities.add(capability));
|
|
568
546
|
}
|
|
569
547
|
return capabilities;
|
|
570
548
|
}
|
|
571
549
|
function canViewWorkflowDraft(workflow, user) {
|
|
572
|
-
if (!user)
|
|
550
|
+
if (!user)
|
|
551
|
+
return false;
|
|
573
552
|
const capabilities = workflowCapabilities(workflow, user);
|
|
574
|
-
if (capabilities.has("entry.edit"))
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
);
|
|
553
|
+
if (capabilities.has("entry.edit"))
|
|
554
|
+
return true;
|
|
555
|
+
return workflow.transitions.some((transition) => (transition.requiredCapabilities ?? []).some((capability) => capabilities.has(capability)));
|
|
578
556
|
}
|
|
579
557
|
function availableWorkflowTransitions(workflow, state, user) {
|
|
580
558
|
const capabilities = workflowCapabilities(workflow, user);
|
|
@@ -591,10 +569,12 @@ function initializeWorkflowDocument(data, workflow) {
|
|
|
591
569
|
}
|
|
592
570
|
function materializeWorkflowDocument(doc, workflow, user) {
|
|
593
571
|
const meta = doc.__workflow;
|
|
594
|
-
if (!meta)
|
|
572
|
+
if (!meta)
|
|
573
|
+
return doc;
|
|
595
574
|
const { __published, __workflow, ...working } = doc;
|
|
596
575
|
if (!canViewWorkflowDraft(workflow, user)) {
|
|
597
|
-
if (!__published || typeof __published !== "object")
|
|
576
|
+
if (!__published || typeof __published !== "object")
|
|
577
|
+
return null;
|
|
598
578
|
return { id: doc.id, ...__published, _workflow: publicMetadata(meta) };
|
|
599
579
|
}
|
|
600
580
|
return {
|
|
@@ -626,13 +606,15 @@ async function persistEvent(db, event) {
|
|
|
626
606
|
}
|
|
627
607
|
async function dispatchLifecycleEvent(config, event) {
|
|
628
608
|
const db = config.db;
|
|
629
|
-
if (!db || !config.events?.handlers.length)
|
|
609
|
+
if (!db || !config.events?.handlers.length)
|
|
610
|
+
return;
|
|
630
611
|
const maxAttempts = config.events.maxAttempts ?? 8;
|
|
631
612
|
const retryDelayMs = config.events.retryDelayMs ?? 1e3;
|
|
632
613
|
const attempts = event.attempts + 1;
|
|
633
614
|
try {
|
|
634
615
|
await db.update({ collection: LIFECYCLE_EVENTS_COLLECTION, id: event.id, data: { status: "processing", attempts } });
|
|
635
|
-
for (const handler of config.events.handlers)
|
|
616
|
+
for (const handler of config.events.handlers)
|
|
617
|
+
await handler({ ...event, status: "processing", attempts });
|
|
636
618
|
await db.update({
|
|
637
619
|
collection: LIFECYCLE_EVENTS_COLLECTION,
|
|
638
620
|
id: event.id,
|
|
@@ -656,7 +638,8 @@ async function saveWorkflowDraft(args) {
|
|
|
656
638
|
const { config, collection, id, originalDoc, data, user } = args;
|
|
657
639
|
const db = config.db;
|
|
658
640
|
const workflow = collection.workflow;
|
|
659
|
-
if (!db.transaction)
|
|
641
|
+
if (!db.transaction)
|
|
642
|
+
throw new Error(`The configured database adapter does not support workflow transactions.`);
|
|
660
643
|
const previous = originalDoc.__workflow;
|
|
661
644
|
const event = createLifecycleEvent({
|
|
662
645
|
name: "revision.created",
|
|
@@ -681,7 +664,8 @@ async function saveWorkflowDraft(args) {
|
|
|
681
664
|
async function createWorkflowDocument(args) {
|
|
682
665
|
const { config, collection, data, user } = args;
|
|
683
666
|
const db = config.db;
|
|
684
|
-
if (!db.transaction)
|
|
667
|
+
if (!db.transaction)
|
|
668
|
+
throw new Error(`The configured database adapter does not support workflow transactions.`);
|
|
685
669
|
let event;
|
|
686
670
|
const doc = await db.transaction(async (tx) => {
|
|
687
671
|
const created = await tx.create({ collection: collection.slug, data });
|
|
@@ -702,11 +686,14 @@ async function transitionWorkflow(args) {
|
|
|
702
686
|
const { config, collection, id, transitionName, expectedRevision, comment, user, req } = args;
|
|
703
687
|
const db = config.db;
|
|
704
688
|
const workflow = collection.workflow;
|
|
705
|
-
if (!db.transaction)
|
|
689
|
+
if (!db.transaction)
|
|
690
|
+
throw new Error(`The configured database adapter does not support workflow transactions.`);
|
|
706
691
|
const original = await db.findOne({ collection: collection.slug, id });
|
|
707
|
-
if (!original)
|
|
692
|
+
if (!original)
|
|
693
|
+
throw Object.assign(new Error("Not Found"), { statusCode: 404 });
|
|
708
694
|
const meta = original.__workflow;
|
|
709
|
-
if (!meta)
|
|
695
|
+
if (!meta)
|
|
696
|
+
throw Object.assign(new Error("Entry has no workflow metadata"), { statusCode: 409 });
|
|
710
697
|
if (expectedRevision !== void 0 && expectedRevision !== meta.revision) {
|
|
711
698
|
throw Object.assign(new Error("This entry changed since it was loaded. Refresh before transitioning it."), { statusCode: 409 });
|
|
712
699
|
}
|
|
@@ -722,7 +709,8 @@ async function transitionWorkflow(args) {
|
|
|
722
709
|
throw Object.assign(new Error(`A comment is required for "${transition.label}".`), { statusCode: 400 });
|
|
723
710
|
}
|
|
724
711
|
const hookContext = { transition, from: meta.state, to: transition.to, doc: original, user, comment, req, db };
|
|
725
|
-
for (const hook of workflow.hooks?.beforeTransition ?? [])
|
|
712
|
+
for (const hook of workflow.hooks?.beforeTransition ?? [])
|
|
713
|
+
await hook(hookContext);
|
|
726
714
|
const events = [createLifecycleEvent({
|
|
727
715
|
name: "workflow.transitioned",
|
|
728
716
|
collection: collection.slug,
|
|
@@ -738,7 +726,8 @@ async function transitionWorkflow(args) {
|
|
|
738
726
|
}
|
|
739
727
|
const updated = await db.transaction(async (tx) => {
|
|
740
728
|
const locked = await tx.findOne({ collection: collection.slug, id });
|
|
741
|
-
if (!locked)
|
|
729
|
+
if (!locked)
|
|
730
|
+
throw Object.assign(new Error("Not Found"), { statusCode: 404 });
|
|
742
731
|
const lockedMeta = locked.__workflow;
|
|
743
732
|
if (lockedMeta.revision !== meta.revision || lockedMeta.state !== meta.state || expectedRevision !== void 0 && lockedMeta.revision !== expectedRevision) {
|
|
744
733
|
throw Object.assign(new Error("This entry changed since it was loaded. Refresh before transitioning it."), { statusCode: 409 });
|
|
@@ -752,20 +741,24 @@ async function transitionWorkflow(args) {
|
|
|
752
741
|
...transition.unpublish ? { publishedRevision: void 0, publishedAt: void 0, publishedBy: void 0 } : {}
|
|
753
742
|
};
|
|
754
743
|
const data = { __workflow: nextMeta };
|
|
755
|
-
if (targetState.published)
|
|
756
|
-
|
|
744
|
+
if (targetState.published)
|
|
745
|
+
data.__published = working;
|
|
746
|
+
if (transition.unpublish)
|
|
747
|
+
data.__published = null;
|
|
757
748
|
const next = await tx.update({ collection: collection.slug, id, data });
|
|
758
749
|
await tx.create({
|
|
759
750
|
collection: WORKFLOW_HISTORY_COLLECTION,
|
|
760
751
|
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 }
|
|
761
752
|
});
|
|
762
|
-
for (const event of events)
|
|
753
|
+
for (const event of events)
|
|
754
|
+
await persistEvent(tx, event);
|
|
763
755
|
if (collection.audit) {
|
|
764
756
|
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 }) } });
|
|
765
757
|
}
|
|
766
758
|
return next;
|
|
767
759
|
});
|
|
768
|
-
for (const event of events)
|
|
760
|
+
for (const event of events)
|
|
761
|
+
void dispatchLifecycleEvent(config, event);
|
|
769
762
|
for (const hook of workflow.hooks?.afterTransition ?? []) {
|
|
770
763
|
try {
|
|
771
764
|
await hook({ ...hookContext, doc: updated, event: events[0] });
|
|
@@ -776,16 +769,61 @@ async function transitionWorkflow(args) {
|
|
|
776
769
|
return updated;
|
|
777
770
|
}
|
|
778
771
|
|
|
779
|
-
// src/controllers/collection.controller.
|
|
772
|
+
// src/controllers/collection.controller.js
|
|
780
773
|
var CollectionController = class {
|
|
781
774
|
collection;
|
|
782
775
|
constructor(collection) {
|
|
783
776
|
this.collection = collection;
|
|
784
777
|
}
|
|
778
|
+
getDelegatedProvider(c) {
|
|
779
|
+
const config = c.get("config");
|
|
780
|
+
const authCollectionSlug = config.adminAuth?.collectionSlug || "__admins";
|
|
781
|
+
if (this.collection.slug !== authCollectionSlug) {
|
|
782
|
+
return null;
|
|
783
|
+
}
|
|
784
|
+
return config.adminAuth?.providers?.find((p) => p.members) || null;
|
|
785
|
+
}
|
|
785
786
|
async find(c) {
|
|
786
787
|
const config = c.get("config");
|
|
787
788
|
const db = config.db;
|
|
788
|
-
if (!db)
|
|
789
|
+
if (!db)
|
|
790
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
791
|
+
const provider = this.getDelegatedProvider(c);
|
|
792
|
+
if (provider && provider.members?.list) {
|
|
793
|
+
const limit2 = Number(c.req.query("limit")) || 10;
|
|
794
|
+
const page2 = Number(c.req.query("page")) || 1;
|
|
795
|
+
const sort2 = c.req.query("sort") || void 0;
|
|
796
|
+
let where2 = void 0;
|
|
797
|
+
const whereRaw2 = c.req.query("where");
|
|
798
|
+
if (whereRaw2) {
|
|
799
|
+
try {
|
|
800
|
+
where2 = JSON.parse(decodeURIComponent(whereRaw2));
|
|
801
|
+
} catch {
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
const paginatedResult = await provider.members.list({ limit: limit2, page: page2, sort: sort2, where: where2, req: c.req });
|
|
805
|
+
const mappedDocs = [];
|
|
806
|
+
for (const m of paginatedResult.docs) {
|
|
807
|
+
let localId = m.id;
|
|
808
|
+
const localDoc = await db.find({
|
|
809
|
+
collection: this.collection.slug,
|
|
810
|
+
where: m.id ? { externalSubject: { equals: m.id } } : { email: { equals: m.email } },
|
|
811
|
+
limit: 1
|
|
812
|
+
});
|
|
813
|
+
if (localDoc.docs[0]) {
|
|
814
|
+
localId = localDoc.docs[0].id;
|
|
815
|
+
}
|
|
816
|
+
mappedDocs.push({
|
|
817
|
+
...m,
|
|
818
|
+
id: localId,
|
|
819
|
+
externalSubject: m.id
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
return c.json({
|
|
823
|
+
...paginatedResult,
|
|
824
|
+
docs: mappedDocs
|
|
825
|
+
});
|
|
826
|
+
}
|
|
789
827
|
const readonlyDb = createReadonlyDb(db);
|
|
790
828
|
const limit = Number(c.req.query("limit")) || 10;
|
|
791
829
|
const page = Number(c.req.query("page")) || 1;
|
|
@@ -843,11 +881,13 @@ var CollectionController = class {
|
|
|
843
881
|
where
|
|
844
882
|
});
|
|
845
883
|
}
|
|
846
|
-
result.docs = result.docs.map((doc) => this.collection.workflow ? materializeWorkflowDocument(doc, this.collection.workflow, user) : doc).filter((doc) => doc !== null)
|
|
884
|
+
result.docs = result.docs.map((doc) => this.collection.workflow ? materializeWorkflowDocument(doc, this.collection.workflow, user) : doc).filter((doc) => doc !== null);
|
|
847
885
|
const processedDocs = [];
|
|
886
|
+
const readonlyDbForHooks = createReadonlyDb(db);
|
|
848
887
|
for (const doc of result.docs) {
|
|
888
|
+
const docWithDefaults = DefaultsService.apply(this.collection.fields, doc);
|
|
849
889
|
const docWithCollectionHooks = await runCollectionHooks(this.collection.hooks?.afterRead, {
|
|
850
|
-
doc,
|
|
890
|
+
doc: docWithDefaults,
|
|
851
891
|
req: c.req,
|
|
852
892
|
user,
|
|
853
893
|
db: readonlyDb
|
|
@@ -865,17 +905,43 @@ var CollectionController = class {
|
|
|
865
905
|
async findOne(c) {
|
|
866
906
|
const config = c.get("config");
|
|
867
907
|
const db = config.db;
|
|
868
|
-
if (!db)
|
|
908
|
+
if (!db)
|
|
909
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
910
|
+
const provider = this.getDelegatedProvider(c);
|
|
911
|
+
if (provider && (provider.members?.get || provider.members?.list)) {
|
|
912
|
+
const id2 = c.req.param("id");
|
|
913
|
+
if (!id2)
|
|
914
|
+
return c.json({ message: "Missing ID" }, 400);
|
|
915
|
+
const localDoc = await db.findOne({ collection: this.collection.slug, id: id2 });
|
|
916
|
+
const externalSubject = localDoc?.externalSubject || id2;
|
|
917
|
+
let member = null;
|
|
918
|
+
if (provider.members.get) {
|
|
919
|
+
member = await provider.members.get({ externalSubject, req: c.req });
|
|
920
|
+
} else if (provider.members.list) {
|
|
921
|
+
const listResult = await provider.members.list({ req: c.req });
|
|
922
|
+
member = listResult.docs.find((m) => m.id === externalSubject) || null;
|
|
923
|
+
}
|
|
924
|
+
if (!member)
|
|
925
|
+
return c.json({ message: "Not Found" }, 404);
|
|
926
|
+
return c.json({
|
|
927
|
+
...member,
|
|
928
|
+
id: localDoc ? localDoc.id : member.id,
|
|
929
|
+
externalSubject: member.id
|
|
930
|
+
});
|
|
931
|
+
}
|
|
869
932
|
const readonlyDb = createReadonlyDb(db);
|
|
870
933
|
const id = c.req.param("id");
|
|
871
934
|
const depth = c.req.query("depth") !== void 0 ? Number(c.req.query("depth")) : 10;
|
|
872
935
|
const user = c.get("user");
|
|
873
|
-
if (!id)
|
|
936
|
+
if (!id)
|
|
937
|
+
return c.json({ message: "Missing ID" }, 400);
|
|
874
938
|
let doc = await db.findOne({ collection: this.collection.slug, id });
|
|
875
|
-
if (!doc)
|
|
939
|
+
if (!doc)
|
|
940
|
+
return c.json({ message: "Not Found" }, 404);
|
|
876
941
|
if (this.collection.workflow) {
|
|
877
942
|
doc = materializeWorkflowDocument(doc, this.collection.workflow, user);
|
|
878
|
-
if (!doc)
|
|
943
|
+
if (!doc)
|
|
944
|
+
return c.json({ message: "Not Found" }, 404);
|
|
879
945
|
}
|
|
880
946
|
const docWithDefaults = DefaultsService.apply(this.collection.fields, doc);
|
|
881
947
|
const docWithCollectionHooks = await runCollectionHooks(this.collection.hooks?.afterRead, {
|
|
@@ -900,7 +966,22 @@ var CollectionController = class {
|
|
|
900
966
|
async create(c) {
|
|
901
967
|
const config = c.get("config");
|
|
902
968
|
const db = config.db;
|
|
903
|
-
if (!db)
|
|
969
|
+
if (!db)
|
|
970
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
971
|
+
const provider = this.getDelegatedProvider(c);
|
|
972
|
+
if (provider && provider.members?.create) {
|
|
973
|
+
const contentType2 = c.req.header("Content-Type") || "";
|
|
974
|
+
if (contentType2.toLowerCase().includes("multipart/form-data")) {
|
|
975
|
+
return this.upload(c);
|
|
976
|
+
}
|
|
977
|
+
const body2 = await c.req.json();
|
|
978
|
+
const member = await provider.members.create({ data: body2, req: c.req });
|
|
979
|
+
return c.json({
|
|
980
|
+
...member,
|
|
981
|
+
id: member.id,
|
|
982
|
+
externalSubject: member.id
|
|
983
|
+
}, 201);
|
|
984
|
+
}
|
|
904
985
|
const readonlyDb = createReadonlyDb(db);
|
|
905
986
|
const contentType = c.req.header("Content-Type") || "";
|
|
906
987
|
if (contentType.toLowerCase().includes("multipart/form-data")) {
|
|
@@ -961,13 +1042,16 @@ var CollectionController = class {
|
|
|
961
1042
|
async upload(c) {
|
|
962
1043
|
const config = c.get("config");
|
|
963
1044
|
const storage = config.storage;
|
|
964
|
-
if (!storage)
|
|
1045
|
+
if (!storage)
|
|
1046
|
+
return c.json({ message: "Storage not configured" }, 500);
|
|
965
1047
|
const db = config.db;
|
|
966
|
-
if (!db)
|
|
1048
|
+
if (!db)
|
|
1049
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
967
1050
|
const readonlyDb = createReadonlyDb(db);
|
|
968
1051
|
const formData = await c.req.formData();
|
|
969
1052
|
const file = formData.get("file");
|
|
970
|
-
if (!file)
|
|
1053
|
+
if (!file)
|
|
1054
|
+
return c.json({ message: "No file uploaded" }, 400);
|
|
971
1055
|
const buffer = new Uint8Array(await file.arrayBuffer());
|
|
972
1056
|
const siteId = c.get("siteId");
|
|
973
1057
|
const workspaceId = c.get("workspaceId");
|
|
@@ -1026,10 +1110,27 @@ var CollectionController = class {
|
|
|
1026
1110
|
async update(c) {
|
|
1027
1111
|
const config = c.get("config");
|
|
1028
1112
|
const db = config.db;
|
|
1029
|
-
if (!db)
|
|
1113
|
+
if (!db)
|
|
1114
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1115
|
+
const provider = this.getDelegatedProvider(c);
|
|
1116
|
+
if (provider && provider.members?.update) {
|
|
1117
|
+
const id2 = c.req.param("id");
|
|
1118
|
+
if (!id2)
|
|
1119
|
+
return c.json({ message: "Missing ID" }, 400);
|
|
1120
|
+
const body2 = await c.req.json();
|
|
1121
|
+
const localDoc = await db.findOne({ collection: this.collection.slug, id: id2 });
|
|
1122
|
+
const externalSubject = localDoc?.externalSubject || id2;
|
|
1123
|
+
const member = await provider.members.update({ externalSubject, data: body2, req: c.req });
|
|
1124
|
+
return c.json({
|
|
1125
|
+
...member,
|
|
1126
|
+
id: localDoc ? localDoc.id : member.id,
|
|
1127
|
+
externalSubject: member.id
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1030
1130
|
const readonlyDb = createReadonlyDb(db);
|
|
1031
1131
|
const id = c.req.param("id");
|
|
1032
|
-
if (!id)
|
|
1132
|
+
if (!id)
|
|
1133
|
+
return c.json({ message: "Missing ID" }, 400);
|
|
1033
1134
|
const body = await c.req.json();
|
|
1034
1135
|
const user = c.get("user");
|
|
1035
1136
|
let data = { ...body };
|
|
@@ -1043,7 +1144,8 @@ var CollectionController = class {
|
|
|
1043
1144
|
updatedBy: user?.sub ?? null
|
|
1044
1145
|
});
|
|
1045
1146
|
const originalDoc = await db.findOne({ collection: this.collection.slug, id });
|
|
1046
|
-
if (!originalDoc)
|
|
1147
|
+
if (!originalDoc)
|
|
1148
|
+
return c.json({ message: "Not Found" }, 404);
|
|
1047
1149
|
let before = null;
|
|
1048
1150
|
if (this.collection.audit) {
|
|
1049
1151
|
before = originalDoc;
|
|
@@ -1087,8 +1189,10 @@ var CollectionController = class {
|
|
|
1087
1189
|
}
|
|
1088
1190
|
async transition(c) {
|
|
1089
1191
|
const config = c.get("config");
|
|
1090
|
-
if (!config.db)
|
|
1091
|
-
|
|
1192
|
+
if (!config.db)
|
|
1193
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1194
|
+
if (!this.collection.workflow)
|
|
1195
|
+
return c.json({ message: "Workflows are not enabled for this collection" }, 404);
|
|
1092
1196
|
const id = c.req.param("id");
|
|
1093
1197
|
const transitionName = c.req.param("transition");
|
|
1094
1198
|
const body = await c.req.json().catch(() => ({}));
|
|
@@ -1111,12 +1215,16 @@ var CollectionController = class {
|
|
|
1111
1215
|
}
|
|
1112
1216
|
async workflowHistory(c) {
|
|
1113
1217
|
const config = c.get("config");
|
|
1114
|
-
if (!config.db)
|
|
1115
|
-
|
|
1218
|
+
if (!config.db)
|
|
1219
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1220
|
+
if (!this.collection.workflow)
|
|
1221
|
+
return c.json({ message: "Workflows are not enabled for this collection" }, 404);
|
|
1116
1222
|
const documentId = c.req.param("id");
|
|
1117
|
-
if (!documentId)
|
|
1223
|
+
if (!documentId)
|
|
1224
|
+
return c.json({ message: "Missing ID" }, 400);
|
|
1118
1225
|
const document = await config.db.findOne({ collection: this.collection.slug, id: documentId });
|
|
1119
|
-
if (!document)
|
|
1226
|
+
if (!document)
|
|
1227
|
+
return c.json({ message: "Not Found" }, 404);
|
|
1120
1228
|
const readAccess = this.collection.access?.read;
|
|
1121
1229
|
if (readAccess !== void 0 && readAccess !== null) {
|
|
1122
1230
|
const args = { user: c.get("user"), req: c.req, doc: document };
|
|
@@ -1130,7 +1238,8 @@ var CollectionController = class {
|
|
|
1130
1238
|
});
|
|
1131
1239
|
allowed = match.total > 0;
|
|
1132
1240
|
}
|
|
1133
|
-
if (!allowed)
|
|
1241
|
+
if (!allowed)
|
|
1242
|
+
return c.json({ error: true, message: `Access denied: read on ${this.collection.slug}` }, 403);
|
|
1134
1243
|
}
|
|
1135
1244
|
const result = await config.db.find({
|
|
1136
1245
|
collection: WORKFLOW_HISTORY_COLLECTION,
|
|
@@ -1154,14 +1263,17 @@ var CollectionController = class {
|
|
|
1154
1263
|
async changePassword(c) {
|
|
1155
1264
|
const config = c.get("config");
|
|
1156
1265
|
const db = config.db;
|
|
1157
|
-
if (!db)
|
|
1266
|
+
if (!db)
|
|
1267
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1158
1268
|
if (!this.collection.auth) {
|
|
1159
1269
|
return c.json({ message: "This collection does not support authentication" }, 400);
|
|
1160
1270
|
}
|
|
1161
1271
|
const id = c.req.param("id");
|
|
1162
|
-
if (!id)
|
|
1272
|
+
if (!id)
|
|
1273
|
+
return c.json({ message: "Missing ID" }, 400);
|
|
1163
1274
|
const user = c.get("user");
|
|
1164
|
-
if (!user)
|
|
1275
|
+
if (!user)
|
|
1276
|
+
return c.json({ message: "Authentication required" }, 401);
|
|
1165
1277
|
const body = await c.req.json().catch(() => null);
|
|
1166
1278
|
const { oldPassword, newPassword, confirmPassword } = body ?? {};
|
|
1167
1279
|
if (!newPassword) {
|
|
@@ -1183,7 +1295,8 @@ var CollectionController = class {
|
|
|
1183
1295
|
return c.json({ message: "Current password is required" }, 400);
|
|
1184
1296
|
}
|
|
1185
1297
|
const existing = await db.findOne({ collection: this.collection.slug, id });
|
|
1186
|
-
if (!existing)
|
|
1298
|
+
if (!existing)
|
|
1299
|
+
return c.json({ message: "User not found" }, 404);
|
|
1187
1300
|
const valid = await verifyPassword(oldPassword, existing.password);
|
|
1188
1301
|
if (!valid) {
|
|
1189
1302
|
return c.json({ message: "Invalid current password" }, 400);
|
|
@@ -1214,13 +1327,26 @@ var CollectionController = class {
|
|
|
1214
1327
|
async delete(c) {
|
|
1215
1328
|
const config = c.get("config");
|
|
1216
1329
|
const db = config.db;
|
|
1217
|
-
if (!db)
|
|
1330
|
+
if (!db)
|
|
1331
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1332
|
+
const provider = this.getDelegatedProvider(c);
|
|
1333
|
+
if (provider && provider.members?.delete) {
|
|
1334
|
+
const id2 = c.req.param("id");
|
|
1335
|
+
if (!id2)
|
|
1336
|
+
return c.json({ message: "Missing ID" }, 400);
|
|
1337
|
+
const localDoc = await db.findOne({ collection: this.collection.slug, id: id2 });
|
|
1338
|
+
const externalSubject = localDoc?.externalSubject || id2;
|
|
1339
|
+
await provider.members.delete({ externalSubject, req: c.req });
|
|
1340
|
+
return c.json({ message: "Deleted" });
|
|
1341
|
+
}
|
|
1218
1342
|
const readonlyDb = createReadonlyDb(db);
|
|
1219
1343
|
const id = c.req.param("id");
|
|
1220
|
-
if (!id)
|
|
1344
|
+
if (!id)
|
|
1345
|
+
return c.json({ message: "Missing ID" }, 400);
|
|
1221
1346
|
const user = c.get("user");
|
|
1222
1347
|
const doc = await db.findOne({ collection: this.collection.slug, id });
|
|
1223
|
-
if (!doc)
|
|
1348
|
+
if (!doc)
|
|
1349
|
+
return c.json({ message: "Not Found" }, 404);
|
|
1224
1350
|
let before = null;
|
|
1225
1351
|
if (this.collection.audit) {
|
|
1226
1352
|
before = doc;
|
|
@@ -1255,7 +1381,8 @@ var CollectionController = class {
|
|
|
1255
1381
|
async deleteMany(c) {
|
|
1256
1382
|
const config = c.get("config");
|
|
1257
1383
|
const db = config.db;
|
|
1258
|
-
if (!db)
|
|
1384
|
+
if (!db)
|
|
1385
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1259
1386
|
const readonlyDb = createReadonlyDb(db);
|
|
1260
1387
|
const user = c.get("user");
|
|
1261
1388
|
let ids = [];
|
|
@@ -1270,7 +1397,8 @@ var CollectionController = class {
|
|
|
1270
1397
|
const raw = c.req.queries("ids") ?? c.req.queries("ids[]") ?? [];
|
|
1271
1398
|
ids = raw.filter(Boolean);
|
|
1272
1399
|
}
|
|
1273
|
-
if (!ids.length)
|
|
1400
|
+
if (!ids.length)
|
|
1401
|
+
return c.json({ message: "No IDs provided" }, 400);
|
|
1274
1402
|
const deleted = [];
|
|
1275
1403
|
const failed = [];
|
|
1276
1404
|
for (const id of ids) {
|
|
@@ -1323,7 +1451,8 @@ var CollectionController = class {
|
|
|
1323
1451
|
async seed(c) {
|
|
1324
1452
|
const config = c.get("config");
|
|
1325
1453
|
const db = config.db;
|
|
1326
|
-
if (!db)
|
|
1454
|
+
if (!db)
|
|
1455
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1327
1456
|
const body = await c.req.json();
|
|
1328
1457
|
const initialData = body.data;
|
|
1329
1458
|
if (!initialData || !Array.isArray(initialData)) {
|
|
@@ -1343,7 +1472,7 @@ var CollectionController = class {
|
|
|
1343
1472
|
}
|
|
1344
1473
|
};
|
|
1345
1474
|
|
|
1346
|
-
// src/controllers/global.controller.
|
|
1475
|
+
// src/controllers/global.controller.js
|
|
1347
1476
|
var GlobalController = class {
|
|
1348
1477
|
global;
|
|
1349
1478
|
constructor(global) {
|
|
@@ -1352,7 +1481,8 @@ var GlobalController = class {
|
|
|
1352
1481
|
async get(c) {
|
|
1353
1482
|
const config = c.get("config");
|
|
1354
1483
|
const db = config.db;
|
|
1355
|
-
if (!db)
|
|
1484
|
+
if (!db)
|
|
1485
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1356
1486
|
const readonlyDb = createReadonlyDb(db);
|
|
1357
1487
|
const depth = c.req.query("depth") !== void 0 ? Number(c.req.query("depth")) : 10;
|
|
1358
1488
|
const user = c.get("user");
|
|
@@ -1396,7 +1526,8 @@ var GlobalController = class {
|
|
|
1396
1526
|
async update(c) {
|
|
1397
1527
|
const config = c.get("config");
|
|
1398
1528
|
const db = config.db;
|
|
1399
|
-
if (!db)
|
|
1529
|
+
if (!db)
|
|
1530
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1400
1531
|
const readonlyDb = createReadonlyDb(db);
|
|
1401
1532
|
const body = await c.req.json();
|
|
1402
1533
|
const user = c.get("user");
|
|
@@ -1431,7 +1562,8 @@ var GlobalController = class {
|
|
|
1431
1562
|
async seed(c) {
|
|
1432
1563
|
const config = c.get("config");
|
|
1433
1564
|
const db = config.db;
|
|
1434
|
-
if (!db)
|
|
1565
|
+
if (!db)
|
|
1566
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1435
1567
|
const body = await c.req.json();
|
|
1436
1568
|
const initialData = body.data;
|
|
1437
1569
|
if (!initialData) {
|
|
@@ -1447,20 +1579,23 @@ var GlobalController = class {
|
|
|
1447
1579
|
}
|
|
1448
1580
|
};
|
|
1449
1581
|
function isFunctionallyEmpty(obj) {
|
|
1450
|
-
if (obj === null || obj === void 0 || obj === "")
|
|
1582
|
+
if (obj === null || obj === void 0 || obj === "")
|
|
1583
|
+
return true;
|
|
1451
1584
|
if (Array.isArray(obj)) {
|
|
1452
|
-
if (obj.length === 0)
|
|
1585
|
+
if (obj.length === 0)
|
|
1586
|
+
return true;
|
|
1453
1587
|
return obj.every(isFunctionallyEmpty);
|
|
1454
1588
|
}
|
|
1455
1589
|
if (typeof obj === "object") {
|
|
1456
1590
|
const keys = Object.keys(obj);
|
|
1457
|
-
if (keys.length === 0)
|
|
1591
|
+
if (keys.length === 0)
|
|
1592
|
+
return true;
|
|
1458
1593
|
return keys.every((key) => isFunctionallyEmpty(obj[key]));
|
|
1459
1594
|
}
|
|
1460
1595
|
return false;
|
|
1461
1596
|
}
|
|
1462
1597
|
|
|
1463
|
-
// src/controllers/media.controller.
|
|
1598
|
+
// src/controllers/media.controller.js
|
|
1464
1599
|
var MediaController = class {
|
|
1465
1600
|
collection;
|
|
1466
1601
|
constructor(collection = "media") {
|
|
@@ -1540,7 +1675,8 @@ var MediaController = class {
|
|
|
1540
1675
|
}
|
|
1541
1676
|
}
|
|
1542
1677
|
const db = config.db;
|
|
1543
|
-
if (!db)
|
|
1678
|
+
if (!db)
|
|
1679
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1544
1680
|
const doc = await db.create({
|
|
1545
1681
|
collection: this.collection,
|
|
1546
1682
|
data: finalFileData
|
|
@@ -1549,7 +1685,8 @@ var MediaController = class {
|
|
|
1549
1685
|
}
|
|
1550
1686
|
async find(c) {
|
|
1551
1687
|
const db = c.get("config").db;
|
|
1552
|
-
if (!db)
|
|
1688
|
+
if (!db)
|
|
1689
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1553
1690
|
const limit = Number(c.req.query("limit")) || 10;
|
|
1554
1691
|
const page = Number(c.req.query("page")) || 1;
|
|
1555
1692
|
const result = await db.find({
|
|
@@ -1563,11 +1700,14 @@ var MediaController = class {
|
|
|
1563
1700
|
const config = c.get("config");
|
|
1564
1701
|
const storage = config.storage;
|
|
1565
1702
|
const db = config.db;
|
|
1566
|
-
if (!db)
|
|
1703
|
+
if (!db)
|
|
1704
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1567
1705
|
const id = c.req.param("id");
|
|
1568
|
-
if (!id)
|
|
1706
|
+
if (!id)
|
|
1707
|
+
return c.json({ message: "Missing ID" }, 400);
|
|
1569
1708
|
const doc = await db.findOne({ collection: this.collection, id });
|
|
1570
|
-
if (!doc)
|
|
1709
|
+
if (!doc)
|
|
1710
|
+
return c.json({ message: "Not Found" }, 404);
|
|
1571
1711
|
if (storage) {
|
|
1572
1712
|
await storage.delete({ filename: doc.filename });
|
|
1573
1713
|
if (doc.sizes) {
|
|
@@ -1588,26 +1728,26 @@ var MediaController = class {
|
|
|
1588
1728
|
return c.json({ message: "Storage not configured for serving" }, 404);
|
|
1589
1729
|
}
|
|
1590
1730
|
const filename = c.req.param("filename");
|
|
1591
|
-
if (!filename)
|
|
1731
|
+
if (!filename)
|
|
1732
|
+
return c.json({ message: "Missing filename" }, 400);
|
|
1592
1733
|
let res = await storage.resolve({ filename });
|
|
1593
1734
|
if (!res && !filename.includes("/")) {
|
|
1594
1735
|
res = await storage.resolve({ filename: `default/${filename}` });
|
|
1595
1736
|
}
|
|
1596
|
-
if (!res)
|
|
1737
|
+
if (!res)
|
|
1738
|
+
return c.json({ message: "Not Found" }, 404);
|
|
1597
1739
|
c.header("Content-Type", res.mimeType);
|
|
1598
1740
|
return c.body(res.buffer);
|
|
1599
1741
|
}
|
|
1600
1742
|
};
|
|
1601
1743
|
|
|
1602
|
-
// src/auth/token.
|
|
1744
|
+
// src/auth/token.js
|
|
1603
1745
|
var import_jose = require("jose");
|
|
1604
1746
|
var import_node_util2 = require("util");
|
|
1605
1747
|
function getSecret() {
|
|
1606
1748
|
const secret = process.env.DYRECTED_JWT_SECRET || process.env.JWT_SECRET;
|
|
1607
1749
|
if (!secret) {
|
|
1608
|
-
throw new Error(
|
|
1609
|
-
"[dyrected/core] DYRECTED_JWT_SECRET is not set. Add it to your environment variables to enable auth collections."
|
|
1610
|
-
);
|
|
1750
|
+
throw new Error("[dyrected/core] DYRECTED_JWT_SECRET is not set. Add it to your environment variables to enable auth collections.");
|
|
1611
1751
|
}
|
|
1612
1752
|
return new import_node_util2.TextEncoder().encode(secret);
|
|
1613
1753
|
}
|
|
@@ -1620,7 +1760,7 @@ async function verifyCollectionToken(token) {
|
|
|
1620
1760
|
return payload;
|
|
1621
1761
|
}
|
|
1622
1762
|
|
|
1623
|
-
// src/services/email-template.
|
|
1763
|
+
// src/services/email-template.js
|
|
1624
1764
|
var emailTokens = {
|
|
1625
1765
|
colors: {
|
|
1626
1766
|
canvas: "#f6f7f2",
|
|
@@ -1684,11 +1824,9 @@ function detailBox(content, monospace = false) {
|
|
|
1684
1824
|
}
|
|
1685
1825
|
function ctaButton(label, href) {
|
|
1686
1826
|
const safeHref = safeHttpUrl(href);
|
|
1687
|
-
if (!safeHref)
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
"padding:8px 0 24px"
|
|
1691
|
-
), "width:auto");
|
|
1827
|
+
if (!safeHref)
|
|
1828
|
+
return "";
|
|
1829
|
+
return table(row(`<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>`, "padding:8px 0 24px"), "width:auto");
|
|
1692
1830
|
}
|
|
1693
1831
|
function alertBox(content) {
|
|
1694
1832
|
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}`);
|
|
@@ -1699,23 +1837,19 @@ function layout({ preheader, title, content, footer }) {
|
|
|
1699
1837
|
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"></head>
|
|
1700
1838
|
<body style="margin:0;padding:0;background:${emailTokens.colors.canvas}">
|
|
1701
1839
|
<div style="display:none;max-height:0;overflow:hidden;opacity:0;color:transparent">${escapeHtml(preheader)}</div>
|
|
1702
|
-
${table(row(
|
|
1703
|
-
table(
|
|
1704
|
-
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"),
|
|
1705
|
-
`width:100%;max-width:${emailTokens.width};background:${emailTokens.colors.surface};border:1px solid ${emailTokens.colors.border};border-radius:${emailTokens.radius.card};overflow:hidden`
|
|
1706
|
-
),
|
|
1707
|
-
"padding:32px 12px"
|
|
1708
|
-
), `width:100%;background:${emailTokens.colors.canvas}`)}
|
|
1840
|
+
${table(row(table(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"), `width:100%;max-width:${emailTokens.width};background:${emailTokens.colors.surface};border:1px solid ${emailTokens.colors.border};border-radius:${emailTokens.radius.card};overflow:hidden`), "padding:32px 12px"), `width:100%;background:${emailTokens.colors.canvas}`)}
|
|
1709
1841
|
</body>
|
|
1710
1842
|
</html>`;
|
|
1711
1843
|
}
|
|
1712
1844
|
|
|
1713
|
-
// src/services/email.service.
|
|
1845
|
+
// src/services/email.service.js
|
|
1714
1846
|
var _devSend = null;
|
|
1715
1847
|
var _devSendPromise = null;
|
|
1716
1848
|
async function getDevSend() {
|
|
1717
|
-
if (_devSend)
|
|
1718
|
-
|
|
1849
|
+
if (_devSend)
|
|
1850
|
+
return _devSend;
|
|
1851
|
+
if (_devSendPromise)
|
|
1852
|
+
return _devSendPromise;
|
|
1719
1853
|
_devSendPromise = (async () => {
|
|
1720
1854
|
try {
|
|
1721
1855
|
const nodemailer = await import("nodemailer");
|
|
@@ -1799,7 +1933,7 @@ function buildPasswordChangedEmail(config, args) {
|
|
|
1799
1933
|
};
|
|
1800
1934
|
}
|
|
1801
1935
|
|
|
1802
|
-
// src/controllers/auth.controller.
|
|
1936
|
+
// src/controllers/auth.controller.js
|
|
1803
1937
|
var AuthController = class {
|
|
1804
1938
|
collection;
|
|
1805
1939
|
constructor(collection) {
|
|
@@ -1811,7 +1945,8 @@ var AuthController = class {
|
|
|
1811
1945
|
// ---------------------------------------------------------------------------
|
|
1812
1946
|
async init(c) {
|
|
1813
1947
|
const db = c.get("config").db;
|
|
1814
|
-
if (!db)
|
|
1948
|
+
if (!db)
|
|
1949
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1815
1950
|
const result = await db.find({
|
|
1816
1951
|
collection: this.collection.slug,
|
|
1817
1952
|
limit: 1
|
|
@@ -1827,7 +1962,8 @@ var AuthController = class {
|
|
|
1827
1962
|
async registerFirstUser(c) {
|
|
1828
1963
|
const config = c.get("config");
|
|
1829
1964
|
const db = config.db;
|
|
1830
|
-
if (!db)
|
|
1965
|
+
if (!db)
|
|
1966
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1831
1967
|
const check = await db.find({
|
|
1832
1968
|
collection: this.collection.slug,
|
|
1833
1969
|
limit: 1
|
|
@@ -1855,9 +1991,7 @@ var AuthController = class {
|
|
|
1855
1991
|
collection: this.collection.slug
|
|
1856
1992
|
});
|
|
1857
1993
|
const { subject, html } = buildWelcomeEmail(config, { email: body.email });
|
|
1858
|
-
sendEmail(config, { to: body.email, subject, html }).catch(
|
|
1859
|
-
(err) => console.error("[dyrected/core] Failed to send welcome email:", err)
|
|
1860
|
-
);
|
|
1994
|
+
sendEmail(config, { to: body.email, subject, html }).catch((err) => console.error("[dyrected/core] Failed to send welcome email:", err));
|
|
1861
1995
|
const { password: _, ...safeUser } = user;
|
|
1862
1996
|
return c.json({ token, user: safeUser });
|
|
1863
1997
|
}
|
|
@@ -1866,7 +2000,8 @@ var AuthController = class {
|
|
|
1866
2000
|
// ---------------------------------------------------------------------------
|
|
1867
2001
|
async login(c) {
|
|
1868
2002
|
const db = c.get("config").db;
|
|
1869
|
-
if (!db)
|
|
2003
|
+
if (!db)
|
|
2004
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1870
2005
|
const body = await c.req.json().catch(() => null);
|
|
1871
2006
|
if (!body?.email || !body?.password) {
|
|
1872
2007
|
return c.json({ error: true, message: "email and password are required." }, 400);
|
|
@@ -1905,7 +2040,8 @@ var AuthController = class {
|
|
|
1905
2040
|
// ---------------------------------------------------------------------------
|
|
1906
2041
|
async me(c) {
|
|
1907
2042
|
const db = c.get("config").db;
|
|
1908
|
-
if (!db)
|
|
2043
|
+
if (!db)
|
|
2044
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1909
2045
|
const requestUser = c.get("user");
|
|
1910
2046
|
if (!requestUser) {
|
|
1911
2047
|
return c.json({ error: true, message: "Authentication required." }, 401);
|
|
@@ -1940,7 +2076,8 @@ var AuthController = class {
|
|
|
1940
2076
|
async forgotPassword(c) {
|
|
1941
2077
|
const config = c.get("config");
|
|
1942
2078
|
const db = config.db;
|
|
1943
|
-
if (!db)
|
|
2079
|
+
if (!db)
|
|
2080
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1944
2081
|
const body = await c.req.json().catch(() => null);
|
|
1945
2082
|
if (!body?.email) {
|
|
1946
2083
|
return c.json({ error: true, message: "email is required." }, 400);
|
|
@@ -1952,10 +2089,7 @@ var AuthController = class {
|
|
|
1952
2089
|
});
|
|
1953
2090
|
const user = result.docs[0];
|
|
1954
2091
|
if (user) {
|
|
1955
|
-
const resetToken = await signCollectionToken(
|
|
1956
|
-
{ sub: user.id, email: user.email, collection: this.collection.slug, purpose: "reset" },
|
|
1957
|
-
"1h"
|
|
1958
|
-
);
|
|
2092
|
+
const resetToken = await signCollectionToken({ sub: user.id, email: user.email, collection: this.collection.slug, purpose: "reset" }, "1h");
|
|
1959
2093
|
const resetUrl = body?.resetUrl;
|
|
1960
2094
|
const url = resetUrl ? `${resetUrl}${resetUrl.includes("?") ? "&" : "?"}token=${encodeURIComponent(resetToken)}` : void 0;
|
|
1961
2095
|
try {
|
|
@@ -1978,7 +2112,8 @@ var AuthController = class {
|
|
|
1978
2112
|
async resetPassword(c) {
|
|
1979
2113
|
const config = c.get("config");
|
|
1980
2114
|
const db = config.db;
|
|
1981
|
-
if (!db)
|
|
2115
|
+
if (!db)
|
|
2116
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1982
2117
|
const body = await c.req.json().catch(() => null);
|
|
1983
2118
|
if (!body?.token || !body?.password) {
|
|
1984
2119
|
return c.json({ error: true, message: "token and password are required." }, 400);
|
|
@@ -1999,9 +2134,7 @@ var AuthController = class {
|
|
|
1999
2134
|
data: { password: hashedPassword }
|
|
2000
2135
|
});
|
|
2001
2136
|
const { subject, html } = buildPasswordChangedEmail(config, { email: payload.email });
|
|
2002
|
-
sendEmail(config, { to: payload.email, subject, html }).catch(
|
|
2003
|
-
(err) => console.error("[dyrected/core] Failed to send password-changed email:", err)
|
|
2004
|
-
);
|
|
2137
|
+
sendEmail(config, { to: payload.email, subject, html }).catch((err) => console.error("[dyrected/core] Failed to send password-changed email:", err));
|
|
2005
2138
|
return c.json({ success: true, message: "Password has been reset. You can now log in." });
|
|
2006
2139
|
}
|
|
2007
2140
|
// ---------------------------------------------------------------------------
|
|
@@ -2011,7 +2144,8 @@ var AuthController = class {
|
|
|
2011
2144
|
async invite(c) {
|
|
2012
2145
|
const config = c.get("config");
|
|
2013
2146
|
const db = config.db;
|
|
2014
|
-
if (!db)
|
|
2147
|
+
if (!db)
|
|
2148
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
2015
2149
|
const requestUser = c.get("user");
|
|
2016
2150
|
if (!requestUser) {
|
|
2017
2151
|
return c.json({ error: true, message: "Authentication required." }, 401);
|
|
@@ -2028,10 +2162,7 @@ var AuthController = class {
|
|
|
2028
2162
|
if (existing.total > 0) {
|
|
2029
2163
|
return c.json({ error: true, message: "An account with that email already exists." }, 409);
|
|
2030
2164
|
}
|
|
2031
|
-
const inviteToken = await signCollectionToken(
|
|
2032
|
-
{ sub: body.email, email: body.email, collection: this.collection.slug, purpose: "invite" },
|
|
2033
|
-
"7d"
|
|
2034
|
-
);
|
|
2165
|
+
const inviteToken = await signCollectionToken({ sub: body.email, email: body.email, collection: this.collection.slug, purpose: "invite" }, "7d");
|
|
2035
2166
|
try {
|
|
2036
2167
|
const { subject, html } = buildInviteEmail(config, {
|
|
2037
2168
|
token: inviteToken,
|
|
@@ -2051,7 +2182,8 @@ var AuthController = class {
|
|
|
2051
2182
|
async acceptInvite(c) {
|
|
2052
2183
|
const config = c.get("config");
|
|
2053
2184
|
const db = config.db;
|
|
2054
|
-
if (!db)
|
|
2185
|
+
if (!db)
|
|
2186
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
2055
2187
|
const body = await c.req.json().catch(() => null);
|
|
2056
2188
|
if (!body?.token || !body?.password) {
|
|
2057
2189
|
return c.json({ error: true, message: "token and password are required." }, 400);
|
|
@@ -2086,20 +2218,18 @@ var AuthController = class {
|
|
|
2086
2218
|
collection: this.collection.slug
|
|
2087
2219
|
});
|
|
2088
2220
|
const { subject, html } = buildWelcomeEmail(config, { email: inviteeEmail });
|
|
2089
|
-
sendEmail(config, { to: inviteeEmail, subject, html }).catch(
|
|
2090
|
-
(err) => console.error("[dyrected/core] Failed to send welcome email:", err)
|
|
2091
|
-
);
|
|
2221
|
+
sendEmail(config, { to: inviteeEmail, subject, html }).catch((err) => console.error("[dyrected/core] Failed to send welcome email:", err));
|
|
2092
2222
|
const { password: _, ...safeUser } = user;
|
|
2093
2223
|
return c.json({ token: sessionToken, user: safeUser }, 201);
|
|
2094
2224
|
}
|
|
2095
2225
|
};
|
|
2096
2226
|
|
|
2097
|
-
// src/controllers/admin-auth.controller.
|
|
2227
|
+
// src/controllers/admin-auth.controller.js
|
|
2098
2228
|
var import_node_crypto2 = require("crypto");
|
|
2099
2229
|
var import_node_util3 = require("util");
|
|
2100
2230
|
var import_jose2 = require("jose");
|
|
2101
2231
|
|
|
2102
|
-
// src/utils/admin-auth.
|
|
2232
|
+
// src/utils/admin-auth.js
|
|
2103
2233
|
function getAdminAuthCollection(config) {
|
|
2104
2234
|
const requestedSlug = config.adminAuth?.collectionSlug;
|
|
2105
2235
|
if (requestedSlug) {
|
|
@@ -2123,16 +2253,17 @@ function getPublicAdminAuthConfig(adminAuth) {
|
|
|
2123
2253
|
}
|
|
2124
2254
|
function humanizeProviderName(id, type) {
|
|
2125
2255
|
const cleaned = id.replace(/[-_]+/g, " ").trim();
|
|
2126
|
-
if (!cleaned)
|
|
2256
|
+
if (!cleaned)
|
|
2257
|
+
return type.toUpperCase();
|
|
2127
2258
|
return cleaned.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
2128
2259
|
}
|
|
2129
2260
|
|
|
2130
|
-
// src/controllers/admin-auth.controller.
|
|
2261
|
+
// src/controllers/admin-auth.controller.js
|
|
2131
2262
|
var AdminAuthController = class {
|
|
2263
|
+
config;
|
|
2132
2264
|
constructor(config) {
|
|
2133
2265
|
this.config = config;
|
|
2134
2266
|
}
|
|
2135
|
-
config;
|
|
2136
2267
|
providers(c) {
|
|
2137
2268
|
return c.json(getPublicAdminAuthConfig(this.config.adminAuth));
|
|
2138
2269
|
}
|
|
@@ -2145,12 +2276,7 @@ var AdminAuthController = class {
|
|
|
2145
2276
|
const siteId = this.getSiteId(c);
|
|
2146
2277
|
const returnTo = this.normalizeReturnTo(c.req.query("returnTo"), c);
|
|
2147
2278
|
if (provider.type === "oidc") {
|
|
2148
|
-
const redirectUrl = await this.buildOidcStartUrl(
|
|
2149
|
-
provider,
|
|
2150
|
-
returnTo,
|
|
2151
|
-
siteId,
|
|
2152
|
-
new URL(c.req.url).origin
|
|
2153
|
-
);
|
|
2279
|
+
const redirectUrl = await this.buildOidcStartUrl(provider, returnTo, siteId, new URL(c.req.url).origin);
|
|
2154
2280
|
return c.redirect(redirectUrl, 302);
|
|
2155
2281
|
}
|
|
2156
2282
|
if (!provider.startUrl) {
|
|
@@ -2158,16 +2284,14 @@ var AdminAuthController = class {
|
|
|
2158
2284
|
}
|
|
2159
2285
|
const url = this.buildCustomProviderStartUrl(provider.startUrl, new URL(c.req.url).origin);
|
|
2160
2286
|
if (!url) {
|
|
2161
|
-
return c.json(
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
},
|
|
2166
|
-
500
|
|
2167
|
-
);
|
|
2287
|
+
return c.json({
|
|
2288
|
+
error: true,
|
|
2289
|
+
message: `Admin auth provider "${provider.id}" has an invalid start URL.`
|
|
2290
|
+
}, 500);
|
|
2168
2291
|
}
|
|
2169
2292
|
url.searchParams.set("returnTo", returnTo);
|
|
2170
|
-
if (siteId)
|
|
2293
|
+
if (siteId)
|
|
2294
|
+
url.searchParams.set("siteId", siteId);
|
|
2171
2295
|
url.searchParams.set("provider", provider.id);
|
|
2172
2296
|
return c.redirect(url.toString(), 302);
|
|
2173
2297
|
}
|
|
@@ -2214,7 +2338,8 @@ var AdminAuthController = class {
|
|
|
2214
2338
|
return c.json({ success: true, message: "Logged out. Discard your token." });
|
|
2215
2339
|
}
|
|
2216
2340
|
getProvider(config, id) {
|
|
2217
|
-
if (config.adminAuth?.mode !== "external")
|
|
2341
|
+
if (config.adminAuth?.mode !== "external")
|
|
2342
|
+
return void 0;
|
|
2218
2343
|
return config.adminAuth.providers.find((provider) => provider.id === id);
|
|
2219
2344
|
}
|
|
2220
2345
|
async completeProviderAuth(requestConfig, provider, c) {
|
|
@@ -2226,7 +2351,8 @@ var AdminAuthController = class {
|
|
|
2226
2351
|
throw new Error("Admin auth collection is not configured.");
|
|
2227
2352
|
}
|
|
2228
2353
|
const db = c.get("config").db;
|
|
2229
|
-
if (!db)
|
|
2354
|
+
if (!db)
|
|
2355
|
+
throw new Error("Database not configured.");
|
|
2230
2356
|
let user = await this.findUserByExternalIdentity(db, adminCollection.slug, provider.id, identity.sub) ?? (identity.email ? (await db.find({
|
|
2231
2357
|
collection: adminCollection.slug,
|
|
2232
2358
|
where: { email: identity.email },
|
|
@@ -2286,7 +2412,8 @@ var AdminAuthController = class {
|
|
|
2286
2412
|
},
|
|
2287
2413
|
user
|
|
2288
2414
|
});
|
|
2289
|
-
if (resolved)
|
|
2415
|
+
if (resolved)
|
|
2416
|
+
return resolved;
|
|
2290
2417
|
return { allowed: true, roles: identity.roles, data: {} };
|
|
2291
2418
|
}
|
|
2292
2419
|
async buildUserData(identity, providerId, roles, extraData) {
|
|
@@ -2317,7 +2444,8 @@ var AdminAuthController = class {
|
|
|
2317
2444
|
}
|
|
2318
2445
|
async getRequestConfig(c) {
|
|
2319
2446
|
const siteId = this.getSiteId(c);
|
|
2320
|
-
if (!siteId || !this.config.onSchemaFetch)
|
|
2447
|
+
if (!siteId || !this.config.onSchemaFetch)
|
|
2448
|
+
return this.config;
|
|
2321
2449
|
const dynamic = await this.config.onSchemaFetch(siteId);
|
|
2322
2450
|
return {
|
|
2323
2451
|
...this.config,
|
|
@@ -2348,7 +2476,8 @@ var AdminAuthController = class {
|
|
|
2348
2476
|
async resolveOidcIdentity(provider, c) {
|
|
2349
2477
|
const code = c.req.query("code");
|
|
2350
2478
|
const state = c.req.query("state");
|
|
2351
|
-
if (!code || !state)
|
|
2479
|
+
if (!code || !state)
|
|
2480
|
+
throw new Error("Missing OIDC callback parameters.");
|
|
2352
2481
|
const parsedState = await this.verifyState(state, provider.id);
|
|
2353
2482
|
const discovery = await this.getOidcDiscovery(provider);
|
|
2354
2483
|
const redirectUri = provider.redirectUri || this.defaultCallbackPath(provider.id, new URL(c.req.url).origin);
|
|
@@ -2383,7 +2512,8 @@ var AdminAuthController = class {
|
|
|
2383
2512
|
const userInfo = await fetch(provider.userInfoEndpoint || discovery.userinfo_endpoint, {
|
|
2384
2513
|
headers: { Authorization: `Bearer ${tokenBody.access_token}` }
|
|
2385
2514
|
});
|
|
2386
|
-
if (!userInfo.ok)
|
|
2515
|
+
if (!userInfo.ok)
|
|
2516
|
+
throw new Error("OIDC userinfo request failed.");
|
|
2387
2517
|
claims = await userInfo.json();
|
|
2388
2518
|
} else {
|
|
2389
2519
|
throw new Error("OIDC provider did not return usable identity claims.");
|
|
@@ -2423,15 +2553,13 @@ var AdminAuthController = class {
|
|
|
2423
2553
|
if (provider.audience) {
|
|
2424
2554
|
const aud = claims.aud;
|
|
2425
2555
|
const matches = Array.isArray(aud) ? aud.includes(provider.audience) : aud === provider.audience;
|
|
2426
|
-
if (!matches)
|
|
2556
|
+
if (!matches)
|
|
2557
|
+
throw new Error("External auth audience mismatch.");
|
|
2427
2558
|
}
|
|
2428
2559
|
}
|
|
2429
2560
|
return {
|
|
2430
2561
|
identity: this.mapClaims(claims, provider.claimMapping),
|
|
2431
|
-
returnTo: this.normalizeReturnTo(
|
|
2432
|
-
body?.returnTo || c.req.query("returnTo"),
|
|
2433
|
-
c
|
|
2434
|
-
)
|
|
2562
|
+
returnTo: this.normalizeReturnTo(body?.returnTo || c.req.query("returnTo"), c)
|
|
2435
2563
|
};
|
|
2436
2564
|
}
|
|
2437
2565
|
mapClaims(claims, mapping) {
|
|
@@ -2461,19 +2589,20 @@ var AdminAuthController = class {
|
|
|
2461
2589
|
return typeof value === "string" ? value : void 0;
|
|
2462
2590
|
}
|
|
2463
2591
|
readStringArrayClaim(value) {
|
|
2464
|
-
if (!value)
|
|
2592
|
+
if (!value)
|
|
2593
|
+
return void 0;
|
|
2465
2594
|
if (Array.isArray(value)) {
|
|
2466
2595
|
return value.filter((entry) => typeof entry === "string");
|
|
2467
2596
|
}
|
|
2468
|
-
if (typeof value === "string")
|
|
2597
|
+
if (typeof value === "string")
|
|
2598
|
+
return [value];
|
|
2469
2599
|
return void 0;
|
|
2470
2600
|
}
|
|
2471
2601
|
async getOidcDiscovery(provider) {
|
|
2472
|
-
const discoveryUrl = new URL(
|
|
2473
|
-
provider.issuer.replace(/\/$/, "") + "/.well-known/openid-configuration"
|
|
2474
|
-
);
|
|
2602
|
+
const discoveryUrl = new URL(provider.issuer.replace(/\/$/, "") + "/.well-known/openid-configuration");
|
|
2475
2603
|
const response = await fetch(discoveryUrl.toString());
|
|
2476
|
-
if (!response.ok)
|
|
2604
|
+
if (!response.ok)
|
|
2605
|
+
throw new Error("Failed to load OIDC discovery document.");
|
|
2477
2606
|
return response.json();
|
|
2478
2607
|
}
|
|
2479
2608
|
defaultCallbackPath(providerId, origin = "") {
|
|
@@ -2481,10 +2610,12 @@ var AdminAuthController = class {
|
|
|
2481
2610
|
}
|
|
2482
2611
|
buildCustomProviderStartUrl(startUrl, origin) {
|
|
2483
2612
|
const value = startUrl.trim();
|
|
2484
|
-
if (!value || /^(undefined|null)(\/|$)/i.test(value))
|
|
2613
|
+
if (!value || /^(undefined|null)(\/|$)/i.test(value))
|
|
2614
|
+
return null;
|
|
2485
2615
|
try {
|
|
2486
2616
|
const url = new URL(value, origin);
|
|
2487
|
-
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
2617
|
+
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
2618
|
+
return null;
|
|
2488
2619
|
return url;
|
|
2489
2620
|
} catch {
|
|
2490
2621
|
return null;
|
|
@@ -2509,13 +2640,14 @@ var AdminAuthController = class {
|
|
|
2509
2640
|
return new import_node_util3.TextEncoder().encode(secret);
|
|
2510
2641
|
}
|
|
2511
2642
|
normalizeReturnTo(returnTo, c) {
|
|
2512
|
-
if (returnTo)
|
|
2643
|
+
if (returnTo)
|
|
2644
|
+
return returnTo;
|
|
2513
2645
|
const url = new URL(c.req.url);
|
|
2514
2646
|
return `${url.origin}/admin`;
|
|
2515
2647
|
}
|
|
2516
2648
|
};
|
|
2517
2649
|
|
|
2518
|
-
// src/controllers/preview.controller.
|
|
2650
|
+
// src/controllers/preview.controller.js
|
|
2519
2651
|
var import_jose3 = require("jose");
|
|
2520
2652
|
var import_node_util4 = require("util");
|
|
2521
2653
|
var PreviewController = class {
|
|
@@ -2558,7 +2690,7 @@ var PreviewController = class {
|
|
|
2558
2690
|
}
|
|
2559
2691
|
};
|
|
2560
2692
|
|
|
2561
|
-
// src/middleware/auth.
|
|
2693
|
+
// src/middleware/auth.js
|
|
2562
2694
|
function requireAuth() {
|
|
2563
2695
|
return async (c, next) => {
|
|
2564
2696
|
const authHeader = c.req.header("Authorization");
|
|
@@ -2590,7 +2722,7 @@ function optionalAuth() {
|
|
|
2590
2722
|
};
|
|
2591
2723
|
}
|
|
2592
2724
|
|
|
2593
|
-
// src/utils/openapi.
|
|
2725
|
+
// src/utils/openapi.js
|
|
2594
2726
|
function generateOpenApi(config) {
|
|
2595
2727
|
const spec = {
|
|
2596
2728
|
openapi: "3.0.0",
|
|
@@ -3299,7 +3431,8 @@ function fieldsToProperties(fields) {
|
|
|
3299
3431
|
required.push(...nested.required);
|
|
3300
3432
|
continue;
|
|
3301
3433
|
}
|
|
3302
|
-
if (!field.name)
|
|
3434
|
+
if (!field.name)
|
|
3435
|
+
continue;
|
|
3303
3436
|
props[field.name] = fieldToSchema(field);
|
|
3304
3437
|
if (field.required) {
|
|
3305
3438
|
required.push(field.name);
|
|
@@ -3411,11 +3544,12 @@ function fieldToSchema(field) {
|
|
|
3411
3544
|
default:
|
|
3412
3545
|
schema = { type: "string" };
|
|
3413
3546
|
}
|
|
3414
|
-
if (field.label)
|
|
3547
|
+
if (field.label)
|
|
3548
|
+
schema.description = field.label;
|
|
3415
3549
|
return schema;
|
|
3416
3550
|
}
|
|
3417
3551
|
|
|
3418
|
-
// src/utils/swagger.
|
|
3552
|
+
// src/utils/swagger.js
|
|
3419
3553
|
function getSwaggerHtml(specUrl = "/api/openapi.json") {
|
|
3420
3554
|
return `
|
|
3421
3555
|
<!DOCTYPE html>
|
|
@@ -3464,7 +3598,7 @@ function getSwaggerHtml(specUrl = "/api/openapi.json") {
|
|
|
3464
3598
|
`;
|
|
3465
3599
|
}
|
|
3466
3600
|
|
|
3467
|
-
// src/router.
|
|
3601
|
+
// src/router.js
|
|
3468
3602
|
function accessGate(target, action) {
|
|
3469
3603
|
return async (c, next) => {
|
|
3470
3604
|
const user = c.get("user");
|
|
@@ -3481,7 +3615,8 @@ function accessGate(target, action) {
|
|
|
3481
3615
|
};
|
|
3482
3616
|
}
|
|
3483
3617
|
async function checkAccess(access, accessArgs) {
|
|
3484
|
-
if (access === void 0 || access === null)
|
|
3618
|
+
if (access === void 0 || access === null)
|
|
3619
|
+
return true;
|
|
3485
3620
|
if (typeof access === "function") {
|
|
3486
3621
|
try {
|
|
3487
3622
|
const result = await access(accessArgs);
|
|
@@ -3497,7 +3632,8 @@ async function checkAccess(access, accessArgs) {
|
|
|
3497
3632
|
return true;
|
|
3498
3633
|
}
|
|
3499
3634
|
function serializeFieldForApi(f) {
|
|
3500
|
-
if (!f)
|
|
3635
|
+
if (!f)
|
|
3636
|
+
return f;
|
|
3501
3637
|
const serialized = { ...f };
|
|
3502
3638
|
if (serialized.admin?.hooks) {
|
|
3503
3639
|
const hooks = { ...serialized.admin.hooks };
|
|
@@ -3530,8 +3666,10 @@ function registerRoutes(app, config) {
|
|
|
3530
3666
|
let globals = [...config.globals];
|
|
3531
3667
|
if (siteId && config.onSchemaFetch) {
|
|
3532
3668
|
const dynamic = await config.onSchemaFetch(siteId);
|
|
3533
|
-
if (dynamic.collections)
|
|
3534
|
-
|
|
3669
|
+
if (dynamic.collections)
|
|
3670
|
+
collections = [...collections, ...dynamic.collections];
|
|
3671
|
+
if (dynamic.globals)
|
|
3672
|
+
globals = [...globals, ...dynamic.globals];
|
|
3535
3673
|
if (dynamic.admin) {
|
|
3536
3674
|
config.admin = { ...config.admin, ...dynamic.admin };
|
|
3537
3675
|
}
|
|
@@ -3542,8 +3680,10 @@ function registerRoutes(app, config) {
|
|
|
3542
3680
|
const user = c.get("user");
|
|
3543
3681
|
const accessArgs = { user, req: c.req, doc: null };
|
|
3544
3682
|
const serializeAccess = async (access) => {
|
|
3545
|
-
if (typeof access === "string")
|
|
3546
|
-
|
|
3683
|
+
if (typeof access === "string")
|
|
3684
|
+
return access;
|
|
3685
|
+
if (typeof access === "boolean")
|
|
3686
|
+
return access;
|
|
3547
3687
|
return checkAccess(access, accessArgs);
|
|
3548
3688
|
};
|
|
3549
3689
|
const filteredCollections = await Promise.all(collections.filter((col) => !siteId || col.shared || !col.siteId || col.siteId === siteId).map(async (col) => ({
|
|
@@ -3636,7 +3776,8 @@ function registerRoutes(app, config) {
|
|
|
3636
3776
|
let collections = [...config.collections];
|
|
3637
3777
|
if (siteId && config.onSchemaFetch) {
|
|
3638
3778
|
const dynamic = await config.onSchemaFetch(siteId);
|
|
3639
|
-
if (dynamic.collections)
|
|
3779
|
+
if (dynamic.collections)
|
|
3780
|
+
collections = [...collections, ...dynamic.collections];
|
|
3640
3781
|
}
|
|
3641
3782
|
const user = c.get("user");
|
|
3642
3783
|
let collection = collections.find((col) => col.slug === colSlug);
|
|
@@ -3655,7 +3796,8 @@ function registerRoutes(app, config) {
|
|
|
3655
3796
|
let globals = [...config.globals];
|
|
3656
3797
|
if (siteId && config.onSchemaFetch) {
|
|
3657
3798
|
const dynamic = await config.onSchemaFetch(siteId);
|
|
3658
|
-
if (dynamic.globals)
|
|
3799
|
+
if (dynamic.globals)
|
|
3800
|
+
globals = [...globals, ...dynamic.globals];
|
|
3659
3801
|
}
|
|
3660
3802
|
const glb = globals.find((g) => g.slug === colSlug);
|
|
3661
3803
|
if (!glb) {
|
|
@@ -3713,9 +3855,12 @@ function registerRoutes(app, config) {
|
|
|
3713
3855
|
const user = c.get("user");
|
|
3714
3856
|
const key = c.req.param("key");
|
|
3715
3857
|
const scope = c.req.query("scope");
|
|
3716
|
-
if (!db)
|
|
3717
|
-
|
|
3718
|
-
if (!
|
|
3858
|
+
if (!db)
|
|
3859
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
3860
|
+
if (!user?.collection || !user.sub)
|
|
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);
|
|
3719
3864
|
const getGlobalPreference = async () => {
|
|
3720
3865
|
const globalDoc = await db.findOne({ collection: "__global_preferences", id: key });
|
|
3721
3866
|
return globalDoc ? globalDoc.value : null;
|
|
@@ -3725,7 +3870,8 @@ function registerRoutes(app, config) {
|
|
|
3725
3870
|
return c.json({ key, value: globalValue2 });
|
|
3726
3871
|
}
|
|
3727
3872
|
const doc = await db.findOne({ collection: user.collection, id: user.sub });
|
|
3728
|
-
if (!doc)
|
|
3873
|
+
if (!doc)
|
|
3874
|
+
return c.json({ error: true, message: "User not found." }, 404);
|
|
3729
3875
|
const preferences = typeof doc.__preferences === "object" && doc.__preferences !== null ? doc.__preferences : {};
|
|
3730
3876
|
if (key in preferences) {
|
|
3731
3877
|
return c.json({ key, value: preferences[key] ?? null });
|
|
@@ -3738,9 +3884,12 @@ function registerRoutes(app, config) {
|
|
|
3738
3884
|
const user = c.get("user");
|
|
3739
3885
|
const key = c.req.param("key");
|
|
3740
3886
|
const scope = c.req.query("scope");
|
|
3741
|
-
if (!db)
|
|
3742
|
-
|
|
3743
|
-
if (!
|
|
3887
|
+
if (!db)
|
|
3888
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
3889
|
+
if (!user?.collection || !user.sub)
|
|
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);
|
|
3744
3893
|
const body = await c.req.json().catch(() => ({}));
|
|
3745
3894
|
if (scope === "global") {
|
|
3746
3895
|
const isAdminUser = Array.isArray(user?.roles) && user.roles.includes("admin");
|
|
@@ -3763,7 +3912,8 @@ function registerRoutes(app, config) {
|
|
|
3763
3912
|
return c.json({ key, value: body.value });
|
|
3764
3913
|
}
|
|
3765
3914
|
const doc = await db.findOne({ collection: user.collection, id: user.sub });
|
|
3766
|
-
if (!doc)
|
|
3915
|
+
if (!doc)
|
|
3916
|
+
return c.json({ error: true, message: "User not found." }, 404);
|
|
3767
3917
|
const preferences = typeof doc.__preferences === "object" && doc.__preferences !== null ? doc.__preferences : {};
|
|
3768
3918
|
const nextPreferences = { ...preferences, [key]: body.value };
|
|
3769
3919
|
await db.update({
|
|
@@ -3778,9 +3928,12 @@ function registerRoutes(app, config) {
|
|
|
3778
3928
|
const user = c.get("user");
|
|
3779
3929
|
const key = c.req.param("key");
|
|
3780
3930
|
const scope = c.req.query("scope");
|
|
3781
|
-
if (!db)
|
|
3782
|
-
|
|
3783
|
-
if (!
|
|
3931
|
+
if (!db)
|
|
3932
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
3933
|
+
if (!user?.collection || !user.sub)
|
|
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);
|
|
3784
3937
|
if (scope === "global") {
|
|
3785
3938
|
const isAdminUser = Array.isArray(user?.roles) && user.roles.includes("admin");
|
|
3786
3939
|
if (!isAdminUser) {
|
|
@@ -3790,7 +3943,8 @@ function registerRoutes(app, config) {
|
|
|
3790
3943
|
return c.json({ success: true });
|
|
3791
3944
|
}
|
|
3792
3945
|
const doc = await db.findOne({ collection: user.collection, id: user.sub });
|
|
3793
|
-
if (!doc)
|
|
3946
|
+
if (!doc)
|
|
3947
|
+
return c.json({ error: true, message: "User not found." }, 404);
|
|
3794
3948
|
const preferences = typeof doc.__preferences === "object" && doc.__preferences !== null ? { ...doc.__preferences } : {};
|
|
3795
3949
|
delete preferences[key];
|
|
3796
3950
|
await db.update({
|
|
@@ -3826,7 +3980,8 @@ function registerRoutes(app, config) {
|
|
|
3826
3980
|
app.post("/api/admin/auth/:provider/exchange", (c) => adminAuthController.exchange(c));
|
|
3827
3981
|
app.post("/api/admin/logout", (c) => adminAuthController.logout(c));
|
|
3828
3982
|
for (const collection of config.collections) {
|
|
3829
|
-
if (!collection.auth)
|
|
3983
|
+
if (!collection.auth)
|
|
3984
|
+
continue;
|
|
3830
3985
|
const path = `/api/collections/${collection.slug}`;
|
|
3831
3986
|
const authController = new AuthController(collection);
|
|
3832
3987
|
app.post(`${path}/login`, (c) => authController.login(c));
|
|
@@ -3928,25 +4083,39 @@ function registerRoutes(app, config) {
|
|
|
3928
4083
|
if (collection.auth && id) {
|
|
3929
4084
|
const authController = new AuthController(collection);
|
|
3930
4085
|
const method2 = c.req.method;
|
|
3931
|
-
if (method2 === "POST" && id === "login")
|
|
3932
|
-
|
|
3933
|
-
if (method2 === "
|
|
3934
|
-
|
|
3935
|
-
if (method2 === "
|
|
3936
|
-
|
|
4086
|
+
if (method2 === "POST" && id === "login")
|
|
4087
|
+
return authController.login(c);
|
|
4088
|
+
if (method2 === "POST" && id === "logout")
|
|
4089
|
+
return authController.logout(c);
|
|
4090
|
+
if (method2 === "GET" && id === "me")
|
|
4091
|
+
return authController.me(c);
|
|
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);
|
|
3937
4098
|
}
|
|
3938
4099
|
const controller = new CollectionController(collection);
|
|
3939
4100
|
const method = c.req.method;
|
|
3940
4101
|
if (id) {
|
|
3941
|
-
if (method === "GET")
|
|
3942
|
-
|
|
3943
|
-
if (method === "
|
|
3944
|
-
|
|
3945
|
-
if (method === "
|
|
3946
|
-
|
|
4102
|
+
if (method === "GET")
|
|
4103
|
+
return controller.findOne(c);
|
|
4104
|
+
if (method === "PATCH")
|
|
4105
|
+
return controller.update(c);
|
|
4106
|
+
if (method === "DELETE" && id === "delete-many")
|
|
4107
|
+
return controller.deleteMany(c);
|
|
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);
|
|
3947
4114
|
} else {
|
|
3948
|
-
if (method === "GET")
|
|
3949
|
-
|
|
4115
|
+
if (method === "GET")
|
|
4116
|
+
return controller.find(c);
|
|
4117
|
+
if (method === "POST")
|
|
4118
|
+
return controller.create(c);
|
|
3950
4119
|
}
|
|
3951
4120
|
}
|
|
3952
4121
|
}
|
|
@@ -3967,10 +4136,13 @@ function registerRoutes(app, config) {
|
|
|
3967
4136
|
const controller = new GlobalController(global);
|
|
3968
4137
|
const method = c.req.method;
|
|
3969
4138
|
if (id) {
|
|
3970
|
-
if (method === "POST" && id === "seed")
|
|
4139
|
+
if (method === "POST" && id === "seed")
|
|
4140
|
+
return controller.seed(c);
|
|
3971
4141
|
} else {
|
|
3972
|
-
if (method === "GET")
|
|
3973
|
-
|
|
4142
|
+
if (method === "GET")
|
|
4143
|
+
return controller.get(c);
|
|
4144
|
+
if (method === "PATCH")
|
|
4145
|
+
return controller.update(c);
|
|
3974
4146
|
}
|
|
3975
4147
|
}
|
|
3976
4148
|
}
|
|
@@ -3978,7 +4150,7 @@ function registerRoutes(app, config) {
|
|
|
3978
4150
|
});
|
|
3979
4151
|
}
|
|
3980
4152
|
|
|
3981
|
-
// src/utils/config.
|
|
4153
|
+
// src/utils/config.js
|
|
3982
4154
|
var AUDIT_COLLECTION_SLUG = "__audit";
|
|
3983
4155
|
var SYSTEM_FIELDS = [
|
|
3984
4156
|
{
|
|
@@ -4192,7 +4364,8 @@ function normalizeConfig(config) {
|
|
|
4192
4364
|
});
|
|
4193
4365
|
const hasAuditCollection = normalizedCollections.some((col) => col.slug === AUDIT_COLLECTION_SLUG);
|
|
4194
4366
|
const systemCollections = [];
|
|
4195
|
-
if (needsAudit && !hasAuditCollection)
|
|
4367
|
+
if (needsAudit && !hasAuditCollection)
|
|
4368
|
+
systemCollections.push(AUDIT_COLLECTION);
|
|
4196
4369
|
if (needsWorkflow && !normalizedCollections.some((col) => col.slug === WORKFLOW_HISTORY_COLLECTION)) {
|
|
4197
4370
|
systemCollections.push(WORKFLOW_HISTORY_COLLECTION_CONFIG);
|
|
4198
4371
|
}
|
|
@@ -4206,7 +4379,7 @@ function normalizeConfig(config) {
|
|
|
4206
4379
|
};
|
|
4207
4380
|
}
|
|
4208
4381
|
|
|
4209
|
-
// src/app.
|
|
4382
|
+
// src/app.js
|
|
4210
4383
|
async function createDyrectedApp(rawConfig) {
|
|
4211
4384
|
const config = normalizeConfig(rawConfig);
|
|
4212
4385
|
const app = new import_hono.Hono();
|