@dyrected/core 2.5.56 → 2.5.58
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-oU1EGHND.d.cts +1538 -0
- package/dist/app-config-oU1EGHND.d.ts +1538 -0
- package/dist/index.cjs +79 -0
- package/dist/index.d.cts +129 -7
- package/dist/index.d.ts +129 -7
- package/dist/index.js +55 -0
- package/dist/server.cjs +428 -84
- package/dist/server.d.cts +26 -5
- package/dist/server.d.ts +26 -5
- package/dist/server.js +428 -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,173 @@ 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
|
+
async function applyFieldReadAccess(context, doc) {
|
|
429
|
+
if (!doc || typeof doc !== "object") return doc;
|
|
430
|
+
const result = { ...doc };
|
|
431
|
+
for (const field of context.fields) {
|
|
432
|
+
if (field.type === "row" && field.fields) {
|
|
433
|
+
Object.assign(result, await applyFieldReadAccess({ ...context, fields: field.fields }, result));
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
if (!field.name) continue;
|
|
437
|
+
const value = result[field.name];
|
|
438
|
+
const canRead = await resolveBooleanAccess(context.config, field.access?.read, {
|
|
439
|
+
user: context.user,
|
|
440
|
+
req: context.req,
|
|
441
|
+
doc: context.doc,
|
|
442
|
+
data: context.data
|
|
443
|
+
});
|
|
444
|
+
if (!canRead) {
|
|
445
|
+
delete result[field.name];
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
if (value === void 0 || value === null) continue;
|
|
449
|
+
if (field.type === "object" && field.fields && typeof value === "object" && !Array.isArray(value)) {
|
|
450
|
+
result[field.name] = await applyFieldReadAccess({ ...context, fields: field.fields }, value);
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
if (field.type === "array" && field.fields && Array.isArray(value)) {
|
|
454
|
+
result[field.name] = await Promise.all(
|
|
455
|
+
value.map(
|
|
456
|
+
(item) => typeof item === "object" && item !== null ? applyFieldReadAccess({ ...context, fields: field.fields }, item) : item
|
|
457
|
+
)
|
|
458
|
+
);
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
if (field.type === "blocks" && field.blocks && Array.isArray(value)) {
|
|
462
|
+
result[field.name] = await Promise.all(
|
|
463
|
+
value.map(async (item) => {
|
|
464
|
+
if (typeof item !== "object" || item === null) return item;
|
|
465
|
+
const typedItem = item;
|
|
466
|
+
const block = field.blocks?.find((candidate) => candidate.slug === typedItem.blockType);
|
|
467
|
+
if (!block) return item;
|
|
468
|
+
return applyFieldReadAccess({ ...context, fields: block.fields }, typedItem);
|
|
469
|
+
})
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return result;
|
|
474
|
+
}
|
|
475
|
+
async function applyFieldWriteAccess(context, data) {
|
|
476
|
+
if (!data || typeof data !== "object") return data;
|
|
477
|
+
const result = { ...data };
|
|
478
|
+
for (const field of context.fields) {
|
|
479
|
+
if (field.type === "row" && field.fields) {
|
|
480
|
+
Object.assign(result, await applyFieldWriteAccess({ ...context, fields: field.fields }, result));
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
if (!field.name || !(field.name in result)) continue;
|
|
484
|
+
const canUpdate = await resolveBooleanAccess(context.config, field.access?.update, {
|
|
485
|
+
user: context.user,
|
|
486
|
+
req: context.req,
|
|
487
|
+
doc: context.doc,
|
|
488
|
+
data: context.data
|
|
489
|
+
});
|
|
490
|
+
if (!canUpdate) {
|
|
491
|
+
delete result[field.name];
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
const value = result[field.name];
|
|
495
|
+
if (value === void 0 || value === null) continue;
|
|
496
|
+
if (field.type === "object" && field.fields && typeof value === "object" && !Array.isArray(value)) {
|
|
497
|
+
result[field.name] = await applyFieldWriteAccess({ ...context, fields: field.fields }, value);
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
if (field.type === "array" && field.fields && Array.isArray(value)) {
|
|
501
|
+
result[field.name] = await Promise.all(
|
|
502
|
+
value.map(
|
|
503
|
+
(item) => typeof item === "object" && item !== null ? applyFieldWriteAccess({ ...context, fields: field.fields }, item) : item
|
|
504
|
+
)
|
|
505
|
+
);
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
if (field.type === "blocks" && field.blocks && Array.isArray(value)) {
|
|
509
|
+
result[field.name] = await Promise.all(
|
|
510
|
+
value.map(async (item) => {
|
|
511
|
+
if (typeof item !== "object" || item === null) return item;
|
|
512
|
+
const typedItem = item;
|
|
513
|
+
const block = field.blocks?.find((candidate) => candidate.slug === typedItem.blockType);
|
|
514
|
+
if (!block) return item;
|
|
515
|
+
return applyFieldWriteAccess({ ...context, fields: block.fields }, typedItem);
|
|
516
|
+
})
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return result;
|
|
521
|
+
}
|
|
522
|
+
function mergeWhereConstraint(where, constraint) {
|
|
523
|
+
return where ? { AND: [where, constraint] } : constraint;
|
|
524
|
+
}
|
|
525
|
+
|
|
356
526
|
// src/controllers/collection.controller.ts
|
|
357
527
|
var CollectionController = class {
|
|
358
528
|
collection;
|
|
@@ -368,11 +538,23 @@ var CollectionController = class {
|
|
|
368
538
|
return config.adminAuth?.providers?.find((p) => p.members) || null;
|
|
369
539
|
}
|
|
370
540
|
toHookRequestContext(c) {
|
|
371
|
-
return
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
541
|
+
return toHookRequestContext(c.req);
|
|
542
|
+
}
|
|
543
|
+
async evaluateAccess(c, action, options = {}) {
|
|
544
|
+
const config = c.get("config");
|
|
545
|
+
return resolveCollectionAccess(
|
|
546
|
+
config,
|
|
547
|
+
this.collection.slug,
|
|
548
|
+
action,
|
|
549
|
+
this.collection.access?.[action],
|
|
550
|
+
{
|
|
551
|
+
id: options.id,
|
|
552
|
+
user: c.get("user"),
|
|
553
|
+
req: this.toHookRequestContext(c),
|
|
554
|
+
doc: options.doc ?? void 0,
|
|
555
|
+
data: options.data
|
|
556
|
+
}
|
|
557
|
+
);
|
|
376
558
|
}
|
|
377
559
|
async find(c) {
|
|
378
560
|
const config = c.get("config");
|
|
@@ -452,6 +634,13 @@ var CollectionController = class {
|
|
|
452
634
|
if (this.collection.workflow && !canViewWorkflowDraft(this.collection.workflow, user)) {
|
|
453
635
|
where = where ? { AND: [where, { __published: { exists: true } }] } : { __published: { exists: true } };
|
|
454
636
|
}
|
|
637
|
+
const access = await this.evaluateAccess(c, "read");
|
|
638
|
+
if (!access.allowed) {
|
|
639
|
+
return c.json({ error: true, message: `Access denied: read on ${this.collection.slug}` }, 403);
|
|
640
|
+
}
|
|
641
|
+
if (access.constraint) {
|
|
642
|
+
where = mergeWhereConstraint(where, access.constraint);
|
|
643
|
+
}
|
|
455
644
|
let result = await db.find({
|
|
456
645
|
collection: this.collection.slug,
|
|
457
646
|
limit,
|
|
@@ -484,7 +673,14 @@ var CollectionController = class {
|
|
|
484
673
|
db: readonlyDb
|
|
485
674
|
});
|
|
486
675
|
const docWithFieldHooks = await executeFieldAfterRead(this.collection.fields, docWithCollectionHooks, user, readonlyDb);
|
|
487
|
-
|
|
676
|
+
const docWithFieldAccess = await applyFieldReadAccess({
|
|
677
|
+
config,
|
|
678
|
+
fields: this.collection.fields,
|
|
679
|
+
user,
|
|
680
|
+
req: this.toHookRequestContext(c),
|
|
681
|
+
doc: docWithFieldHooks
|
|
682
|
+
}, docWithFieldHooks);
|
|
683
|
+
processedDocs.push(docWithFieldAccess);
|
|
488
684
|
}
|
|
489
685
|
result.docs = processedDocs;
|
|
490
686
|
if (depth > 0) {
|
|
@@ -529,6 +725,10 @@ var CollectionController = class {
|
|
|
529
725
|
doc = materializeWorkflowDocument(doc, this.collection.workflow, user);
|
|
530
726
|
if (!doc) return c.json({ message: "Not Found" }, 404);
|
|
531
727
|
}
|
|
728
|
+
const access = await this.evaluateAccess(c, "read", { id, doc });
|
|
729
|
+
if (!access.allowed) {
|
|
730
|
+
return c.json({ error: true, message: `Access denied: read on ${this.collection.slug}` }, 403);
|
|
731
|
+
}
|
|
532
732
|
const docWithDefaults = DefaultsService.apply(this.collection.fields, doc);
|
|
533
733
|
const docWithCollectionHooks = await runCollectionHooks(this.collection.hooks?.afterRead, {
|
|
534
734
|
doc: docWithDefaults,
|
|
@@ -537,17 +737,24 @@ var CollectionController = class {
|
|
|
537
737
|
db: readonlyDb
|
|
538
738
|
});
|
|
539
739
|
const docWithFieldHooks = await executeFieldAfterRead(this.collection.fields, docWithCollectionHooks, user, readonlyDb);
|
|
540
|
-
|
|
740
|
+
const docWithFieldAccess = await applyFieldReadAccess({
|
|
741
|
+
config,
|
|
742
|
+
fields: this.collection.fields,
|
|
743
|
+
user,
|
|
744
|
+
req: this.toHookRequestContext(c),
|
|
745
|
+
doc: docWithFieldHooks
|
|
746
|
+
}, docWithFieldHooks);
|
|
747
|
+
if (depth > 0 && docWithFieldAccess) {
|
|
541
748
|
const populationService = new PopulationService(db, config.collections);
|
|
542
749
|
const populatedDoc = await populationService.populate({
|
|
543
|
-
data:
|
|
750
|
+
data: docWithFieldAccess,
|
|
544
751
|
fields: this.collection.fields,
|
|
545
752
|
currentDepth: 0,
|
|
546
753
|
maxDepth: depth
|
|
547
754
|
});
|
|
548
755
|
return c.json(populatedDoc);
|
|
549
756
|
}
|
|
550
|
-
return c.json(
|
|
757
|
+
return c.json(docWithFieldAccess);
|
|
551
758
|
}
|
|
552
759
|
async create(c) {
|
|
553
760
|
const config = c.get("config");
|
|
@@ -586,9 +793,20 @@ var CollectionController = class {
|
|
|
586
793
|
if (this.collection.workflow) {
|
|
587
794
|
data = initializeWorkflowDocument(data, this.collection.workflow);
|
|
588
795
|
}
|
|
796
|
+
const createAccess = await this.evaluateAccess(c, "create", { data });
|
|
797
|
+
if (!createAccess.allowed) {
|
|
798
|
+
return c.json({ error: true, message: `Access denied: create on ${this.collection.slug}` }, 403);
|
|
799
|
+
}
|
|
589
800
|
if (this.collection.auth && data.password) {
|
|
590
801
|
data.password = await hashPassword(data.password);
|
|
591
802
|
}
|
|
803
|
+
data = await applyFieldWriteAccess({
|
|
804
|
+
config,
|
|
805
|
+
fields: this.collection.fields,
|
|
806
|
+
user,
|
|
807
|
+
req: this.toHookRequestContext(c),
|
|
808
|
+
data
|
|
809
|
+
}, data);
|
|
592
810
|
data = await executeFieldBeforeChange(this.collection.fields, data, null, user, readonlyDb);
|
|
593
811
|
data = await runCollectionHooks(this.collection.hooks?.beforeChange, {
|
|
594
812
|
data,
|
|
@@ -623,7 +841,14 @@ var CollectionController = class {
|
|
|
623
841
|
db: readonlyDb
|
|
624
842
|
});
|
|
625
843
|
const finalDoc = await executeFieldAfterRead(this.collection.fields, readDoc, user, readonlyDb);
|
|
626
|
-
|
|
844
|
+
const accessibleDoc = await applyFieldReadAccess({
|
|
845
|
+
config,
|
|
846
|
+
fields: this.collection.fields,
|
|
847
|
+
user,
|
|
848
|
+
req: this.toHookRequestContext(c),
|
|
849
|
+
doc: finalDoc
|
|
850
|
+
}, finalDoc);
|
|
851
|
+
return c.json(accessibleDoc, 201);
|
|
627
852
|
}
|
|
628
853
|
async upload(c) {
|
|
629
854
|
const config = c.get("config");
|
|
@@ -666,6 +891,17 @@ var CollectionController = class {
|
|
|
666
891
|
createdBy: user?.sub ?? null,
|
|
667
892
|
updatedBy: user?.sub ?? null
|
|
668
893
|
};
|
|
894
|
+
const createAccess = await this.evaluateAccess(c, "create", { data });
|
|
895
|
+
if (!createAccess.allowed) {
|
|
896
|
+
return c.json({ error: true, message: `Access denied: create on ${this.collection.slug}` }, 403);
|
|
897
|
+
}
|
|
898
|
+
data = await applyFieldWriteAccess({
|
|
899
|
+
config,
|
|
900
|
+
fields: this.collection.fields,
|
|
901
|
+
user,
|
|
902
|
+
req: this.toHookRequestContext(c),
|
|
903
|
+
data
|
|
904
|
+
}, data);
|
|
669
905
|
data = await executeFieldBeforeChange(this.collection.fields, data, null, user, readonlyDb);
|
|
670
906
|
data = await runCollectionHooks(this.collection.hooks?.beforeChange, {
|
|
671
907
|
data,
|
|
@@ -693,7 +929,14 @@ var CollectionController = class {
|
|
|
693
929
|
db: readonlyDb
|
|
694
930
|
});
|
|
695
931
|
const finalDoc = await executeFieldAfterRead(this.collection.fields, readDoc, user, readonlyDb);
|
|
696
|
-
|
|
932
|
+
const accessibleDoc = await applyFieldReadAccess({
|
|
933
|
+
config,
|
|
934
|
+
fields: this.collection.fields,
|
|
935
|
+
user,
|
|
936
|
+
req: this.toHookRequestContext(c),
|
|
937
|
+
doc: finalDoc
|
|
938
|
+
}, finalDoc);
|
|
939
|
+
return c.json(accessibleDoc, 201);
|
|
697
940
|
}
|
|
698
941
|
async update(c) {
|
|
699
942
|
const config = c.get("config");
|
|
@@ -731,10 +974,22 @@ var CollectionController = class {
|
|
|
731
974
|
});
|
|
732
975
|
const originalDoc = await db.findOne({ collection: this.collection.slug, id });
|
|
733
976
|
if (!originalDoc) return c.json({ message: "Not Found" }, 404);
|
|
977
|
+
const updateAccess = await this.evaluateAccess(c, "update", { id, doc: originalDoc, data });
|
|
978
|
+
if (!updateAccess.allowed) {
|
|
979
|
+
return c.json({ error: true, message: `Access denied: update on ${this.collection.slug}` }, 403);
|
|
980
|
+
}
|
|
734
981
|
let before = null;
|
|
735
982
|
if (this.collection.audit) {
|
|
736
983
|
before = originalDoc;
|
|
737
984
|
}
|
|
985
|
+
data = await applyFieldWriteAccess({
|
|
986
|
+
config,
|
|
987
|
+
fields: this.collection.fields,
|
|
988
|
+
user,
|
|
989
|
+
req: this.toHookRequestContext(c),
|
|
990
|
+
doc: originalDoc,
|
|
991
|
+
data
|
|
992
|
+
}, data);
|
|
738
993
|
data = await executeFieldBeforeChange(this.collection.fields, data, originalDoc, user, readonlyDb);
|
|
739
994
|
data = await runCollectionHooks(this.collection.hooks?.beforeChange, {
|
|
740
995
|
data,
|
|
@@ -770,7 +1025,14 @@ var CollectionController = class {
|
|
|
770
1025
|
db: readonlyDb
|
|
771
1026
|
});
|
|
772
1027
|
const finalDoc = await executeFieldAfterRead(this.collection.fields, readDoc, user, readonlyDb);
|
|
773
|
-
|
|
1028
|
+
const accessibleDoc = await applyFieldReadAccess({
|
|
1029
|
+
config,
|
|
1030
|
+
fields: this.collection.fields,
|
|
1031
|
+
user,
|
|
1032
|
+
req: this.toHookRequestContext(c),
|
|
1033
|
+
doc: finalDoc
|
|
1034
|
+
}, finalDoc);
|
|
1035
|
+
return c.json(accessibleDoc);
|
|
774
1036
|
}
|
|
775
1037
|
async transition(c) {
|
|
776
1038
|
const config = c.get("config");
|
|
@@ -807,7 +1069,7 @@ var CollectionController = class {
|
|
|
807
1069
|
const readAccess = this.collection.access?.read;
|
|
808
1070
|
if (readAccess !== void 0 && readAccess !== null) {
|
|
809
1071
|
const args = { user: c.get("user"), req: c.req, doc: document };
|
|
810
|
-
const result2 =
|
|
1072
|
+
const result2 = await resolveAccess(config, readAccess, args);
|
|
811
1073
|
let allowed = result2 === true;
|
|
812
1074
|
if (result2 && typeof result2 === "object") {
|
|
813
1075
|
const match = await config.db.find({
|
|
@@ -918,6 +1180,10 @@ var CollectionController = class {
|
|
|
918
1180
|
const user = c.get("user");
|
|
919
1181
|
const doc = await db.findOne({ collection: this.collection.slug, id });
|
|
920
1182
|
if (!doc) return c.json({ message: "Not Found" }, 404);
|
|
1183
|
+
const deleteAccess = await this.evaluateAccess(c, "delete", { id, doc });
|
|
1184
|
+
if (!deleteAccess.allowed) {
|
|
1185
|
+
return c.json({ error: true, message: `Access denied: delete on ${this.collection.slug}` }, 403);
|
|
1186
|
+
}
|
|
921
1187
|
let before = null;
|
|
922
1188
|
if (this.collection.audit) {
|
|
923
1189
|
before = doc;
|
|
@@ -977,6 +1243,11 @@ var CollectionController = class {
|
|
|
977
1243
|
failed.push({ id, error: "Not Found" });
|
|
978
1244
|
continue;
|
|
979
1245
|
}
|
|
1246
|
+
const deleteAccess = await this.evaluateAccess(c, "delete", { id, doc });
|
|
1247
|
+
if (!deleteAccess.allowed) {
|
|
1248
|
+
failed.push({ id, error: "Access denied" });
|
|
1249
|
+
continue;
|
|
1250
|
+
}
|
|
980
1251
|
let before = null;
|
|
981
1252
|
if (this.collection.audit) {
|
|
982
1253
|
before = doc;
|
|
@@ -1070,6 +1341,14 @@ var GlobalController = class {
|
|
|
1070
1341
|
await db.updateGlobal({ slug: this.global.slug, data: this.global.initialData });
|
|
1071
1342
|
data = this.global.initialData;
|
|
1072
1343
|
}
|
|
1344
|
+
const canRead = await resolveBooleanAccess(config, this.global.access?.read, {
|
|
1345
|
+
user,
|
|
1346
|
+
req: toHookRequestContext(c.req),
|
|
1347
|
+
doc: data
|
|
1348
|
+
});
|
|
1349
|
+
if (!canRead) {
|
|
1350
|
+
return c.json({ error: true, message: `Access denied: read on ${this.global.slug}` }, 403);
|
|
1351
|
+
}
|
|
1073
1352
|
const dataWithDefaults = DefaultsService.apply(this.global.fields, data);
|
|
1074
1353
|
const docWithCollectionHooks = await runCollectionHooks(this.global.hooks?.afterRead, {
|
|
1075
1354
|
doc: dataWithDefaults,
|
|
@@ -1078,17 +1357,24 @@ var GlobalController = class {
|
|
|
1078
1357
|
db: readonlyDb
|
|
1079
1358
|
});
|
|
1080
1359
|
const docWithFieldHooks = await executeFieldAfterRead(this.global.fields, docWithCollectionHooks, user, readonlyDb);
|
|
1081
|
-
|
|
1360
|
+
const docWithFieldAccess = await applyFieldReadAccess({
|
|
1361
|
+
config,
|
|
1362
|
+
fields: this.global.fields,
|
|
1363
|
+
user,
|
|
1364
|
+
req: toHookRequestContext(c.req),
|
|
1365
|
+
doc: docWithFieldHooks
|
|
1366
|
+
}, docWithFieldHooks);
|
|
1367
|
+
if (depth > 0 && docWithFieldAccess) {
|
|
1082
1368
|
const populationService = new PopulationService(db, config.collections);
|
|
1083
1369
|
const populatedData = await populationService.populate({
|
|
1084
|
-
data:
|
|
1370
|
+
data: docWithFieldAccess,
|
|
1085
1371
|
fields: this.global.fields,
|
|
1086
1372
|
currentDepth: 0,
|
|
1087
1373
|
maxDepth: depth
|
|
1088
1374
|
});
|
|
1089
1375
|
return c.json(populatedData);
|
|
1090
1376
|
}
|
|
1091
|
-
return c.json(
|
|
1377
|
+
return c.json(docWithFieldAccess);
|
|
1092
1378
|
}
|
|
1093
1379
|
async update(c) {
|
|
1094
1380
|
const config = c.get("config");
|
|
@@ -1098,7 +1384,24 @@ var GlobalController = class {
|
|
|
1098
1384
|
const body = await c.req.json();
|
|
1099
1385
|
const user = c.get("user");
|
|
1100
1386
|
const originalDoc = await db.getGlobal({ slug: this.global.slug }) || {};
|
|
1101
|
-
|
|
1387
|
+
const canUpdate = await resolveBooleanAccess(config, this.global.access?.update, {
|
|
1388
|
+
user,
|
|
1389
|
+
req: toHookRequestContext(c.req),
|
|
1390
|
+
doc: originalDoc,
|
|
1391
|
+
data: body
|
|
1392
|
+
});
|
|
1393
|
+
if (!canUpdate) {
|
|
1394
|
+
return c.json({ error: true, message: `Access denied: update on ${this.global.slug}` }, 403);
|
|
1395
|
+
}
|
|
1396
|
+
let sanitizedBody = await applyFieldWriteAccess({
|
|
1397
|
+
config,
|
|
1398
|
+
fields: this.global.fields,
|
|
1399
|
+
user,
|
|
1400
|
+
req: toHookRequestContext(c.req),
|
|
1401
|
+
doc: originalDoc,
|
|
1402
|
+
data: body
|
|
1403
|
+
}, body);
|
|
1404
|
+
let data = await executeFieldBeforeChange(this.global.fields, sanitizedBody, originalDoc, user, readonlyDb);
|
|
1102
1405
|
data = await runCollectionHooks(this.global.hooks?.beforeChange, {
|
|
1103
1406
|
data,
|
|
1104
1407
|
doc: originalDoc,
|
|
@@ -1123,7 +1426,14 @@ var GlobalController = class {
|
|
|
1123
1426
|
db: readonlyDb
|
|
1124
1427
|
});
|
|
1125
1428
|
const finalDoc = await executeFieldAfterRead(this.global.fields, readDoc, user, readonlyDb);
|
|
1126
|
-
|
|
1429
|
+
const accessibleDoc = await applyFieldReadAccess({
|
|
1430
|
+
config,
|
|
1431
|
+
fields: this.global.fields,
|
|
1432
|
+
user,
|
|
1433
|
+
req: toHookRequestContext(c.req),
|
|
1434
|
+
doc: finalDoc
|
|
1435
|
+
}, finalDoc);
|
|
1436
|
+
return c.json(accessibleDoc);
|
|
1127
1437
|
}
|
|
1128
1438
|
async seed(c) {
|
|
1129
1439
|
const config = c.get("config");
|
|
@@ -1305,7 +1615,7 @@ var MediaController = class {
|
|
|
1305
1615
|
import { SignJWT, jwtVerify, decodeJwt } from "jose";
|
|
1306
1616
|
import { TextEncoder } from "util";
|
|
1307
1617
|
function getSecret() {
|
|
1308
|
-
const secret = process.env.DYRECTED_JWT_SECRET
|
|
1618
|
+
const secret = process.env.DYRECTED_JWT_SECRET;
|
|
1309
1619
|
if (!secret) {
|
|
1310
1620
|
throw new Error(
|
|
1311
1621
|
"[dyrected/core] DYRECTED_JWT_SECRET is not set. Add it to your environment variables to enable auth collections."
|
|
@@ -2180,7 +2490,7 @@ var AdminAuthController = class {
|
|
|
2180
2490
|
return state;
|
|
2181
2491
|
}
|
|
2182
2492
|
getStateSecret() {
|
|
2183
|
-
const secret = process.env.DYRECTED_JWT_SECRET
|
|
2493
|
+
const secret = process.env.DYRECTED_JWT_SECRET;
|
|
2184
2494
|
if (!secret) {
|
|
2185
2495
|
throw new Error("[dyrected/core] DYRECTED_JWT_SECRET is not set.");
|
|
2186
2496
|
}
|
|
@@ -2198,7 +2508,7 @@ import { SignJWT as SignJWT3, jwtVerify as jwtVerify3 } from "jose";
|
|
|
2198
2508
|
import { TextEncoder as TextEncoder3 } from "util";
|
|
2199
2509
|
var PreviewController = class {
|
|
2200
2510
|
getSecret() {
|
|
2201
|
-
const secret = process.env.DYRECTED_JWT_SECRET ||
|
|
2511
|
+
const secret = process.env.DYRECTED_JWT_SECRET || "dyrected-preview-secret-change-me";
|
|
2202
2512
|
return new TextEncoder3().encode(secret);
|
|
2203
2513
|
}
|
|
2204
2514
|
/**
|
|
@@ -2237,30 +2547,74 @@ var PreviewController = class {
|
|
|
2237
2547
|
};
|
|
2238
2548
|
|
|
2239
2549
|
// src/middleware/auth.ts
|
|
2240
|
-
function
|
|
2550
|
+
function getBearerToken(c) {
|
|
2551
|
+
const authHeader = c.req.header("Authorization");
|
|
2552
|
+
return authHeader?.replace(/^Bearer\s+/i, "") || void 0;
|
|
2553
|
+
}
|
|
2554
|
+
async function resolveUser(token, config) {
|
|
2555
|
+
const payload = await verifyCollectionToken(token);
|
|
2556
|
+
if (payload.purpose) {
|
|
2557
|
+
return payload;
|
|
2558
|
+
}
|
|
2559
|
+
const db = config?.db;
|
|
2560
|
+
if (!db) {
|
|
2561
|
+
return payload;
|
|
2562
|
+
}
|
|
2563
|
+
let doc;
|
|
2564
|
+
try {
|
|
2565
|
+
doc = await db.findOne({
|
|
2566
|
+
collection: payload.collection,
|
|
2567
|
+
id: payload.sub
|
|
2568
|
+
});
|
|
2569
|
+
} catch (err) {
|
|
2570
|
+
console.error("[dyrected/core] Failed to hydrate user from token:", err);
|
|
2571
|
+
return payload;
|
|
2572
|
+
}
|
|
2573
|
+
if (!doc) {
|
|
2574
|
+
return null;
|
|
2575
|
+
}
|
|
2576
|
+
const { password: _password, ...safe } = doc;
|
|
2577
|
+
return {
|
|
2578
|
+
...safe,
|
|
2579
|
+
sub: payload.sub,
|
|
2580
|
+
email: payload.email ?? safe.email,
|
|
2581
|
+
collection: payload.collection
|
|
2582
|
+
};
|
|
2583
|
+
}
|
|
2584
|
+
function requireAuth(config) {
|
|
2241
2585
|
return async (c, next) => {
|
|
2242
|
-
|
|
2243
|
-
|
|
2586
|
+
if (c.get("user")) {
|
|
2587
|
+
return next();
|
|
2588
|
+
}
|
|
2589
|
+
const token = getBearerToken(c);
|
|
2244
2590
|
if (!token) {
|
|
2245
2591
|
return c.json({ error: true, message: "Authentication required." }, 401);
|
|
2246
2592
|
}
|
|
2593
|
+
let user;
|
|
2247
2594
|
try {
|
|
2248
|
-
|
|
2249
|
-
c.set("user", user);
|
|
2250
|
-
await next();
|
|
2595
|
+
user = await resolveUser(token, config ?? c.get("config"));
|
|
2251
2596
|
} catch {
|
|
2252
2597
|
return c.json({ error: true, message: "Invalid or expired token." }, 401);
|
|
2253
2598
|
}
|
|
2599
|
+
if (!user) {
|
|
2600
|
+
return c.json({ error: true, message: "Invalid or expired token." }, 401);
|
|
2601
|
+
}
|
|
2602
|
+
c.set("user", user);
|
|
2603
|
+
await next();
|
|
2254
2604
|
};
|
|
2255
2605
|
}
|
|
2256
|
-
function optionalAuth() {
|
|
2606
|
+
function optionalAuth(config) {
|
|
2257
2607
|
return async (c, next) => {
|
|
2258
|
-
|
|
2259
|
-
|
|
2608
|
+
if (c.get("user")) {
|
|
2609
|
+
return next();
|
|
2610
|
+
}
|
|
2611
|
+
const token = getBearerToken(c);
|
|
2260
2612
|
if (token) {
|
|
2261
2613
|
try {
|
|
2262
|
-
const user = await
|
|
2263
|
-
|
|
2614
|
+
const user = await resolveUser(token, config ?? c.get("config"));
|
|
2615
|
+
if (user) {
|
|
2616
|
+
c.set("user", user);
|
|
2617
|
+
}
|
|
2264
2618
|
} catch {
|
|
2265
2619
|
}
|
|
2266
2620
|
}
|
|
@@ -2323,37 +2677,23 @@ function getSwaggerHtml(specUrl) {
|
|
|
2323
2677
|
}
|
|
2324
2678
|
|
|
2325
2679
|
// src/router.ts
|
|
2326
|
-
function accessGate(target, action) {
|
|
2680
|
+
function accessGate(config, target, action) {
|
|
2327
2681
|
return async (c, next) => {
|
|
2328
2682
|
const user = c.get("user");
|
|
2329
2683
|
const accessExpr = target.access?.[action];
|
|
2330
2684
|
if (accessExpr === void 0 || accessExpr === null) {
|
|
2331
2685
|
return await next();
|
|
2332
2686
|
}
|
|
2333
|
-
const
|
|
2334
|
-
|
|
2687
|
+
const allowed = await resolveBooleanAccess(config, accessExpr, {
|
|
2688
|
+
user,
|
|
2689
|
+
req: toHookRequestContext(c.req)
|
|
2690
|
+
});
|
|
2335
2691
|
if (!allowed) {
|
|
2336
2692
|
return c.json({ error: true, message: `Access denied: ${action} on ${target.slug}` }, 403);
|
|
2337
2693
|
}
|
|
2338
2694
|
await next();
|
|
2339
2695
|
};
|
|
2340
2696
|
}
|
|
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
2697
|
function serializeFieldForApi(f) {
|
|
2358
2698
|
if (!f) return f;
|
|
2359
2699
|
const serialized = { ...f };
|
|
@@ -2382,7 +2722,7 @@ function serializeFieldForApi(f) {
|
|
|
2382
2722
|
return serialized;
|
|
2383
2723
|
}
|
|
2384
2724
|
function registerRoutes(app, config) {
|
|
2385
|
-
app.get("/api/schemas", optionalAuth(), async (c) => {
|
|
2725
|
+
app.get("/api/schemas", optionalAuth(config), async (c) => {
|
|
2386
2726
|
const siteId = c.req.header("X-Site-Id");
|
|
2387
2727
|
let collections = [...config.collections];
|
|
2388
2728
|
let globals = [...config.globals];
|
|
@@ -2398,11 +2738,11 @@ function registerRoutes(app, config) {
|
|
|
2398
2738
|
}
|
|
2399
2739
|
}
|
|
2400
2740
|
const user = c.get("user");
|
|
2401
|
-
const accessArgs = { user, req: c.req
|
|
2741
|
+
const accessArgs = { user, req: toHookRequestContext(c.req) };
|
|
2402
2742
|
const serializeAccess = async (access) => {
|
|
2403
2743
|
if (typeof access === "string") return access;
|
|
2404
2744
|
if (typeof access === "boolean") return access;
|
|
2405
|
-
return
|
|
2745
|
+
return resolveBooleanAccess(config, access, accessArgs);
|
|
2406
2746
|
};
|
|
2407
2747
|
const filteredCollections = await Promise.all(collections.filter((col) => !siteId || col.shared || !col.siteId || col.siteId === siteId).map(async (col) => ({
|
|
2408
2748
|
slug: col.slug,
|
|
@@ -2488,7 +2828,7 @@ function registerRoutes(app, config) {
|
|
|
2488
2828
|
adminAuth: getPublicAdminAuthConfig(config.adminAuth, collections)
|
|
2489
2829
|
});
|
|
2490
2830
|
});
|
|
2491
|
-
app.get("/api/dyrected/options/:collection/:field", optionalAuth(), async (c) => {
|
|
2831
|
+
app.get("/api/dyrected/options/:collection/:field", optionalAuth(config), async (c) => {
|
|
2492
2832
|
const { collection: colSlug, field: fieldName } = c.req.param();
|
|
2493
2833
|
const siteId = c.req.header("X-Site-Id");
|
|
2494
2834
|
let collections = [...config.collections];
|
|
@@ -2502,8 +2842,10 @@ function registerRoutes(app, config) {
|
|
|
2502
2842
|
if (collection) {
|
|
2503
2843
|
const accessExpr = collection.access?.read;
|
|
2504
2844
|
if (accessExpr !== void 0 && accessExpr !== null) {
|
|
2505
|
-
const
|
|
2506
|
-
|
|
2845
|
+
const allowed = await resolveBooleanAccess(config, accessExpr, {
|
|
2846
|
+
user,
|
|
2847
|
+
req: toHookRequestContext(c.req)
|
|
2848
|
+
});
|
|
2507
2849
|
if (!allowed) {
|
|
2508
2850
|
return c.json({ error: true, message: `Access denied: read on ${colSlug}` }, 403);
|
|
2509
2851
|
}
|
|
@@ -2521,8 +2863,10 @@ function registerRoutes(app, config) {
|
|
|
2521
2863
|
}
|
|
2522
2864
|
const accessExpr = glb.access?.read;
|
|
2523
2865
|
if (accessExpr !== void 0 && accessExpr !== null) {
|
|
2524
|
-
const
|
|
2525
|
-
|
|
2866
|
+
const allowed = await resolveBooleanAccess(config, accessExpr, {
|
|
2867
|
+
user,
|
|
2868
|
+
req: toHookRequestContext(c.req)
|
|
2869
|
+
});
|
|
2526
2870
|
if (!allowed) {
|
|
2527
2871
|
return c.json({ error: true, message: `Access denied: read on global ${colSlug}` }, 403);
|
|
2528
2872
|
}
|
|
@@ -2566,7 +2910,7 @@ function registerRoutes(app, config) {
|
|
|
2566
2910
|
app.get("/api/docs", (c) => {
|
|
2567
2911
|
return c.html(getSwaggerHtml());
|
|
2568
2912
|
});
|
|
2569
|
-
app.get("/api/preferences/:key", requireAuth(), async (c) => {
|
|
2913
|
+
app.get("/api/preferences/:key", requireAuth(config), async (c) => {
|
|
2570
2914
|
const db = config.db;
|
|
2571
2915
|
const user = c.get("user");
|
|
2572
2916
|
const key = c.req.param("key");
|
|
@@ -2591,7 +2935,7 @@ function registerRoutes(app, config) {
|
|
|
2591
2935
|
const globalValue = await getGlobalPreference();
|
|
2592
2936
|
return c.json({ key, value: globalValue });
|
|
2593
2937
|
});
|
|
2594
|
-
app.put("/api/preferences/:key", requireAuth(), async (c) => {
|
|
2938
|
+
app.put("/api/preferences/:key", requireAuth(config), async (c) => {
|
|
2595
2939
|
const db = config.db;
|
|
2596
2940
|
const user = c.get("user");
|
|
2597
2941
|
const key = c.req.param("key");
|
|
@@ -2631,7 +2975,7 @@ function registerRoutes(app, config) {
|
|
|
2631
2975
|
});
|
|
2632
2976
|
return c.json({ key, value: body.value });
|
|
2633
2977
|
});
|
|
2634
|
-
app.delete("/api/preferences/:key", requireAuth(), async (c) => {
|
|
2978
|
+
app.delete("/api/preferences/:key", requireAuth(config), async (c) => {
|
|
2635
2979
|
const db = config.db;
|
|
2636
2980
|
const user = c.get("user");
|
|
2637
2981
|
const key = c.req.param("key");
|
|
@@ -2671,10 +3015,10 @@ function registerRoutes(app, config) {
|
|
|
2671
3015
|
for (const col of uploadCollections) {
|
|
2672
3016
|
const mediaController = new MediaController(col.slug);
|
|
2673
3017
|
const prefix = `/api/collections/${col.slug}`;
|
|
2674
|
-
app.get(`${prefix}/media`, accessGate(col, "read"), (c) => mediaController.find(c));
|
|
3018
|
+
app.get(`${prefix}/media`, accessGate(config, col, "read"), (c) => mediaController.find(c));
|
|
2675
3019
|
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));
|
|
3020
|
+
app.post(`${prefix}/media`, accessGate(config, col, "create"), (c) => mediaController.upload(c));
|
|
3021
|
+
app.delete(`${prefix}/media/:id`, accessGate(config, col, "delete"), (c) => mediaController.delete(c));
|
|
2678
3022
|
}
|
|
2679
3023
|
}
|
|
2680
3024
|
const adminAuthController = new AdminAuthController(config);
|
|
@@ -2691,43 +3035,43 @@ function registerRoutes(app, config) {
|
|
|
2691
3035
|
app.post(`${path}/logout`, (c) => authController.logout(c));
|
|
2692
3036
|
app.get(`${path}/init`, (c) => authController.init(c));
|
|
2693
3037
|
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));
|
|
3038
|
+
app.get(`${path}/me`, requireAuth(config), (c) => authController.me(c));
|
|
3039
|
+
app.post(`${path}/refresh-token`, requireAuth(config), (c) => authController.refreshToken(c));
|
|
2696
3040
|
app.post(`${path}/forgot-password`, (c) => authController.forgotPassword(c));
|
|
2697
3041
|
app.post(`${path}/reset-password`, (c) => authController.resetPassword(c));
|
|
2698
|
-
app.post(`${path}/invite`, requireAuth(), (c) => authController.invite(c));
|
|
3042
|
+
app.post(`${path}/invite`, requireAuth(config), (c) => authController.invite(c));
|
|
2699
3043
|
app.post(`${path}/accept-invite`, (c) => authController.acceptInvite(c));
|
|
2700
3044
|
}
|
|
2701
3045
|
for (const collection of config.collections) {
|
|
2702
3046
|
const path = `/api/collections/${collection.slug}`;
|
|
2703
3047
|
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`,
|
|
3048
|
+
app.get(path, (c) => controller.find(c));
|
|
3049
|
+
app.post(path, (c) => controller.create(c));
|
|
3050
|
+
app.post(`${path}/media`, (c) => controller.create(c));
|
|
3051
|
+
app.delete(`${path}/delete-many`, (c) => controller.deleteMany(c));
|
|
3052
|
+
app.get(`${path}/:id`, (c) => controller.findOne(c));
|
|
3053
|
+
app.patch(`${path}/:id`, (c) => controller.update(c));
|
|
3054
|
+
app.delete(`${path}/:id`, (c) => controller.delete(c));
|
|
2711
3055
|
app.post(`${path}/seed`, (c) => controller.seed(c));
|
|
2712
3056
|
if (collection.auth) {
|
|
2713
|
-
app.post(`${path}/:id/change-password`, requireAuth(), (c) => controller.changePassword(c));
|
|
3057
|
+
app.post(`${path}/:id/change-password`, requireAuth(config), (c) => controller.changePassword(c));
|
|
2714
3058
|
}
|
|
2715
3059
|
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));
|
|
3060
|
+
app.post(`${path}/:id/transitions/:transition`, requireAuth(config), (c) => controller.transition(c));
|
|
3061
|
+
app.get(`${path}/:id/workflow-history`, requireAuth(config), (c) => controller.workflowHistory(c));
|
|
2718
3062
|
}
|
|
2719
3063
|
}
|
|
2720
3064
|
for (const global of config.globals) {
|
|
2721
3065
|
const path = `/api/globals/${global.slug}`;
|
|
2722
3066
|
const controller = new GlobalController(global);
|
|
2723
|
-
app.get(path,
|
|
2724
|
-
app.patch(path,
|
|
3067
|
+
app.get(path, (c) => controller.get(c));
|
|
3068
|
+
app.patch(path, (c) => controller.update(c));
|
|
2725
3069
|
app.post(`${path}/seed`, (c) => controller.seed(c));
|
|
2726
3070
|
}
|
|
2727
3071
|
const previewController = new PreviewController();
|
|
2728
|
-
app.post("/api/preview-token", requireAuth(), (c) => previewController.createToken(c));
|
|
3072
|
+
app.post("/api/preview-token", requireAuth(config), (c) => previewController.createToken(c));
|
|
2729
3073
|
app.get("/api/preview-data", (c) => previewController.getData(c));
|
|
2730
|
-
app.post("/api/collections/:slug/:id/transitions/:transition", requireAuth(), async (c) => {
|
|
3074
|
+
app.post("/api/collections/:slug/:id/transitions/:transition", requireAuth(config), async (c) => {
|
|
2731
3075
|
const slug = c.req.param("slug");
|
|
2732
3076
|
const siteId = c.req.header("X-Site-Id") || c.get("siteId");
|
|
2733
3077
|
const config2 = c.get("config");
|
|
@@ -2745,7 +3089,7 @@ function registerRoutes(app, config) {
|
|
|
2745
3089
|
const controller = new CollectionController(collection);
|
|
2746
3090
|
return controller.transition(c);
|
|
2747
3091
|
});
|
|
2748
|
-
app.get("/api/collections/:slug/:id/workflow-history", requireAuth(), async (c) => {
|
|
3092
|
+
app.get("/api/collections/:slug/:id/workflow-history", requireAuth(config), async (c) => {
|
|
2749
3093
|
const slug = c.req.param("slug");
|
|
2750
3094
|
const siteId = c.req.header("X-Site-Id") || c.get("siteId");
|
|
2751
3095
|
const config2 = c.get("config");
|
|
@@ -2844,7 +3188,7 @@ async function createDyrectedApp(rawConfig) {
|
|
|
2844
3188
|
await config.db.sync(config.collections, config.globals);
|
|
2845
3189
|
}
|
|
2846
3190
|
app.use("*", requestId());
|
|
2847
|
-
app.use("*", optionalAuth());
|
|
3191
|
+
app.use("*", optionalAuth(config));
|
|
2848
3192
|
app.use("*", async (c, next) => {
|
|
2849
3193
|
const start = Date.now();
|
|
2850
3194
|
await next();
|