@dyrected/core 2.5.42 → 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-2vF6W56z.d.cts +1335 -0
- package/dist/app-config-2vF6W56z.d.ts +1335 -0
- 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 +495 -302
- package/dist/server.d.cts +34 -1
- package/dist/server.d.ts +34 -1
- package/dist/server.js +423 -223
- 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,20 +2276,22 @@ 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) {
|
|
2157
2283
|
return c.json({ error: true, message: "This provider does not expose a start URL." }, 400);
|
|
2158
2284
|
}
|
|
2159
|
-
const url = new URL(
|
|
2285
|
+
const url = this.buildCustomProviderStartUrl(provider.startUrl, new URL(c.req.url).origin);
|
|
2286
|
+
if (!url) {
|
|
2287
|
+
return c.json({
|
|
2288
|
+
error: true,
|
|
2289
|
+
message: `Admin auth provider "${provider.id}" has an invalid start URL.`
|
|
2290
|
+
}, 500);
|
|
2291
|
+
}
|
|
2160
2292
|
url.searchParams.set("returnTo", returnTo);
|
|
2161
|
-
if (siteId)
|
|
2293
|
+
if (siteId)
|
|
2294
|
+
url.searchParams.set("siteId", siteId);
|
|
2162
2295
|
url.searchParams.set("provider", provider.id);
|
|
2163
2296
|
return c.redirect(url.toString(), 302);
|
|
2164
2297
|
}
|
|
@@ -2205,7 +2338,8 @@ var AdminAuthController = class {
|
|
|
2205
2338
|
return c.json({ success: true, message: "Logged out. Discard your token." });
|
|
2206
2339
|
}
|
|
2207
2340
|
getProvider(config, id) {
|
|
2208
|
-
if (config.adminAuth?.mode !== "external")
|
|
2341
|
+
if (config.adminAuth?.mode !== "external")
|
|
2342
|
+
return void 0;
|
|
2209
2343
|
return config.adminAuth.providers.find((provider) => provider.id === id);
|
|
2210
2344
|
}
|
|
2211
2345
|
async completeProviderAuth(requestConfig, provider, c) {
|
|
@@ -2217,7 +2351,8 @@ var AdminAuthController = class {
|
|
|
2217
2351
|
throw new Error("Admin auth collection is not configured.");
|
|
2218
2352
|
}
|
|
2219
2353
|
const db = c.get("config").db;
|
|
2220
|
-
if (!db)
|
|
2354
|
+
if (!db)
|
|
2355
|
+
throw new Error("Database not configured.");
|
|
2221
2356
|
let user = await this.findUserByExternalIdentity(db, adminCollection.slug, provider.id, identity.sub) ?? (identity.email ? (await db.find({
|
|
2222
2357
|
collection: adminCollection.slug,
|
|
2223
2358
|
where: { email: identity.email },
|
|
@@ -2277,7 +2412,8 @@ var AdminAuthController = class {
|
|
|
2277
2412
|
},
|
|
2278
2413
|
user
|
|
2279
2414
|
});
|
|
2280
|
-
if (resolved)
|
|
2415
|
+
if (resolved)
|
|
2416
|
+
return resolved;
|
|
2281
2417
|
return { allowed: true, roles: identity.roles, data: {} };
|
|
2282
2418
|
}
|
|
2283
2419
|
async buildUserData(identity, providerId, roles, extraData) {
|
|
@@ -2308,7 +2444,8 @@ var AdminAuthController = class {
|
|
|
2308
2444
|
}
|
|
2309
2445
|
async getRequestConfig(c) {
|
|
2310
2446
|
const siteId = this.getSiteId(c);
|
|
2311
|
-
if (!siteId || !this.config.onSchemaFetch)
|
|
2447
|
+
if (!siteId || !this.config.onSchemaFetch)
|
|
2448
|
+
return this.config;
|
|
2312
2449
|
const dynamic = await this.config.onSchemaFetch(siteId);
|
|
2313
2450
|
return {
|
|
2314
2451
|
...this.config,
|
|
@@ -2339,7 +2476,8 @@ var AdminAuthController = class {
|
|
|
2339
2476
|
async resolveOidcIdentity(provider, c) {
|
|
2340
2477
|
const code = c.req.query("code");
|
|
2341
2478
|
const state = c.req.query("state");
|
|
2342
|
-
if (!code || !state)
|
|
2479
|
+
if (!code || !state)
|
|
2480
|
+
throw new Error("Missing OIDC callback parameters.");
|
|
2343
2481
|
const parsedState = await this.verifyState(state, provider.id);
|
|
2344
2482
|
const discovery = await this.getOidcDiscovery(provider);
|
|
2345
2483
|
const redirectUri = provider.redirectUri || this.defaultCallbackPath(provider.id, new URL(c.req.url).origin);
|
|
@@ -2374,7 +2512,8 @@ var AdminAuthController = class {
|
|
|
2374
2512
|
const userInfo = await fetch(provider.userInfoEndpoint || discovery.userinfo_endpoint, {
|
|
2375
2513
|
headers: { Authorization: `Bearer ${tokenBody.access_token}` }
|
|
2376
2514
|
});
|
|
2377
|
-
if (!userInfo.ok)
|
|
2515
|
+
if (!userInfo.ok)
|
|
2516
|
+
throw new Error("OIDC userinfo request failed.");
|
|
2378
2517
|
claims = await userInfo.json();
|
|
2379
2518
|
} else {
|
|
2380
2519
|
throw new Error("OIDC provider did not return usable identity claims.");
|
|
@@ -2414,15 +2553,13 @@ var AdminAuthController = class {
|
|
|
2414
2553
|
if (provider.audience) {
|
|
2415
2554
|
const aud = claims.aud;
|
|
2416
2555
|
const matches = Array.isArray(aud) ? aud.includes(provider.audience) : aud === provider.audience;
|
|
2417
|
-
if (!matches)
|
|
2556
|
+
if (!matches)
|
|
2557
|
+
throw new Error("External auth audience mismatch.");
|
|
2418
2558
|
}
|
|
2419
2559
|
}
|
|
2420
2560
|
return {
|
|
2421
2561
|
identity: this.mapClaims(claims, provider.claimMapping),
|
|
2422
|
-
returnTo: this.normalizeReturnTo(
|
|
2423
|
-
body?.returnTo || c.req.query("returnTo"),
|
|
2424
|
-
c
|
|
2425
|
-
)
|
|
2562
|
+
returnTo: this.normalizeReturnTo(body?.returnTo || c.req.query("returnTo"), c)
|
|
2426
2563
|
};
|
|
2427
2564
|
}
|
|
2428
2565
|
mapClaims(claims, mapping) {
|
|
@@ -2452,24 +2589,38 @@ var AdminAuthController = class {
|
|
|
2452
2589
|
return typeof value === "string" ? value : void 0;
|
|
2453
2590
|
}
|
|
2454
2591
|
readStringArrayClaim(value) {
|
|
2455
|
-
if (!value)
|
|
2592
|
+
if (!value)
|
|
2593
|
+
return void 0;
|
|
2456
2594
|
if (Array.isArray(value)) {
|
|
2457
2595
|
return value.filter((entry) => typeof entry === "string");
|
|
2458
2596
|
}
|
|
2459
|
-
if (typeof value === "string")
|
|
2597
|
+
if (typeof value === "string")
|
|
2598
|
+
return [value];
|
|
2460
2599
|
return void 0;
|
|
2461
2600
|
}
|
|
2462
2601
|
async getOidcDiscovery(provider) {
|
|
2463
|
-
const discoveryUrl = new URL(
|
|
2464
|
-
provider.issuer.replace(/\/$/, "") + "/.well-known/openid-configuration"
|
|
2465
|
-
);
|
|
2602
|
+
const discoveryUrl = new URL(provider.issuer.replace(/\/$/, "") + "/.well-known/openid-configuration");
|
|
2466
2603
|
const response = await fetch(discoveryUrl.toString());
|
|
2467
|
-
if (!response.ok)
|
|
2604
|
+
if (!response.ok)
|
|
2605
|
+
throw new Error("Failed to load OIDC discovery document.");
|
|
2468
2606
|
return response.json();
|
|
2469
2607
|
}
|
|
2470
2608
|
defaultCallbackPath(providerId, origin = "") {
|
|
2471
2609
|
return `${origin}/api/admin/auth/${providerId}/callback`;
|
|
2472
2610
|
}
|
|
2611
|
+
buildCustomProviderStartUrl(startUrl, origin) {
|
|
2612
|
+
const value = startUrl.trim();
|
|
2613
|
+
if (!value || /^(undefined|null)(\/|$)/i.test(value))
|
|
2614
|
+
return null;
|
|
2615
|
+
try {
|
|
2616
|
+
const url = new URL(value, origin);
|
|
2617
|
+
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
2618
|
+
return null;
|
|
2619
|
+
return url;
|
|
2620
|
+
} catch {
|
|
2621
|
+
return null;
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2473
2624
|
async signState(payload) {
|
|
2474
2625
|
return new import_jose2.SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime("15m").sign(this.getStateSecret());
|
|
2475
2626
|
}
|
|
@@ -2489,13 +2640,14 @@ var AdminAuthController = class {
|
|
|
2489
2640
|
return new import_node_util3.TextEncoder().encode(secret);
|
|
2490
2641
|
}
|
|
2491
2642
|
normalizeReturnTo(returnTo, c) {
|
|
2492
|
-
if (returnTo)
|
|
2643
|
+
if (returnTo)
|
|
2644
|
+
return returnTo;
|
|
2493
2645
|
const url = new URL(c.req.url);
|
|
2494
2646
|
return `${url.origin}/admin`;
|
|
2495
2647
|
}
|
|
2496
2648
|
};
|
|
2497
2649
|
|
|
2498
|
-
// src/controllers/preview.controller.
|
|
2650
|
+
// src/controllers/preview.controller.js
|
|
2499
2651
|
var import_jose3 = require("jose");
|
|
2500
2652
|
var import_node_util4 = require("util");
|
|
2501
2653
|
var PreviewController = class {
|
|
@@ -2538,7 +2690,7 @@ var PreviewController = class {
|
|
|
2538
2690
|
}
|
|
2539
2691
|
};
|
|
2540
2692
|
|
|
2541
|
-
// src/middleware/auth.
|
|
2693
|
+
// src/middleware/auth.js
|
|
2542
2694
|
function requireAuth() {
|
|
2543
2695
|
return async (c, next) => {
|
|
2544
2696
|
const authHeader = c.req.header("Authorization");
|
|
@@ -2570,7 +2722,7 @@ function optionalAuth() {
|
|
|
2570
2722
|
};
|
|
2571
2723
|
}
|
|
2572
2724
|
|
|
2573
|
-
// src/utils/openapi.
|
|
2725
|
+
// src/utils/openapi.js
|
|
2574
2726
|
function generateOpenApi(config) {
|
|
2575
2727
|
const spec = {
|
|
2576
2728
|
openapi: "3.0.0",
|
|
@@ -3279,7 +3431,8 @@ function fieldsToProperties(fields) {
|
|
|
3279
3431
|
required.push(...nested.required);
|
|
3280
3432
|
continue;
|
|
3281
3433
|
}
|
|
3282
|
-
if (!field.name)
|
|
3434
|
+
if (!field.name)
|
|
3435
|
+
continue;
|
|
3283
3436
|
props[field.name] = fieldToSchema(field);
|
|
3284
3437
|
if (field.required) {
|
|
3285
3438
|
required.push(field.name);
|
|
@@ -3391,11 +3544,12 @@ function fieldToSchema(field) {
|
|
|
3391
3544
|
default:
|
|
3392
3545
|
schema = { type: "string" };
|
|
3393
3546
|
}
|
|
3394
|
-
if (field.label)
|
|
3547
|
+
if (field.label)
|
|
3548
|
+
schema.description = field.label;
|
|
3395
3549
|
return schema;
|
|
3396
3550
|
}
|
|
3397
3551
|
|
|
3398
|
-
// src/utils/swagger.
|
|
3552
|
+
// src/utils/swagger.js
|
|
3399
3553
|
function getSwaggerHtml(specUrl = "/api/openapi.json") {
|
|
3400
3554
|
return `
|
|
3401
3555
|
<!DOCTYPE html>
|
|
@@ -3444,7 +3598,7 @@ function getSwaggerHtml(specUrl = "/api/openapi.json") {
|
|
|
3444
3598
|
`;
|
|
3445
3599
|
}
|
|
3446
3600
|
|
|
3447
|
-
// src/router.
|
|
3601
|
+
// src/router.js
|
|
3448
3602
|
function accessGate(target, action) {
|
|
3449
3603
|
return async (c, next) => {
|
|
3450
3604
|
const user = c.get("user");
|
|
@@ -3461,7 +3615,8 @@ function accessGate(target, action) {
|
|
|
3461
3615
|
};
|
|
3462
3616
|
}
|
|
3463
3617
|
async function checkAccess(access, accessArgs) {
|
|
3464
|
-
if (access === void 0 || access === null)
|
|
3618
|
+
if (access === void 0 || access === null)
|
|
3619
|
+
return true;
|
|
3465
3620
|
if (typeof access === "function") {
|
|
3466
3621
|
try {
|
|
3467
3622
|
const result = await access(accessArgs);
|
|
@@ -3477,7 +3632,8 @@ async function checkAccess(access, accessArgs) {
|
|
|
3477
3632
|
return true;
|
|
3478
3633
|
}
|
|
3479
3634
|
function serializeFieldForApi(f) {
|
|
3480
|
-
if (!f)
|
|
3635
|
+
if (!f)
|
|
3636
|
+
return f;
|
|
3481
3637
|
const serialized = { ...f };
|
|
3482
3638
|
if (serialized.admin?.hooks) {
|
|
3483
3639
|
const hooks = { ...serialized.admin.hooks };
|
|
@@ -3510,8 +3666,10 @@ function registerRoutes(app, config) {
|
|
|
3510
3666
|
let globals = [...config.globals];
|
|
3511
3667
|
if (siteId && config.onSchemaFetch) {
|
|
3512
3668
|
const dynamic = await config.onSchemaFetch(siteId);
|
|
3513
|
-
if (dynamic.collections)
|
|
3514
|
-
|
|
3669
|
+
if (dynamic.collections)
|
|
3670
|
+
collections = [...collections, ...dynamic.collections];
|
|
3671
|
+
if (dynamic.globals)
|
|
3672
|
+
globals = [...globals, ...dynamic.globals];
|
|
3515
3673
|
if (dynamic.admin) {
|
|
3516
3674
|
config.admin = { ...config.admin, ...dynamic.admin };
|
|
3517
3675
|
}
|
|
@@ -3522,8 +3680,10 @@ function registerRoutes(app, config) {
|
|
|
3522
3680
|
const user = c.get("user");
|
|
3523
3681
|
const accessArgs = { user, req: c.req, doc: null };
|
|
3524
3682
|
const serializeAccess = async (access) => {
|
|
3525
|
-
if (typeof access === "string")
|
|
3526
|
-
|
|
3683
|
+
if (typeof access === "string")
|
|
3684
|
+
return access;
|
|
3685
|
+
if (typeof access === "boolean")
|
|
3686
|
+
return access;
|
|
3527
3687
|
return checkAccess(access, accessArgs);
|
|
3528
3688
|
};
|
|
3529
3689
|
const filteredCollections = await Promise.all(collections.filter((col) => !siteId || col.shared || !col.siteId || col.siteId === siteId).map(async (col) => ({
|
|
@@ -3616,7 +3776,8 @@ function registerRoutes(app, config) {
|
|
|
3616
3776
|
let collections = [...config.collections];
|
|
3617
3777
|
if (siteId && config.onSchemaFetch) {
|
|
3618
3778
|
const dynamic = await config.onSchemaFetch(siteId);
|
|
3619
|
-
if (dynamic.collections)
|
|
3779
|
+
if (dynamic.collections)
|
|
3780
|
+
collections = [...collections, ...dynamic.collections];
|
|
3620
3781
|
}
|
|
3621
3782
|
const user = c.get("user");
|
|
3622
3783
|
let collection = collections.find((col) => col.slug === colSlug);
|
|
@@ -3635,7 +3796,8 @@ function registerRoutes(app, config) {
|
|
|
3635
3796
|
let globals = [...config.globals];
|
|
3636
3797
|
if (siteId && config.onSchemaFetch) {
|
|
3637
3798
|
const dynamic = await config.onSchemaFetch(siteId);
|
|
3638
|
-
if (dynamic.globals)
|
|
3799
|
+
if (dynamic.globals)
|
|
3800
|
+
globals = [...globals, ...dynamic.globals];
|
|
3639
3801
|
}
|
|
3640
3802
|
const glb = globals.find((g) => g.slug === colSlug);
|
|
3641
3803
|
if (!glb) {
|
|
@@ -3693,9 +3855,12 @@ function registerRoutes(app, config) {
|
|
|
3693
3855
|
const user = c.get("user");
|
|
3694
3856
|
const key = c.req.param("key");
|
|
3695
3857
|
const scope = c.req.query("scope");
|
|
3696
|
-
if (!db)
|
|
3697
|
-
|
|
3698
|
-
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);
|
|
3699
3864
|
const getGlobalPreference = async () => {
|
|
3700
3865
|
const globalDoc = await db.findOne({ collection: "__global_preferences", id: key });
|
|
3701
3866
|
return globalDoc ? globalDoc.value : null;
|
|
@@ -3705,7 +3870,8 @@ function registerRoutes(app, config) {
|
|
|
3705
3870
|
return c.json({ key, value: globalValue2 });
|
|
3706
3871
|
}
|
|
3707
3872
|
const doc = await db.findOne({ collection: user.collection, id: user.sub });
|
|
3708
|
-
if (!doc)
|
|
3873
|
+
if (!doc)
|
|
3874
|
+
return c.json({ error: true, message: "User not found." }, 404);
|
|
3709
3875
|
const preferences = typeof doc.__preferences === "object" && doc.__preferences !== null ? doc.__preferences : {};
|
|
3710
3876
|
if (key in preferences) {
|
|
3711
3877
|
return c.json({ key, value: preferences[key] ?? null });
|
|
@@ -3718,9 +3884,12 @@ function registerRoutes(app, config) {
|
|
|
3718
3884
|
const user = c.get("user");
|
|
3719
3885
|
const key = c.req.param("key");
|
|
3720
3886
|
const scope = c.req.query("scope");
|
|
3721
|
-
if (!db)
|
|
3722
|
-
|
|
3723
|
-
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);
|
|
3724
3893
|
const body = await c.req.json().catch(() => ({}));
|
|
3725
3894
|
if (scope === "global") {
|
|
3726
3895
|
const isAdminUser = Array.isArray(user?.roles) && user.roles.includes("admin");
|
|
@@ -3743,7 +3912,8 @@ function registerRoutes(app, config) {
|
|
|
3743
3912
|
return c.json({ key, value: body.value });
|
|
3744
3913
|
}
|
|
3745
3914
|
const doc = await db.findOne({ collection: user.collection, id: user.sub });
|
|
3746
|
-
if (!doc)
|
|
3915
|
+
if (!doc)
|
|
3916
|
+
return c.json({ error: true, message: "User not found." }, 404);
|
|
3747
3917
|
const preferences = typeof doc.__preferences === "object" && doc.__preferences !== null ? doc.__preferences : {};
|
|
3748
3918
|
const nextPreferences = { ...preferences, [key]: body.value };
|
|
3749
3919
|
await db.update({
|
|
@@ -3758,9 +3928,12 @@ function registerRoutes(app, config) {
|
|
|
3758
3928
|
const user = c.get("user");
|
|
3759
3929
|
const key = c.req.param("key");
|
|
3760
3930
|
const scope = c.req.query("scope");
|
|
3761
|
-
if (!db)
|
|
3762
|
-
|
|
3763
|
-
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);
|
|
3764
3937
|
if (scope === "global") {
|
|
3765
3938
|
const isAdminUser = Array.isArray(user?.roles) && user.roles.includes("admin");
|
|
3766
3939
|
if (!isAdminUser) {
|
|
@@ -3770,7 +3943,8 @@ function registerRoutes(app, config) {
|
|
|
3770
3943
|
return c.json({ success: true });
|
|
3771
3944
|
}
|
|
3772
3945
|
const doc = await db.findOne({ collection: user.collection, id: user.sub });
|
|
3773
|
-
if (!doc)
|
|
3946
|
+
if (!doc)
|
|
3947
|
+
return c.json({ error: true, message: "User not found." }, 404);
|
|
3774
3948
|
const preferences = typeof doc.__preferences === "object" && doc.__preferences !== null ? { ...doc.__preferences } : {};
|
|
3775
3949
|
delete preferences[key];
|
|
3776
3950
|
await db.update({
|
|
@@ -3806,7 +3980,8 @@ function registerRoutes(app, config) {
|
|
|
3806
3980
|
app.post("/api/admin/auth/:provider/exchange", (c) => adminAuthController.exchange(c));
|
|
3807
3981
|
app.post("/api/admin/logout", (c) => adminAuthController.logout(c));
|
|
3808
3982
|
for (const collection of config.collections) {
|
|
3809
|
-
if (!collection.auth)
|
|
3983
|
+
if (!collection.auth)
|
|
3984
|
+
continue;
|
|
3810
3985
|
const path = `/api/collections/${collection.slug}`;
|
|
3811
3986
|
const authController = new AuthController(collection);
|
|
3812
3987
|
app.post(`${path}/login`, (c) => authController.login(c));
|
|
@@ -3908,25 +4083,39 @@ function registerRoutes(app, config) {
|
|
|
3908
4083
|
if (collection.auth && id) {
|
|
3909
4084
|
const authController = new AuthController(collection);
|
|
3910
4085
|
const method2 = c.req.method;
|
|
3911
|
-
if (method2 === "POST" && id === "login")
|
|
3912
|
-
|
|
3913
|
-
if (method2 === "
|
|
3914
|
-
|
|
3915
|
-
if (method2 === "
|
|
3916
|
-
|
|
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);
|
|
3917
4098
|
}
|
|
3918
4099
|
const controller = new CollectionController(collection);
|
|
3919
4100
|
const method = c.req.method;
|
|
3920
4101
|
if (id) {
|
|
3921
|
-
if (method === "GET")
|
|
3922
|
-
|
|
3923
|
-
if (method === "
|
|
3924
|
-
|
|
3925
|
-
if (method === "
|
|
3926
|
-
|
|
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);
|
|
3927
4114
|
} else {
|
|
3928
|
-
if (method === "GET")
|
|
3929
|
-
|
|
4115
|
+
if (method === "GET")
|
|
4116
|
+
return controller.find(c);
|
|
4117
|
+
if (method === "POST")
|
|
4118
|
+
return controller.create(c);
|
|
3930
4119
|
}
|
|
3931
4120
|
}
|
|
3932
4121
|
}
|
|
@@ -3947,10 +4136,13 @@ function registerRoutes(app, config) {
|
|
|
3947
4136
|
const controller = new GlobalController(global);
|
|
3948
4137
|
const method = c.req.method;
|
|
3949
4138
|
if (id) {
|
|
3950
|
-
if (method === "POST" && id === "seed")
|
|
4139
|
+
if (method === "POST" && id === "seed")
|
|
4140
|
+
return controller.seed(c);
|
|
3951
4141
|
} else {
|
|
3952
|
-
if (method === "GET")
|
|
3953
|
-
|
|
4142
|
+
if (method === "GET")
|
|
4143
|
+
return controller.get(c);
|
|
4144
|
+
if (method === "PATCH")
|
|
4145
|
+
return controller.update(c);
|
|
3954
4146
|
}
|
|
3955
4147
|
}
|
|
3956
4148
|
}
|
|
@@ -3958,7 +4150,7 @@ function registerRoutes(app, config) {
|
|
|
3958
4150
|
});
|
|
3959
4151
|
}
|
|
3960
4152
|
|
|
3961
|
-
// src/utils/config.
|
|
4153
|
+
// src/utils/config.js
|
|
3962
4154
|
var AUDIT_COLLECTION_SLUG = "__audit";
|
|
3963
4155
|
var SYSTEM_FIELDS = [
|
|
3964
4156
|
{
|
|
@@ -4172,7 +4364,8 @@ function normalizeConfig(config) {
|
|
|
4172
4364
|
});
|
|
4173
4365
|
const hasAuditCollection = normalizedCollections.some((col) => col.slug === AUDIT_COLLECTION_SLUG);
|
|
4174
4366
|
const systemCollections = [];
|
|
4175
|
-
if (needsAudit && !hasAuditCollection)
|
|
4367
|
+
if (needsAudit && !hasAuditCollection)
|
|
4368
|
+
systemCollections.push(AUDIT_COLLECTION);
|
|
4176
4369
|
if (needsWorkflow && !normalizedCollections.some((col) => col.slug === WORKFLOW_HISTORY_COLLECTION)) {
|
|
4177
4370
|
systemCollections.push(WORKFLOW_HISTORY_COLLECTION_CONFIG);
|
|
4178
4371
|
}
|
|
@@ -4186,7 +4379,7 @@ function normalizeConfig(config) {
|
|
|
4186
4379
|
};
|
|
4187
4380
|
}
|
|
4188
4381
|
|
|
4189
|
-
// src/app.
|
|
4382
|
+
// src/app.js
|
|
4190
4383
|
async function createDyrectedApp(rawConfig) {
|
|
4191
4384
|
const config = normalizeConfig(rawConfig);
|
|
4192
4385
|
const app = new import_hono.Hono();
|