@dyrected/core 2.5.56 → 2.5.59
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-BqhrBrSM.d.cts +1522 -0
- package/dist/app-config-BqhrBrSM.d.ts +1522 -0
- package/dist/app-config-C3X5BAyK.d.cts +1539 -0
- package/dist/app-config-C3X5BAyK.d.ts +1539 -0
- package/dist/app-config-oU1EGHND.d.cts +1538 -0
- package/dist/app-config-oU1EGHND.d.ts +1538 -0
- package/dist/app-config-wnLtFu6c.d.cts +1538 -0
- package/dist/app-config-wnLtFu6c.d.ts +1538 -0
- package/dist/index.cjs +79 -0
- package/dist/index.d.cts +130 -8
- package/dist/index.d.ts +130 -8
- package/dist/index.js +55 -0
- package/dist/server.cjs +436 -84
- package/dist/server.d.cts +26 -5
- package/dist/server.d.ts +26 -5
- package/dist/server.js +436 -84
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -346,6 +346,9 @@ async function evaluateAccess(expression, context) {
|
|
|
346
346
|
if (typeof expression === "boolean") return expression;
|
|
347
347
|
try {
|
|
348
348
|
const result = await jexl2.eval(expression, context);
|
|
349
|
+
if (result && typeof result === "object" && !Array.isArray(result)) {
|
|
350
|
+
return result;
|
|
351
|
+
}
|
|
349
352
|
return !!result;
|
|
350
353
|
} catch (err) {
|
|
351
354
|
console.error("[dyrected/core] Jexl evaluation failed:", err);
|
|
@@ -353,6 +356,181 @@ async function evaluateAccess(expression, context) {
|
|
|
353
356
|
}
|
|
354
357
|
}
|
|
355
358
|
|
|
359
|
+
// src/auth/access.ts
|
|
360
|
+
function isNamedAccessPolicy(value) {
|
|
361
|
+
return !!value && typeof value === "object" && "policy" in value && typeof value.policy === "string";
|
|
362
|
+
}
|
|
363
|
+
async function resolveAccess(config, access, args) {
|
|
364
|
+
if (access === void 0 || access === null) return void 0;
|
|
365
|
+
if (typeof access === "boolean" || typeof access === "string") {
|
|
366
|
+
return evaluateAccess(access, args);
|
|
367
|
+
}
|
|
368
|
+
if (typeof access === "function") {
|
|
369
|
+
try {
|
|
370
|
+
return await access(args);
|
|
371
|
+
} catch (err) {
|
|
372
|
+
console.error("[dyrected/core] Functional access check failed:", err);
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
if (isNamedAccessPolicy(access)) {
|
|
377
|
+
const resolver = config.accessPolicies?.[access.policy];
|
|
378
|
+
if (!resolver) {
|
|
379
|
+
console.error(`[dyrected/core] Unknown access policy "${access.policy}".`);
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
try {
|
|
383
|
+
return await resolver({ ...args, params: access.params });
|
|
384
|
+
} catch (err) {
|
|
385
|
+
console.error(`[dyrected/core] Access policy "${access.policy}" failed:`, err);
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// src/utils/access-control.ts
|
|
393
|
+
function toHookRequestContext(req) {
|
|
394
|
+
return {
|
|
395
|
+
query: req.query(),
|
|
396
|
+
headers: req.header(),
|
|
397
|
+
raw: req.raw
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
async function resolveBooleanAccess(config, access, args) {
|
|
401
|
+
const result = await resolveAccess(config, access, args);
|
|
402
|
+
if (result === void 0) return true;
|
|
403
|
+
return typeof result === "boolean" ? result : false;
|
|
404
|
+
}
|
|
405
|
+
async function matchesAccessConstraint(config, collection, id, constraint) {
|
|
406
|
+
if (!config.db) return false;
|
|
407
|
+
const match = await config.db.find({
|
|
408
|
+
collection,
|
|
409
|
+
where: { AND: [{ id: { equals: id } }, constraint] },
|
|
410
|
+
limit: 1
|
|
411
|
+
});
|
|
412
|
+
return match.total > 0;
|
|
413
|
+
}
|
|
414
|
+
async function resolveCollectionAccess(config, collection, action, access, args) {
|
|
415
|
+
const result = await resolveAccess(config, access, args);
|
|
416
|
+
if (result === void 0) return { allowed: true };
|
|
417
|
+
if (typeof result === "boolean") return { allowed: result };
|
|
418
|
+
if (action === "read" && !args.id) {
|
|
419
|
+
return { allowed: true, constraint: result };
|
|
420
|
+
}
|
|
421
|
+
if (!args.id) {
|
|
422
|
+
return { allowed: false };
|
|
423
|
+
}
|
|
424
|
+
return {
|
|
425
|
+
allowed: await matchesAccessConstraint(config, collection, args.id, result)
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
function resolveFieldAccessId(context) {
|
|
429
|
+
const candidate = context.id ?? context.doc?.id ?? context.data?.id;
|
|
430
|
+
return typeof candidate === "string" && candidate.length > 0 ? candidate : void 0;
|
|
431
|
+
}
|
|
432
|
+
async function applyFieldReadAccess(context, doc) {
|
|
433
|
+
if (!doc || typeof doc !== "object") return doc;
|
|
434
|
+
const result = { ...doc };
|
|
435
|
+
for (const field of context.fields) {
|
|
436
|
+
if (field.type === "row" && field.fields) {
|
|
437
|
+
Object.assign(result, await applyFieldReadAccess({ ...context, fields: field.fields }, result));
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
if (!field.name) continue;
|
|
441
|
+
const value = result[field.name];
|
|
442
|
+
const canRead = await resolveBooleanAccess(context.config, field.access?.read, {
|
|
443
|
+
user: context.user,
|
|
444
|
+
req: context.req,
|
|
445
|
+
doc: context.doc,
|
|
446
|
+
data: context.data,
|
|
447
|
+
id: resolveFieldAccessId(context)
|
|
448
|
+
});
|
|
449
|
+
if (!canRead) {
|
|
450
|
+
delete result[field.name];
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
if (value === void 0 || value === null) continue;
|
|
454
|
+
if (field.type === "object" && field.fields && typeof value === "object" && !Array.isArray(value)) {
|
|
455
|
+
result[field.name] = await applyFieldReadAccess({ ...context, fields: field.fields }, value);
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
if (field.type === "array" && field.fields && Array.isArray(value)) {
|
|
459
|
+
const childFields = field.fields;
|
|
460
|
+
result[field.name] = await Promise.all(
|
|
461
|
+
value.map(
|
|
462
|
+
(item) => typeof item === "object" && item !== null ? applyFieldReadAccess({ ...context, fields: childFields }, item) : item
|
|
463
|
+
)
|
|
464
|
+
);
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
if (field.type === "blocks" && field.blocks && Array.isArray(value)) {
|
|
468
|
+
result[field.name] = await Promise.all(
|
|
469
|
+
value.map(async (item) => {
|
|
470
|
+
if (typeof item !== "object" || item === null) return item;
|
|
471
|
+
const typedItem = item;
|
|
472
|
+
const block = field.blocks?.find((candidate) => candidate.slug === typedItem.blockType);
|
|
473
|
+
if (!block || !block.fields) return item;
|
|
474
|
+
return applyFieldReadAccess({ ...context, fields: block.fields }, typedItem);
|
|
475
|
+
})
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return result;
|
|
480
|
+
}
|
|
481
|
+
async function applyFieldWriteAccess(context, data) {
|
|
482
|
+
if (!data || typeof data !== "object") return data;
|
|
483
|
+
const result = { ...data };
|
|
484
|
+
for (const field of context.fields) {
|
|
485
|
+
if (field.type === "row" && field.fields) {
|
|
486
|
+
Object.assign(result, await applyFieldWriteAccess({ ...context, fields: field.fields }, result));
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
if (!field.name || !(field.name in result)) continue;
|
|
490
|
+
const canUpdate = await resolveBooleanAccess(context.config, field.access?.update, {
|
|
491
|
+
user: context.user,
|
|
492
|
+
req: context.req,
|
|
493
|
+
doc: context.doc,
|
|
494
|
+
data: context.data,
|
|
495
|
+
id: resolveFieldAccessId(context)
|
|
496
|
+
});
|
|
497
|
+
if (!canUpdate) {
|
|
498
|
+
delete result[field.name];
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
const value = result[field.name];
|
|
502
|
+
if (value === void 0 || value === null) continue;
|
|
503
|
+
if (field.type === "object" && field.fields && typeof value === "object" && !Array.isArray(value)) {
|
|
504
|
+
result[field.name] = await applyFieldWriteAccess({ ...context, fields: field.fields }, value);
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
if (field.type === "array" && field.fields && Array.isArray(value)) {
|
|
508
|
+
const childFields = field.fields;
|
|
509
|
+
result[field.name] = await Promise.all(
|
|
510
|
+
value.map(
|
|
511
|
+
(item) => typeof item === "object" && item !== null ? applyFieldWriteAccess({ ...context, fields: childFields }, item) : item
|
|
512
|
+
)
|
|
513
|
+
);
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
if (field.type === "blocks" && field.blocks && Array.isArray(value)) {
|
|
517
|
+
result[field.name] = await Promise.all(
|
|
518
|
+
value.map(async (item) => {
|
|
519
|
+
if (typeof item !== "object" || item === null) return item;
|
|
520
|
+
const typedItem = item;
|
|
521
|
+
const block = field.blocks?.find((candidate) => candidate.slug === typedItem.blockType);
|
|
522
|
+
if (!block || !block.fields) return item;
|
|
523
|
+
return applyFieldWriteAccess({ ...context, fields: block.fields }, typedItem);
|
|
524
|
+
})
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
return result;
|
|
529
|
+
}
|
|
530
|
+
function mergeWhereConstraint(where, constraint) {
|
|
531
|
+
return where ? { AND: [where, constraint] } : constraint;
|
|
532
|
+
}
|
|
533
|
+
|
|
356
534
|
// src/controllers/collection.controller.ts
|
|
357
535
|
var CollectionController = class {
|
|
358
536
|
collection;
|
|
@@ -368,11 +546,23 @@ var CollectionController = class {
|
|
|
368
546
|
return config.adminAuth?.providers?.find((p) => p.members) || null;
|
|
369
547
|
}
|
|
370
548
|
toHookRequestContext(c) {
|
|
371
|
-
return
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
549
|
+
return toHookRequestContext(c.req);
|
|
550
|
+
}
|
|
551
|
+
async evaluateAccess(c, action, options = {}) {
|
|
552
|
+
const config = c.get("config");
|
|
553
|
+
return resolveCollectionAccess(
|
|
554
|
+
config,
|
|
555
|
+
this.collection.slug,
|
|
556
|
+
action,
|
|
557
|
+
this.collection.access?.[action],
|
|
558
|
+
{
|
|
559
|
+
id: options.id,
|
|
560
|
+
user: c.get("user"),
|
|
561
|
+
req: this.toHookRequestContext(c),
|
|
562
|
+
doc: options.doc ?? void 0,
|
|
563
|
+
data: options.data
|
|
564
|
+
}
|
|
565
|
+
);
|
|
376
566
|
}
|
|
377
567
|
async find(c) {
|
|
378
568
|
const config = c.get("config");
|
|
@@ -452,6 +642,13 @@ var CollectionController = class {
|
|
|
452
642
|
if (this.collection.workflow && !canViewWorkflowDraft(this.collection.workflow, user)) {
|
|
453
643
|
where = where ? { AND: [where, { __published: { exists: true } }] } : { __published: { exists: true } };
|
|
454
644
|
}
|
|
645
|
+
const access = await this.evaluateAccess(c, "read");
|
|
646
|
+
if (!access.allowed) {
|
|
647
|
+
return c.json({ error: true, message: `Access denied: read on ${this.collection.slug}` }, 403);
|
|
648
|
+
}
|
|
649
|
+
if (access.constraint) {
|
|
650
|
+
where = mergeWhereConstraint(where, access.constraint);
|
|
651
|
+
}
|
|
455
652
|
let result = await db.find({
|
|
456
653
|
collection: this.collection.slug,
|
|
457
654
|
limit,
|
|
@@ -484,7 +681,14 @@ var CollectionController = class {
|
|
|
484
681
|
db: readonlyDb
|
|
485
682
|
});
|
|
486
683
|
const docWithFieldHooks = await executeFieldAfterRead(this.collection.fields, docWithCollectionHooks, user, readonlyDb);
|
|
487
|
-
|
|
684
|
+
const docWithFieldAccess = await applyFieldReadAccess({
|
|
685
|
+
config,
|
|
686
|
+
fields: this.collection.fields,
|
|
687
|
+
user,
|
|
688
|
+
req: this.toHookRequestContext(c),
|
|
689
|
+
doc: docWithFieldHooks
|
|
690
|
+
}, docWithFieldHooks);
|
|
691
|
+
processedDocs.push(docWithFieldAccess);
|
|
488
692
|
}
|
|
489
693
|
result.docs = processedDocs;
|
|
490
694
|
if (depth > 0) {
|
|
@@ -529,6 +733,10 @@ var CollectionController = class {
|
|
|
529
733
|
doc = materializeWorkflowDocument(doc, this.collection.workflow, user);
|
|
530
734
|
if (!doc) return c.json({ message: "Not Found" }, 404);
|
|
531
735
|
}
|
|
736
|
+
const access = await this.evaluateAccess(c, "read", { id, doc });
|
|
737
|
+
if (!access.allowed) {
|
|
738
|
+
return c.json({ error: true, message: `Access denied: read on ${this.collection.slug}` }, 403);
|
|
739
|
+
}
|
|
532
740
|
const docWithDefaults = DefaultsService.apply(this.collection.fields, doc);
|
|
533
741
|
const docWithCollectionHooks = await runCollectionHooks(this.collection.hooks?.afterRead, {
|
|
534
742
|
doc: docWithDefaults,
|
|
@@ -537,17 +745,24 @@ var CollectionController = class {
|
|
|
537
745
|
db: readonlyDb
|
|
538
746
|
});
|
|
539
747
|
const docWithFieldHooks = await executeFieldAfterRead(this.collection.fields, docWithCollectionHooks, user, readonlyDb);
|
|
540
|
-
|
|
748
|
+
const docWithFieldAccess = await applyFieldReadAccess({
|
|
749
|
+
config,
|
|
750
|
+
fields: this.collection.fields,
|
|
751
|
+
user,
|
|
752
|
+
req: this.toHookRequestContext(c),
|
|
753
|
+
doc: docWithFieldHooks
|
|
754
|
+
}, docWithFieldHooks);
|
|
755
|
+
if (depth > 0 && docWithFieldAccess) {
|
|
541
756
|
const populationService = new PopulationService(db, config.collections);
|
|
542
757
|
const populatedDoc = await populationService.populate({
|
|
543
|
-
data:
|
|
758
|
+
data: docWithFieldAccess,
|
|
544
759
|
fields: this.collection.fields,
|
|
545
760
|
currentDepth: 0,
|
|
546
761
|
maxDepth: depth
|
|
547
762
|
});
|
|
548
763
|
return c.json(populatedDoc);
|
|
549
764
|
}
|
|
550
|
-
return c.json(
|
|
765
|
+
return c.json(docWithFieldAccess);
|
|
551
766
|
}
|
|
552
767
|
async create(c) {
|
|
553
768
|
const config = c.get("config");
|
|
@@ -586,9 +801,20 @@ var CollectionController = class {
|
|
|
586
801
|
if (this.collection.workflow) {
|
|
587
802
|
data = initializeWorkflowDocument(data, this.collection.workflow);
|
|
588
803
|
}
|
|
804
|
+
const createAccess = await this.evaluateAccess(c, "create", { data });
|
|
805
|
+
if (!createAccess.allowed) {
|
|
806
|
+
return c.json({ error: true, message: `Access denied: create on ${this.collection.slug}` }, 403);
|
|
807
|
+
}
|
|
589
808
|
if (this.collection.auth && data.password) {
|
|
590
809
|
data.password = await hashPassword(data.password);
|
|
591
810
|
}
|
|
811
|
+
data = await applyFieldWriteAccess({
|
|
812
|
+
config,
|
|
813
|
+
fields: this.collection.fields,
|
|
814
|
+
user,
|
|
815
|
+
req: this.toHookRequestContext(c),
|
|
816
|
+
data
|
|
817
|
+
}, data);
|
|
592
818
|
data = await executeFieldBeforeChange(this.collection.fields, data, null, user, readonlyDb);
|
|
593
819
|
data = await runCollectionHooks(this.collection.hooks?.beforeChange, {
|
|
594
820
|
data,
|
|
@@ -623,7 +849,14 @@ var CollectionController = class {
|
|
|
623
849
|
db: readonlyDb
|
|
624
850
|
});
|
|
625
851
|
const finalDoc = await executeFieldAfterRead(this.collection.fields, readDoc, user, readonlyDb);
|
|
626
|
-
|
|
852
|
+
const accessibleDoc = await applyFieldReadAccess({
|
|
853
|
+
config,
|
|
854
|
+
fields: this.collection.fields,
|
|
855
|
+
user,
|
|
856
|
+
req: this.toHookRequestContext(c),
|
|
857
|
+
doc: finalDoc
|
|
858
|
+
}, finalDoc);
|
|
859
|
+
return c.json(accessibleDoc, 201);
|
|
627
860
|
}
|
|
628
861
|
async upload(c) {
|
|
629
862
|
const config = c.get("config");
|
|
@@ -666,6 +899,17 @@ var CollectionController = class {
|
|
|
666
899
|
createdBy: user?.sub ?? null,
|
|
667
900
|
updatedBy: user?.sub ?? null
|
|
668
901
|
};
|
|
902
|
+
const createAccess = await this.evaluateAccess(c, "create", { data });
|
|
903
|
+
if (!createAccess.allowed) {
|
|
904
|
+
return c.json({ error: true, message: `Access denied: create on ${this.collection.slug}` }, 403);
|
|
905
|
+
}
|
|
906
|
+
data = await applyFieldWriteAccess({
|
|
907
|
+
config,
|
|
908
|
+
fields: this.collection.fields,
|
|
909
|
+
user,
|
|
910
|
+
req: this.toHookRequestContext(c),
|
|
911
|
+
data
|
|
912
|
+
}, data);
|
|
669
913
|
data = await executeFieldBeforeChange(this.collection.fields, data, null, user, readonlyDb);
|
|
670
914
|
data = await runCollectionHooks(this.collection.hooks?.beforeChange, {
|
|
671
915
|
data,
|
|
@@ -693,7 +937,14 @@ var CollectionController = class {
|
|
|
693
937
|
db: readonlyDb
|
|
694
938
|
});
|
|
695
939
|
const finalDoc = await executeFieldAfterRead(this.collection.fields, readDoc, user, readonlyDb);
|
|
696
|
-
|
|
940
|
+
const accessibleDoc = await applyFieldReadAccess({
|
|
941
|
+
config,
|
|
942
|
+
fields: this.collection.fields,
|
|
943
|
+
user,
|
|
944
|
+
req: this.toHookRequestContext(c),
|
|
945
|
+
doc: finalDoc
|
|
946
|
+
}, finalDoc);
|
|
947
|
+
return c.json(accessibleDoc, 201);
|
|
697
948
|
}
|
|
698
949
|
async update(c) {
|
|
699
950
|
const config = c.get("config");
|
|
@@ -731,10 +982,22 @@ var CollectionController = class {
|
|
|
731
982
|
});
|
|
732
983
|
const originalDoc = await db.findOne({ collection: this.collection.slug, id });
|
|
733
984
|
if (!originalDoc) return c.json({ message: "Not Found" }, 404);
|
|
985
|
+
const updateAccess = await this.evaluateAccess(c, "update", { id, doc: originalDoc, data });
|
|
986
|
+
if (!updateAccess.allowed) {
|
|
987
|
+
return c.json({ error: true, message: `Access denied: update on ${this.collection.slug}` }, 403);
|
|
988
|
+
}
|
|
734
989
|
let before = null;
|
|
735
990
|
if (this.collection.audit) {
|
|
736
991
|
before = originalDoc;
|
|
737
992
|
}
|
|
993
|
+
data = await applyFieldWriteAccess({
|
|
994
|
+
config,
|
|
995
|
+
fields: this.collection.fields,
|
|
996
|
+
user,
|
|
997
|
+
req: this.toHookRequestContext(c),
|
|
998
|
+
doc: originalDoc,
|
|
999
|
+
data
|
|
1000
|
+
}, data);
|
|
738
1001
|
data = await executeFieldBeforeChange(this.collection.fields, data, originalDoc, user, readonlyDb);
|
|
739
1002
|
data = await runCollectionHooks(this.collection.hooks?.beforeChange, {
|
|
740
1003
|
data,
|
|
@@ -770,7 +1033,14 @@ var CollectionController = class {
|
|
|
770
1033
|
db: readonlyDb
|
|
771
1034
|
});
|
|
772
1035
|
const finalDoc = await executeFieldAfterRead(this.collection.fields, readDoc, user, readonlyDb);
|
|
773
|
-
|
|
1036
|
+
const accessibleDoc = await applyFieldReadAccess({
|
|
1037
|
+
config,
|
|
1038
|
+
fields: this.collection.fields,
|
|
1039
|
+
user,
|
|
1040
|
+
req: this.toHookRequestContext(c),
|
|
1041
|
+
doc: finalDoc
|
|
1042
|
+
}, finalDoc);
|
|
1043
|
+
return c.json(accessibleDoc);
|
|
774
1044
|
}
|
|
775
1045
|
async transition(c) {
|
|
776
1046
|
const config = c.get("config");
|
|
@@ -807,7 +1077,7 @@ var CollectionController = class {
|
|
|
807
1077
|
const readAccess = this.collection.access?.read;
|
|
808
1078
|
if (readAccess !== void 0 && readAccess !== null) {
|
|
809
1079
|
const args = { user: c.get("user"), req: c.req, doc: document };
|
|
810
|
-
const result2 =
|
|
1080
|
+
const result2 = await resolveAccess(config, readAccess, args);
|
|
811
1081
|
let allowed = result2 === true;
|
|
812
1082
|
if (result2 && typeof result2 === "object") {
|
|
813
1083
|
const match = await config.db.find({
|
|
@@ -918,6 +1188,10 @@ var CollectionController = class {
|
|
|
918
1188
|
const user = c.get("user");
|
|
919
1189
|
const doc = await db.findOne({ collection: this.collection.slug, id });
|
|
920
1190
|
if (!doc) return c.json({ message: "Not Found" }, 404);
|
|
1191
|
+
const deleteAccess = await this.evaluateAccess(c, "delete", { id, doc });
|
|
1192
|
+
if (!deleteAccess.allowed) {
|
|
1193
|
+
return c.json({ error: true, message: `Access denied: delete on ${this.collection.slug}` }, 403);
|
|
1194
|
+
}
|
|
921
1195
|
let before = null;
|
|
922
1196
|
if (this.collection.audit) {
|
|
923
1197
|
before = doc;
|
|
@@ -977,6 +1251,11 @@ var CollectionController = class {
|
|
|
977
1251
|
failed.push({ id, error: "Not Found" });
|
|
978
1252
|
continue;
|
|
979
1253
|
}
|
|
1254
|
+
const deleteAccess = await this.evaluateAccess(c, "delete", { id, doc });
|
|
1255
|
+
if (!deleteAccess.allowed) {
|
|
1256
|
+
failed.push({ id, error: "Access denied" });
|
|
1257
|
+
continue;
|
|
1258
|
+
}
|
|
980
1259
|
let before = null;
|
|
981
1260
|
if (this.collection.audit) {
|
|
982
1261
|
before = doc;
|
|
@@ -1070,6 +1349,14 @@ var GlobalController = class {
|
|
|
1070
1349
|
await db.updateGlobal({ slug: this.global.slug, data: this.global.initialData });
|
|
1071
1350
|
data = this.global.initialData;
|
|
1072
1351
|
}
|
|
1352
|
+
const canRead = await resolveBooleanAccess(config, this.global.access?.read, {
|
|
1353
|
+
user,
|
|
1354
|
+
req: toHookRequestContext(c.req),
|
|
1355
|
+
doc: data
|
|
1356
|
+
});
|
|
1357
|
+
if (!canRead) {
|
|
1358
|
+
return c.json({ error: true, message: `Access denied: read on ${this.global.slug}` }, 403);
|
|
1359
|
+
}
|
|
1073
1360
|
const dataWithDefaults = DefaultsService.apply(this.global.fields, data);
|
|
1074
1361
|
const docWithCollectionHooks = await runCollectionHooks(this.global.hooks?.afterRead, {
|
|
1075
1362
|
doc: dataWithDefaults,
|
|
@@ -1078,17 +1365,24 @@ var GlobalController = class {
|
|
|
1078
1365
|
db: readonlyDb
|
|
1079
1366
|
});
|
|
1080
1367
|
const docWithFieldHooks = await executeFieldAfterRead(this.global.fields, docWithCollectionHooks, user, readonlyDb);
|
|
1081
|
-
|
|
1368
|
+
const docWithFieldAccess = await applyFieldReadAccess({
|
|
1369
|
+
config,
|
|
1370
|
+
fields: this.global.fields,
|
|
1371
|
+
user,
|
|
1372
|
+
req: toHookRequestContext(c.req),
|
|
1373
|
+
doc: docWithFieldHooks
|
|
1374
|
+
}, docWithFieldHooks);
|
|
1375
|
+
if (depth > 0 && docWithFieldAccess) {
|
|
1082
1376
|
const populationService = new PopulationService(db, config.collections);
|
|
1083
1377
|
const populatedData = await populationService.populate({
|
|
1084
|
-
data:
|
|
1378
|
+
data: docWithFieldAccess,
|
|
1085
1379
|
fields: this.global.fields,
|
|
1086
1380
|
currentDepth: 0,
|
|
1087
1381
|
maxDepth: depth
|
|
1088
1382
|
});
|
|
1089
1383
|
return c.json(populatedData);
|
|
1090
1384
|
}
|
|
1091
|
-
return c.json(
|
|
1385
|
+
return c.json(docWithFieldAccess);
|
|
1092
1386
|
}
|
|
1093
1387
|
async update(c) {
|
|
1094
1388
|
const config = c.get("config");
|
|
@@ -1098,7 +1392,24 @@ var GlobalController = class {
|
|
|
1098
1392
|
const body = await c.req.json();
|
|
1099
1393
|
const user = c.get("user");
|
|
1100
1394
|
const originalDoc = await db.getGlobal({ slug: this.global.slug }) || {};
|
|
1101
|
-
|
|
1395
|
+
const canUpdate = await resolveBooleanAccess(config, this.global.access?.update, {
|
|
1396
|
+
user,
|
|
1397
|
+
req: toHookRequestContext(c.req),
|
|
1398
|
+
doc: originalDoc,
|
|
1399
|
+
data: body
|
|
1400
|
+
});
|
|
1401
|
+
if (!canUpdate) {
|
|
1402
|
+
return c.json({ error: true, message: `Access denied: update on ${this.global.slug}` }, 403);
|
|
1403
|
+
}
|
|
1404
|
+
let sanitizedBody = await applyFieldWriteAccess({
|
|
1405
|
+
config,
|
|
1406
|
+
fields: this.global.fields,
|
|
1407
|
+
user,
|
|
1408
|
+
req: toHookRequestContext(c.req),
|
|
1409
|
+
doc: originalDoc,
|
|
1410
|
+
data: body
|
|
1411
|
+
}, body);
|
|
1412
|
+
let data = await executeFieldBeforeChange(this.global.fields, sanitizedBody, originalDoc, user, readonlyDb);
|
|
1102
1413
|
data = await runCollectionHooks(this.global.hooks?.beforeChange, {
|
|
1103
1414
|
data,
|
|
1104
1415
|
doc: originalDoc,
|
|
@@ -1123,7 +1434,14 @@ var GlobalController = class {
|
|
|
1123
1434
|
db: readonlyDb
|
|
1124
1435
|
});
|
|
1125
1436
|
const finalDoc = await executeFieldAfterRead(this.global.fields, readDoc, user, readonlyDb);
|
|
1126
|
-
|
|
1437
|
+
const accessibleDoc = await applyFieldReadAccess({
|
|
1438
|
+
config,
|
|
1439
|
+
fields: this.global.fields,
|
|
1440
|
+
user,
|
|
1441
|
+
req: toHookRequestContext(c.req),
|
|
1442
|
+
doc: finalDoc
|
|
1443
|
+
}, finalDoc);
|
|
1444
|
+
return c.json(accessibleDoc);
|
|
1127
1445
|
}
|
|
1128
1446
|
async seed(c) {
|
|
1129
1447
|
const config = c.get("config");
|
|
@@ -1305,7 +1623,7 @@ var MediaController = class {
|
|
|
1305
1623
|
import { SignJWT, jwtVerify, decodeJwt } from "jose";
|
|
1306
1624
|
import { TextEncoder } from "util";
|
|
1307
1625
|
function getSecret() {
|
|
1308
|
-
const secret = process.env.DYRECTED_JWT_SECRET
|
|
1626
|
+
const secret = process.env.DYRECTED_JWT_SECRET;
|
|
1309
1627
|
if (!secret) {
|
|
1310
1628
|
throw new Error(
|
|
1311
1629
|
"[dyrected/core] DYRECTED_JWT_SECRET is not set. Add it to your environment variables to enable auth collections."
|
|
@@ -2180,7 +2498,7 @@ var AdminAuthController = class {
|
|
|
2180
2498
|
return state;
|
|
2181
2499
|
}
|
|
2182
2500
|
getStateSecret() {
|
|
2183
|
-
const secret = process.env.DYRECTED_JWT_SECRET
|
|
2501
|
+
const secret = process.env.DYRECTED_JWT_SECRET;
|
|
2184
2502
|
if (!secret) {
|
|
2185
2503
|
throw new Error("[dyrected/core] DYRECTED_JWT_SECRET is not set.");
|
|
2186
2504
|
}
|
|
@@ -2198,7 +2516,7 @@ import { SignJWT as SignJWT3, jwtVerify as jwtVerify3 } from "jose";
|
|
|
2198
2516
|
import { TextEncoder as TextEncoder3 } from "util";
|
|
2199
2517
|
var PreviewController = class {
|
|
2200
2518
|
getSecret() {
|
|
2201
|
-
const secret = process.env.DYRECTED_JWT_SECRET ||
|
|
2519
|
+
const secret = process.env.DYRECTED_JWT_SECRET || "dyrected-preview-secret-change-me";
|
|
2202
2520
|
return new TextEncoder3().encode(secret);
|
|
2203
2521
|
}
|
|
2204
2522
|
/**
|
|
@@ -2237,30 +2555,74 @@ var PreviewController = class {
|
|
|
2237
2555
|
};
|
|
2238
2556
|
|
|
2239
2557
|
// src/middleware/auth.ts
|
|
2240
|
-
function
|
|
2558
|
+
function getBearerToken(c) {
|
|
2559
|
+
const authHeader = c.req.header("Authorization");
|
|
2560
|
+
return authHeader?.replace(/^Bearer\s+/i, "") || void 0;
|
|
2561
|
+
}
|
|
2562
|
+
async function resolveUser(token, config) {
|
|
2563
|
+
const payload = await verifyCollectionToken(token);
|
|
2564
|
+
if (payload.purpose) {
|
|
2565
|
+
return payload;
|
|
2566
|
+
}
|
|
2567
|
+
const db = config?.db;
|
|
2568
|
+
if (!db) {
|
|
2569
|
+
return payload;
|
|
2570
|
+
}
|
|
2571
|
+
let doc;
|
|
2572
|
+
try {
|
|
2573
|
+
doc = await db.findOne({
|
|
2574
|
+
collection: payload.collection,
|
|
2575
|
+
id: payload.sub
|
|
2576
|
+
});
|
|
2577
|
+
} catch (err) {
|
|
2578
|
+
console.error("[dyrected/core] Failed to hydrate user from token:", err);
|
|
2579
|
+
return payload;
|
|
2580
|
+
}
|
|
2581
|
+
if (!doc) {
|
|
2582
|
+
return null;
|
|
2583
|
+
}
|
|
2584
|
+
const { password: _password, ...safe } = doc;
|
|
2585
|
+
return {
|
|
2586
|
+
...safe,
|
|
2587
|
+
sub: payload.sub,
|
|
2588
|
+
email: payload.email ?? safe.email,
|
|
2589
|
+
collection: payload.collection
|
|
2590
|
+
};
|
|
2591
|
+
}
|
|
2592
|
+
function requireAuth(config) {
|
|
2241
2593
|
return async (c, next) => {
|
|
2242
|
-
|
|
2243
|
-
|
|
2594
|
+
if (c.get("user")) {
|
|
2595
|
+
return next();
|
|
2596
|
+
}
|
|
2597
|
+
const token = getBearerToken(c);
|
|
2244
2598
|
if (!token) {
|
|
2245
2599
|
return c.json({ error: true, message: "Authentication required." }, 401);
|
|
2246
2600
|
}
|
|
2601
|
+
let user;
|
|
2247
2602
|
try {
|
|
2248
|
-
|
|
2249
|
-
c.set("user", user);
|
|
2250
|
-
await next();
|
|
2603
|
+
user = await resolveUser(token, config ?? c.get("config"));
|
|
2251
2604
|
} catch {
|
|
2252
2605
|
return c.json({ error: true, message: "Invalid or expired token." }, 401);
|
|
2253
2606
|
}
|
|
2607
|
+
if (!user) {
|
|
2608
|
+
return c.json({ error: true, message: "Invalid or expired token." }, 401);
|
|
2609
|
+
}
|
|
2610
|
+
c.set("user", user);
|
|
2611
|
+
await next();
|
|
2254
2612
|
};
|
|
2255
2613
|
}
|
|
2256
|
-
function optionalAuth() {
|
|
2614
|
+
function optionalAuth(config) {
|
|
2257
2615
|
return async (c, next) => {
|
|
2258
|
-
|
|
2259
|
-
|
|
2616
|
+
if (c.get("user")) {
|
|
2617
|
+
return next();
|
|
2618
|
+
}
|
|
2619
|
+
const token = getBearerToken(c);
|
|
2260
2620
|
if (token) {
|
|
2261
2621
|
try {
|
|
2262
|
-
const user = await
|
|
2263
|
-
|
|
2622
|
+
const user = await resolveUser(token, config ?? c.get("config"));
|
|
2623
|
+
if (user) {
|
|
2624
|
+
c.set("user", user);
|
|
2625
|
+
}
|
|
2264
2626
|
} catch {
|
|
2265
2627
|
}
|
|
2266
2628
|
}
|
|
@@ -2323,37 +2685,23 @@ function getSwaggerHtml(specUrl) {
|
|
|
2323
2685
|
}
|
|
2324
2686
|
|
|
2325
2687
|
// src/router.ts
|
|
2326
|
-
function accessGate(target, action) {
|
|
2688
|
+
function accessGate(config, target, action) {
|
|
2327
2689
|
return async (c, next) => {
|
|
2328
2690
|
const user = c.get("user");
|
|
2329
2691
|
const accessExpr = target.access?.[action];
|
|
2330
2692
|
if (accessExpr === void 0 || accessExpr === null) {
|
|
2331
2693
|
return await next();
|
|
2332
2694
|
}
|
|
2333
|
-
const
|
|
2334
|
-
|
|
2695
|
+
const allowed = await resolveBooleanAccess(config, accessExpr, {
|
|
2696
|
+
user,
|
|
2697
|
+
req: toHookRequestContext(c.req)
|
|
2698
|
+
});
|
|
2335
2699
|
if (!allowed) {
|
|
2336
2700
|
return c.json({ error: true, message: `Access denied: ${action} on ${target.slug}` }, 403);
|
|
2337
2701
|
}
|
|
2338
2702
|
await next();
|
|
2339
2703
|
};
|
|
2340
2704
|
}
|
|
2341
|
-
async function checkAccess(access, accessArgs) {
|
|
2342
|
-
if (access === void 0 || access === null) return true;
|
|
2343
|
-
if (typeof access === "function") {
|
|
2344
|
-
try {
|
|
2345
|
-
const result = await access(accessArgs);
|
|
2346
|
-
return typeof result === "boolean" ? result : !!result;
|
|
2347
|
-
} catch (err) {
|
|
2348
|
-
console.error("[dyrected/core] Functional access check failed:", err);
|
|
2349
|
-
return false;
|
|
2350
|
-
}
|
|
2351
|
-
}
|
|
2352
|
-
if (typeof access === "string" || typeof access === "boolean") {
|
|
2353
|
-
return evaluateAccess(access, accessArgs);
|
|
2354
|
-
}
|
|
2355
|
-
return true;
|
|
2356
|
-
}
|
|
2357
2705
|
function serializeFieldForApi(f) {
|
|
2358
2706
|
if (!f) return f;
|
|
2359
2707
|
const serialized = { ...f };
|
|
@@ -2382,7 +2730,7 @@ function serializeFieldForApi(f) {
|
|
|
2382
2730
|
return serialized;
|
|
2383
2731
|
}
|
|
2384
2732
|
function registerRoutes(app, config) {
|
|
2385
|
-
app.get("/api/schemas", optionalAuth(), async (c) => {
|
|
2733
|
+
app.get("/api/schemas", optionalAuth(config), async (c) => {
|
|
2386
2734
|
const siteId = c.req.header("X-Site-Id");
|
|
2387
2735
|
let collections = [...config.collections];
|
|
2388
2736
|
let globals = [...config.globals];
|
|
@@ -2398,11 +2746,11 @@ function registerRoutes(app, config) {
|
|
|
2398
2746
|
}
|
|
2399
2747
|
}
|
|
2400
2748
|
const user = c.get("user");
|
|
2401
|
-
const accessArgs = { user, req: c.req
|
|
2749
|
+
const accessArgs = { user, req: toHookRequestContext(c.req) };
|
|
2402
2750
|
const serializeAccess = async (access) => {
|
|
2403
2751
|
if (typeof access === "string") return access;
|
|
2404
2752
|
if (typeof access === "boolean") return access;
|
|
2405
|
-
return
|
|
2753
|
+
return resolveBooleanAccess(config, access, accessArgs);
|
|
2406
2754
|
};
|
|
2407
2755
|
const filteredCollections = await Promise.all(collections.filter((col) => !siteId || col.shared || !col.siteId || col.siteId === siteId).map(async (col) => ({
|
|
2408
2756
|
slug: col.slug,
|
|
@@ -2488,7 +2836,7 @@ function registerRoutes(app, config) {
|
|
|
2488
2836
|
adminAuth: getPublicAdminAuthConfig(config.adminAuth, collections)
|
|
2489
2837
|
});
|
|
2490
2838
|
});
|
|
2491
|
-
app.get("/api/dyrected/options/:collection/:field", optionalAuth(), async (c) => {
|
|
2839
|
+
app.get("/api/dyrected/options/:collection/:field", optionalAuth(config), async (c) => {
|
|
2492
2840
|
const { collection: colSlug, field: fieldName } = c.req.param();
|
|
2493
2841
|
const siteId = c.req.header("X-Site-Id");
|
|
2494
2842
|
let collections = [...config.collections];
|
|
@@ -2502,8 +2850,10 @@ function registerRoutes(app, config) {
|
|
|
2502
2850
|
if (collection) {
|
|
2503
2851
|
const accessExpr = collection.access?.read;
|
|
2504
2852
|
if (accessExpr !== void 0 && accessExpr !== null) {
|
|
2505
|
-
const
|
|
2506
|
-
|
|
2853
|
+
const allowed = await resolveBooleanAccess(config, accessExpr, {
|
|
2854
|
+
user,
|
|
2855
|
+
req: toHookRequestContext(c.req)
|
|
2856
|
+
});
|
|
2507
2857
|
if (!allowed) {
|
|
2508
2858
|
return c.json({ error: true, message: `Access denied: read on ${colSlug}` }, 403);
|
|
2509
2859
|
}
|
|
@@ -2521,8 +2871,10 @@ function registerRoutes(app, config) {
|
|
|
2521
2871
|
}
|
|
2522
2872
|
const accessExpr = glb.access?.read;
|
|
2523
2873
|
if (accessExpr !== void 0 && accessExpr !== null) {
|
|
2524
|
-
const
|
|
2525
|
-
|
|
2874
|
+
const allowed = await resolveBooleanAccess(config, accessExpr, {
|
|
2875
|
+
user,
|
|
2876
|
+
req: toHookRequestContext(c.req)
|
|
2877
|
+
});
|
|
2526
2878
|
if (!allowed) {
|
|
2527
2879
|
return c.json({ error: true, message: `Access denied: read on global ${colSlug}` }, 403);
|
|
2528
2880
|
}
|
|
@@ -2566,7 +2918,7 @@ function registerRoutes(app, config) {
|
|
|
2566
2918
|
app.get("/api/docs", (c) => {
|
|
2567
2919
|
return c.html(getSwaggerHtml());
|
|
2568
2920
|
});
|
|
2569
|
-
app.get("/api/preferences/:key", requireAuth(), async (c) => {
|
|
2921
|
+
app.get("/api/preferences/:key", requireAuth(config), async (c) => {
|
|
2570
2922
|
const db = config.db;
|
|
2571
2923
|
const user = c.get("user");
|
|
2572
2924
|
const key = c.req.param("key");
|
|
@@ -2591,7 +2943,7 @@ function registerRoutes(app, config) {
|
|
|
2591
2943
|
const globalValue = await getGlobalPreference();
|
|
2592
2944
|
return c.json({ key, value: globalValue });
|
|
2593
2945
|
});
|
|
2594
|
-
app.put("/api/preferences/:key", requireAuth(), async (c) => {
|
|
2946
|
+
app.put("/api/preferences/:key", requireAuth(config), async (c) => {
|
|
2595
2947
|
const db = config.db;
|
|
2596
2948
|
const user = c.get("user");
|
|
2597
2949
|
const key = c.req.param("key");
|
|
@@ -2631,7 +2983,7 @@ function registerRoutes(app, config) {
|
|
|
2631
2983
|
});
|
|
2632
2984
|
return c.json({ key, value: body.value });
|
|
2633
2985
|
});
|
|
2634
|
-
app.delete("/api/preferences/:key", requireAuth(), async (c) => {
|
|
2986
|
+
app.delete("/api/preferences/:key", requireAuth(config), async (c) => {
|
|
2635
2987
|
const db = config.db;
|
|
2636
2988
|
const user = c.get("user");
|
|
2637
2989
|
const key = c.req.param("key");
|
|
@@ -2671,10 +3023,10 @@ function registerRoutes(app, config) {
|
|
|
2671
3023
|
for (const col of uploadCollections) {
|
|
2672
3024
|
const mediaController = new MediaController(col.slug);
|
|
2673
3025
|
const prefix = `/api/collections/${col.slug}`;
|
|
2674
|
-
app.get(`${prefix}/media`, accessGate(col, "read"), (c) => mediaController.find(c));
|
|
3026
|
+
app.get(`${prefix}/media`, accessGate(config, col, "read"), (c) => mediaController.find(c));
|
|
2675
3027
|
app.get(`${prefix}/media/:filename{.+$}`, (c) => mediaController.serve(c));
|
|
2676
|
-
app.post(`${prefix}/media`, accessGate(col, "create"), (c) => mediaController.upload(c));
|
|
2677
|
-
app.delete(`${prefix}/media/:id`, accessGate(col, "delete"), (c) => mediaController.delete(c));
|
|
3028
|
+
app.post(`${prefix}/media`, accessGate(config, col, "create"), (c) => mediaController.upload(c));
|
|
3029
|
+
app.delete(`${prefix}/media/:id`, accessGate(config, col, "delete"), (c) => mediaController.delete(c));
|
|
2678
3030
|
}
|
|
2679
3031
|
}
|
|
2680
3032
|
const adminAuthController = new AdminAuthController(config);
|
|
@@ -2691,43 +3043,43 @@ function registerRoutes(app, config) {
|
|
|
2691
3043
|
app.post(`${path}/logout`, (c) => authController.logout(c));
|
|
2692
3044
|
app.get(`${path}/init`, (c) => authController.init(c));
|
|
2693
3045
|
app.post(`${path}/first-user`, (c) => authController.registerFirstUser(c));
|
|
2694
|
-
app.get(`${path}/me`, requireAuth(), (c) => authController.me(c));
|
|
2695
|
-
app.post(`${path}/refresh-token`, requireAuth(), (c) => authController.refreshToken(c));
|
|
3046
|
+
app.get(`${path}/me`, requireAuth(config), (c) => authController.me(c));
|
|
3047
|
+
app.post(`${path}/refresh-token`, requireAuth(config), (c) => authController.refreshToken(c));
|
|
2696
3048
|
app.post(`${path}/forgot-password`, (c) => authController.forgotPassword(c));
|
|
2697
3049
|
app.post(`${path}/reset-password`, (c) => authController.resetPassword(c));
|
|
2698
|
-
app.post(`${path}/invite`, requireAuth(), (c) => authController.invite(c));
|
|
3050
|
+
app.post(`${path}/invite`, requireAuth(config), (c) => authController.invite(c));
|
|
2699
3051
|
app.post(`${path}/accept-invite`, (c) => authController.acceptInvite(c));
|
|
2700
3052
|
}
|
|
2701
3053
|
for (const collection of config.collections) {
|
|
2702
3054
|
const path = `/api/collections/${collection.slug}`;
|
|
2703
3055
|
const controller = new CollectionController(collection);
|
|
2704
|
-
app.get(path,
|
|
2705
|
-
app.post(path,
|
|
2706
|
-
app.post(`${path}/media`,
|
|
2707
|
-
app.delete(`${path}/delete-many`,
|
|
2708
|
-
app.get(`${path}/:id`,
|
|
2709
|
-
app.patch(`${path}/:id`,
|
|
2710
|
-
app.delete(`${path}/:id`,
|
|
3056
|
+
app.get(path, (c) => controller.find(c));
|
|
3057
|
+
app.post(path, (c) => controller.create(c));
|
|
3058
|
+
app.post(`${path}/media`, (c) => controller.create(c));
|
|
3059
|
+
app.delete(`${path}/delete-many`, (c) => controller.deleteMany(c));
|
|
3060
|
+
app.get(`${path}/:id`, (c) => controller.findOne(c));
|
|
3061
|
+
app.patch(`${path}/:id`, (c) => controller.update(c));
|
|
3062
|
+
app.delete(`${path}/:id`, (c) => controller.delete(c));
|
|
2711
3063
|
app.post(`${path}/seed`, (c) => controller.seed(c));
|
|
2712
3064
|
if (collection.auth) {
|
|
2713
|
-
app.post(`${path}/:id/change-password`, requireAuth(), (c) => controller.changePassword(c));
|
|
3065
|
+
app.post(`${path}/:id/change-password`, requireAuth(config), (c) => controller.changePassword(c));
|
|
2714
3066
|
}
|
|
2715
3067
|
if (collection.workflow) {
|
|
2716
|
-
app.post(`${path}/:id/transitions/:transition`, requireAuth(), (c) => controller.transition(c));
|
|
2717
|
-
app.get(`${path}/:id/workflow-history`, requireAuth(), (c) => controller.workflowHistory(c));
|
|
3068
|
+
app.post(`${path}/:id/transitions/:transition`, requireAuth(config), (c) => controller.transition(c));
|
|
3069
|
+
app.get(`${path}/:id/workflow-history`, requireAuth(config), (c) => controller.workflowHistory(c));
|
|
2718
3070
|
}
|
|
2719
3071
|
}
|
|
2720
3072
|
for (const global of config.globals) {
|
|
2721
3073
|
const path = `/api/globals/${global.slug}`;
|
|
2722
3074
|
const controller = new GlobalController(global);
|
|
2723
|
-
app.get(path,
|
|
2724
|
-
app.patch(path,
|
|
3075
|
+
app.get(path, (c) => controller.get(c));
|
|
3076
|
+
app.patch(path, (c) => controller.update(c));
|
|
2725
3077
|
app.post(`${path}/seed`, (c) => controller.seed(c));
|
|
2726
3078
|
}
|
|
2727
3079
|
const previewController = new PreviewController();
|
|
2728
|
-
app.post("/api/preview-token", requireAuth(), (c) => previewController.createToken(c));
|
|
3080
|
+
app.post("/api/preview-token", requireAuth(config), (c) => previewController.createToken(c));
|
|
2729
3081
|
app.get("/api/preview-data", (c) => previewController.getData(c));
|
|
2730
|
-
app.post("/api/collections/:slug/:id/transitions/:transition", requireAuth(), async (c) => {
|
|
3082
|
+
app.post("/api/collections/:slug/:id/transitions/:transition", requireAuth(config), async (c) => {
|
|
2731
3083
|
const slug = c.req.param("slug");
|
|
2732
3084
|
const siteId = c.req.header("X-Site-Id") || c.get("siteId");
|
|
2733
3085
|
const config2 = c.get("config");
|
|
@@ -2745,7 +3097,7 @@ function registerRoutes(app, config) {
|
|
|
2745
3097
|
const controller = new CollectionController(collection);
|
|
2746
3098
|
return controller.transition(c);
|
|
2747
3099
|
});
|
|
2748
|
-
app.get("/api/collections/:slug/:id/workflow-history", requireAuth(), async (c) => {
|
|
3100
|
+
app.get("/api/collections/:slug/:id/workflow-history", requireAuth(config), async (c) => {
|
|
2749
3101
|
const slug = c.req.param("slug");
|
|
2750
3102
|
const siteId = c.req.header("X-Site-Id") || c.get("siteId");
|
|
2751
3103
|
const config2 = c.get("config");
|
|
@@ -2844,7 +3196,7 @@ async function createDyrectedApp(rawConfig) {
|
|
|
2844
3196
|
await config.db.sync(config.collections, config.globals);
|
|
2845
3197
|
}
|
|
2846
3198
|
app.use("*", requestId());
|
|
2847
|
-
app.use("*", optionalAuth());
|
|
3199
|
+
app.use("*", optionalAuth(config));
|
|
2848
3200
|
app.use("*", async (c, next) => {
|
|
2849
3201
|
const start = Date.now();
|
|
2850
3202
|
await next();
|