@dyrected/core 2.5.55 → 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-Bxg_DVxv.d.cts +1413 -0
- package/dist/app-config-Bxg_DVxv.d.ts +1413 -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.cjs
CHANGED
|
@@ -599,6 +599,9 @@ async function evaluateAccess(expression, context) {
|
|
|
599
599
|
if (typeof expression === "boolean") return expression;
|
|
600
600
|
try {
|
|
601
601
|
const result = await import_jexl2.default.eval(expression, context);
|
|
602
|
+
if (result && typeof result === "object" && !Array.isArray(result)) {
|
|
603
|
+
return result;
|
|
604
|
+
}
|
|
602
605
|
return !!result;
|
|
603
606
|
} catch (err) {
|
|
604
607
|
console.error("[dyrected/core] Jexl evaluation failed:", err);
|
|
@@ -606,6 +609,39 @@ async function evaluateAccess(expression, context) {
|
|
|
606
609
|
}
|
|
607
610
|
}
|
|
608
611
|
|
|
612
|
+
// src/auth/access.ts
|
|
613
|
+
function isNamedAccessPolicy(value) {
|
|
614
|
+
return !!value && typeof value === "object" && "policy" in value && typeof value.policy === "string";
|
|
615
|
+
}
|
|
616
|
+
async function resolveAccess(config, access, args) {
|
|
617
|
+
if (access === void 0 || access === null) return void 0;
|
|
618
|
+
if (typeof access === "boolean" || typeof access === "string") {
|
|
619
|
+
return evaluateAccess(access, args);
|
|
620
|
+
}
|
|
621
|
+
if (typeof access === "function") {
|
|
622
|
+
try {
|
|
623
|
+
return await access(args);
|
|
624
|
+
} catch (err) {
|
|
625
|
+
console.error("[dyrected/core] Functional access check failed:", err);
|
|
626
|
+
return false;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
if (isNamedAccessPolicy(access)) {
|
|
630
|
+
const resolver = config.accessPolicies?.[access.policy];
|
|
631
|
+
if (!resolver) {
|
|
632
|
+
console.error(`[dyrected/core] Unknown access policy "${access.policy}".`);
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
635
|
+
try {
|
|
636
|
+
return await resolver({ ...args, params: access.params });
|
|
637
|
+
} catch (err) {
|
|
638
|
+
console.error(`[dyrected/core] Access policy "${access.policy}" failed:`, err);
|
|
639
|
+
return false;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
return false;
|
|
643
|
+
}
|
|
644
|
+
|
|
609
645
|
// src/utils/admin-auth.ts
|
|
610
646
|
function getAdminAuthCollection(config) {
|
|
611
647
|
const adminCollection = resolveAdminAuthCollection(config.collections, config.adminAuth?.collectionSlug);
|
|
@@ -641,6 +677,140 @@ function humanizeProviderName(id, type) {
|
|
|
641
677
|
return cleaned.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
642
678
|
}
|
|
643
679
|
|
|
680
|
+
// src/utils/access-control.ts
|
|
681
|
+
function toHookRequestContext(req) {
|
|
682
|
+
return {
|
|
683
|
+
query: req.query(),
|
|
684
|
+
headers: req.header(),
|
|
685
|
+
raw: req.raw
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
async function resolveBooleanAccess(config, access, args) {
|
|
689
|
+
const result = await resolveAccess(config, access, args);
|
|
690
|
+
if (result === void 0) return true;
|
|
691
|
+
return typeof result === "boolean" ? result : false;
|
|
692
|
+
}
|
|
693
|
+
async function matchesAccessConstraint(config, collection, id, constraint) {
|
|
694
|
+
if (!config.db) return false;
|
|
695
|
+
const match = await config.db.find({
|
|
696
|
+
collection,
|
|
697
|
+
where: { AND: [{ id: { equals: id } }, constraint] },
|
|
698
|
+
limit: 1
|
|
699
|
+
});
|
|
700
|
+
return match.total > 0;
|
|
701
|
+
}
|
|
702
|
+
async function resolveCollectionAccess(config, collection, action, access, args) {
|
|
703
|
+
const result = await resolveAccess(config, access, args);
|
|
704
|
+
if (result === void 0) return { allowed: true };
|
|
705
|
+
if (typeof result === "boolean") return { allowed: result };
|
|
706
|
+
if (action === "read" && !args.id) {
|
|
707
|
+
return { allowed: true, constraint: result };
|
|
708
|
+
}
|
|
709
|
+
if (!args.id) {
|
|
710
|
+
return { allowed: false };
|
|
711
|
+
}
|
|
712
|
+
return {
|
|
713
|
+
allowed: await matchesAccessConstraint(config, collection, args.id, result)
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
async function applyFieldReadAccess(context, doc) {
|
|
717
|
+
if (!doc || typeof doc !== "object") return doc;
|
|
718
|
+
const result = { ...doc };
|
|
719
|
+
for (const field of context.fields) {
|
|
720
|
+
if (field.type === "row" && field.fields) {
|
|
721
|
+
Object.assign(result, await applyFieldReadAccess({ ...context, fields: field.fields }, result));
|
|
722
|
+
continue;
|
|
723
|
+
}
|
|
724
|
+
if (!field.name) continue;
|
|
725
|
+
const value = result[field.name];
|
|
726
|
+
const canRead = await resolveBooleanAccess(context.config, field.access?.read, {
|
|
727
|
+
user: context.user,
|
|
728
|
+
req: context.req,
|
|
729
|
+
doc: context.doc,
|
|
730
|
+
data: context.data
|
|
731
|
+
});
|
|
732
|
+
if (!canRead) {
|
|
733
|
+
delete result[field.name];
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
if (value === void 0 || value === null) continue;
|
|
737
|
+
if (field.type === "object" && field.fields && typeof value === "object" && !Array.isArray(value)) {
|
|
738
|
+
result[field.name] = await applyFieldReadAccess({ ...context, fields: field.fields }, value);
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
if (field.type === "array" && field.fields && Array.isArray(value)) {
|
|
742
|
+
result[field.name] = await Promise.all(
|
|
743
|
+
value.map(
|
|
744
|
+
(item) => typeof item === "object" && item !== null ? applyFieldReadAccess({ ...context, fields: field.fields }, item) : item
|
|
745
|
+
)
|
|
746
|
+
);
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
if (field.type === "blocks" && field.blocks && Array.isArray(value)) {
|
|
750
|
+
result[field.name] = await Promise.all(
|
|
751
|
+
value.map(async (item) => {
|
|
752
|
+
if (typeof item !== "object" || item === null) return item;
|
|
753
|
+
const typedItem = item;
|
|
754
|
+
const block = field.blocks?.find((candidate) => candidate.slug === typedItem.blockType);
|
|
755
|
+
if (!block) return item;
|
|
756
|
+
return applyFieldReadAccess({ ...context, fields: block.fields }, typedItem);
|
|
757
|
+
})
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
return result;
|
|
762
|
+
}
|
|
763
|
+
async function applyFieldWriteAccess(context, data) {
|
|
764
|
+
if (!data || typeof data !== "object") return data;
|
|
765
|
+
const result = { ...data };
|
|
766
|
+
for (const field of context.fields) {
|
|
767
|
+
if (field.type === "row" && field.fields) {
|
|
768
|
+
Object.assign(result, await applyFieldWriteAccess({ ...context, fields: field.fields }, result));
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
if (!field.name || !(field.name in result)) continue;
|
|
772
|
+
const canUpdate = await resolveBooleanAccess(context.config, field.access?.update, {
|
|
773
|
+
user: context.user,
|
|
774
|
+
req: context.req,
|
|
775
|
+
doc: context.doc,
|
|
776
|
+
data: context.data
|
|
777
|
+
});
|
|
778
|
+
if (!canUpdate) {
|
|
779
|
+
delete result[field.name];
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
782
|
+
const value = result[field.name];
|
|
783
|
+
if (value === void 0 || value === null) continue;
|
|
784
|
+
if (field.type === "object" && field.fields && typeof value === "object" && !Array.isArray(value)) {
|
|
785
|
+
result[field.name] = await applyFieldWriteAccess({ ...context, fields: field.fields }, value);
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
if (field.type === "array" && field.fields && Array.isArray(value)) {
|
|
789
|
+
result[field.name] = await Promise.all(
|
|
790
|
+
value.map(
|
|
791
|
+
(item) => typeof item === "object" && item !== null ? applyFieldWriteAccess({ ...context, fields: field.fields }, item) : item
|
|
792
|
+
)
|
|
793
|
+
);
|
|
794
|
+
continue;
|
|
795
|
+
}
|
|
796
|
+
if (field.type === "blocks" && field.blocks && Array.isArray(value)) {
|
|
797
|
+
result[field.name] = await Promise.all(
|
|
798
|
+
value.map(async (item) => {
|
|
799
|
+
if (typeof item !== "object" || item === null) return item;
|
|
800
|
+
const typedItem = item;
|
|
801
|
+
const block = field.blocks?.find((candidate) => candidate.slug === typedItem.blockType);
|
|
802
|
+
if (!block) return item;
|
|
803
|
+
return applyFieldWriteAccess({ ...context, fields: block.fields }, typedItem);
|
|
804
|
+
})
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
return result;
|
|
809
|
+
}
|
|
810
|
+
function mergeWhereConstraint(where, constraint) {
|
|
811
|
+
return where ? { AND: [where, constraint] } : constraint;
|
|
812
|
+
}
|
|
813
|
+
|
|
644
814
|
// src/workflows.ts
|
|
645
815
|
var WORKFLOW_HISTORY_COLLECTION = "__workflow_history";
|
|
646
816
|
var LIFECYCLE_EVENTS_COLLECTION = "__lifecycle_events";
|
|
@@ -879,11 +1049,23 @@ var CollectionController = class {
|
|
|
879
1049
|
return config.adminAuth?.providers?.find((p) => p.members) || null;
|
|
880
1050
|
}
|
|
881
1051
|
toHookRequestContext(c) {
|
|
882
|
-
return
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
1052
|
+
return toHookRequestContext(c.req);
|
|
1053
|
+
}
|
|
1054
|
+
async evaluateAccess(c, action, options = {}) {
|
|
1055
|
+
const config = c.get("config");
|
|
1056
|
+
return resolveCollectionAccess(
|
|
1057
|
+
config,
|
|
1058
|
+
this.collection.slug,
|
|
1059
|
+
action,
|
|
1060
|
+
this.collection.access?.[action],
|
|
1061
|
+
{
|
|
1062
|
+
id: options.id,
|
|
1063
|
+
user: c.get("user"),
|
|
1064
|
+
req: this.toHookRequestContext(c),
|
|
1065
|
+
doc: options.doc ?? void 0,
|
|
1066
|
+
data: options.data
|
|
1067
|
+
}
|
|
1068
|
+
);
|
|
887
1069
|
}
|
|
888
1070
|
async find(c) {
|
|
889
1071
|
const config = c.get("config");
|
|
@@ -963,6 +1145,13 @@ var CollectionController = class {
|
|
|
963
1145
|
if (this.collection.workflow && !canViewWorkflowDraft(this.collection.workflow, user)) {
|
|
964
1146
|
where = where ? { AND: [where, { __published: { exists: true } }] } : { __published: { exists: true } };
|
|
965
1147
|
}
|
|
1148
|
+
const access = await this.evaluateAccess(c, "read");
|
|
1149
|
+
if (!access.allowed) {
|
|
1150
|
+
return c.json({ error: true, message: `Access denied: read on ${this.collection.slug}` }, 403);
|
|
1151
|
+
}
|
|
1152
|
+
if (access.constraint) {
|
|
1153
|
+
where = mergeWhereConstraint(where, access.constraint);
|
|
1154
|
+
}
|
|
966
1155
|
let result = await db.find({
|
|
967
1156
|
collection: this.collection.slug,
|
|
968
1157
|
limit,
|
|
@@ -995,7 +1184,14 @@ var CollectionController = class {
|
|
|
995
1184
|
db: readonlyDb
|
|
996
1185
|
});
|
|
997
1186
|
const docWithFieldHooks = await executeFieldAfterRead(this.collection.fields, docWithCollectionHooks, user, readonlyDb);
|
|
998
|
-
|
|
1187
|
+
const docWithFieldAccess = await applyFieldReadAccess({
|
|
1188
|
+
config,
|
|
1189
|
+
fields: this.collection.fields,
|
|
1190
|
+
user,
|
|
1191
|
+
req: this.toHookRequestContext(c),
|
|
1192
|
+
doc: docWithFieldHooks
|
|
1193
|
+
}, docWithFieldHooks);
|
|
1194
|
+
processedDocs.push(docWithFieldAccess);
|
|
999
1195
|
}
|
|
1000
1196
|
result.docs = processedDocs;
|
|
1001
1197
|
if (depth > 0) {
|
|
@@ -1040,6 +1236,10 @@ var CollectionController = class {
|
|
|
1040
1236
|
doc = materializeWorkflowDocument(doc, this.collection.workflow, user);
|
|
1041
1237
|
if (!doc) return c.json({ message: "Not Found" }, 404);
|
|
1042
1238
|
}
|
|
1239
|
+
const access = await this.evaluateAccess(c, "read", { id, doc });
|
|
1240
|
+
if (!access.allowed) {
|
|
1241
|
+
return c.json({ error: true, message: `Access denied: read on ${this.collection.slug}` }, 403);
|
|
1242
|
+
}
|
|
1043
1243
|
const docWithDefaults = DefaultsService.apply(this.collection.fields, doc);
|
|
1044
1244
|
const docWithCollectionHooks = await runCollectionHooks(this.collection.hooks?.afterRead, {
|
|
1045
1245
|
doc: docWithDefaults,
|
|
@@ -1048,17 +1248,24 @@ var CollectionController = class {
|
|
|
1048
1248
|
db: readonlyDb
|
|
1049
1249
|
});
|
|
1050
1250
|
const docWithFieldHooks = await executeFieldAfterRead(this.collection.fields, docWithCollectionHooks, user, readonlyDb);
|
|
1051
|
-
|
|
1251
|
+
const docWithFieldAccess = await applyFieldReadAccess({
|
|
1252
|
+
config,
|
|
1253
|
+
fields: this.collection.fields,
|
|
1254
|
+
user,
|
|
1255
|
+
req: this.toHookRequestContext(c),
|
|
1256
|
+
doc: docWithFieldHooks
|
|
1257
|
+
}, docWithFieldHooks);
|
|
1258
|
+
if (depth > 0 && docWithFieldAccess) {
|
|
1052
1259
|
const populationService = new PopulationService(db, config.collections);
|
|
1053
1260
|
const populatedDoc = await populationService.populate({
|
|
1054
|
-
data:
|
|
1261
|
+
data: docWithFieldAccess,
|
|
1055
1262
|
fields: this.collection.fields,
|
|
1056
1263
|
currentDepth: 0,
|
|
1057
1264
|
maxDepth: depth
|
|
1058
1265
|
});
|
|
1059
1266
|
return c.json(populatedDoc);
|
|
1060
1267
|
}
|
|
1061
|
-
return c.json(
|
|
1268
|
+
return c.json(docWithFieldAccess);
|
|
1062
1269
|
}
|
|
1063
1270
|
async create(c) {
|
|
1064
1271
|
const config = c.get("config");
|
|
@@ -1097,9 +1304,20 @@ var CollectionController = class {
|
|
|
1097
1304
|
if (this.collection.workflow) {
|
|
1098
1305
|
data = initializeWorkflowDocument(data, this.collection.workflow);
|
|
1099
1306
|
}
|
|
1307
|
+
const createAccess = await this.evaluateAccess(c, "create", { data });
|
|
1308
|
+
if (!createAccess.allowed) {
|
|
1309
|
+
return c.json({ error: true, message: `Access denied: create on ${this.collection.slug}` }, 403);
|
|
1310
|
+
}
|
|
1100
1311
|
if (this.collection.auth && data.password) {
|
|
1101
1312
|
data.password = await hashPassword(data.password);
|
|
1102
1313
|
}
|
|
1314
|
+
data = await applyFieldWriteAccess({
|
|
1315
|
+
config,
|
|
1316
|
+
fields: this.collection.fields,
|
|
1317
|
+
user,
|
|
1318
|
+
req: this.toHookRequestContext(c),
|
|
1319
|
+
data
|
|
1320
|
+
}, data);
|
|
1103
1321
|
data = await executeFieldBeforeChange(this.collection.fields, data, null, user, readonlyDb);
|
|
1104
1322
|
data = await runCollectionHooks(this.collection.hooks?.beforeChange, {
|
|
1105
1323
|
data,
|
|
@@ -1134,7 +1352,14 @@ var CollectionController = class {
|
|
|
1134
1352
|
db: readonlyDb
|
|
1135
1353
|
});
|
|
1136
1354
|
const finalDoc = await executeFieldAfterRead(this.collection.fields, readDoc, user, readonlyDb);
|
|
1137
|
-
|
|
1355
|
+
const accessibleDoc = await applyFieldReadAccess({
|
|
1356
|
+
config,
|
|
1357
|
+
fields: this.collection.fields,
|
|
1358
|
+
user,
|
|
1359
|
+
req: this.toHookRequestContext(c),
|
|
1360
|
+
doc: finalDoc
|
|
1361
|
+
}, finalDoc);
|
|
1362
|
+
return c.json(accessibleDoc, 201);
|
|
1138
1363
|
}
|
|
1139
1364
|
async upload(c) {
|
|
1140
1365
|
const config = c.get("config");
|
|
@@ -1177,6 +1402,17 @@ var CollectionController = class {
|
|
|
1177
1402
|
createdBy: user?.sub ?? null,
|
|
1178
1403
|
updatedBy: user?.sub ?? null
|
|
1179
1404
|
};
|
|
1405
|
+
const createAccess = await this.evaluateAccess(c, "create", { data });
|
|
1406
|
+
if (!createAccess.allowed) {
|
|
1407
|
+
return c.json({ error: true, message: `Access denied: create on ${this.collection.slug}` }, 403);
|
|
1408
|
+
}
|
|
1409
|
+
data = await applyFieldWriteAccess({
|
|
1410
|
+
config,
|
|
1411
|
+
fields: this.collection.fields,
|
|
1412
|
+
user,
|
|
1413
|
+
req: this.toHookRequestContext(c),
|
|
1414
|
+
data
|
|
1415
|
+
}, data);
|
|
1180
1416
|
data = await executeFieldBeforeChange(this.collection.fields, data, null, user, readonlyDb);
|
|
1181
1417
|
data = await runCollectionHooks(this.collection.hooks?.beforeChange, {
|
|
1182
1418
|
data,
|
|
@@ -1204,7 +1440,14 @@ var CollectionController = class {
|
|
|
1204
1440
|
db: readonlyDb
|
|
1205
1441
|
});
|
|
1206
1442
|
const finalDoc = await executeFieldAfterRead(this.collection.fields, readDoc, user, readonlyDb);
|
|
1207
|
-
|
|
1443
|
+
const accessibleDoc = await applyFieldReadAccess({
|
|
1444
|
+
config,
|
|
1445
|
+
fields: this.collection.fields,
|
|
1446
|
+
user,
|
|
1447
|
+
req: this.toHookRequestContext(c),
|
|
1448
|
+
doc: finalDoc
|
|
1449
|
+
}, finalDoc);
|
|
1450
|
+
return c.json(accessibleDoc, 201);
|
|
1208
1451
|
}
|
|
1209
1452
|
async update(c) {
|
|
1210
1453
|
const config = c.get("config");
|
|
@@ -1242,10 +1485,22 @@ var CollectionController = class {
|
|
|
1242
1485
|
});
|
|
1243
1486
|
const originalDoc = await db.findOne({ collection: this.collection.slug, id });
|
|
1244
1487
|
if (!originalDoc) return c.json({ message: "Not Found" }, 404);
|
|
1488
|
+
const updateAccess = await this.evaluateAccess(c, "update", { id, doc: originalDoc, data });
|
|
1489
|
+
if (!updateAccess.allowed) {
|
|
1490
|
+
return c.json({ error: true, message: `Access denied: update on ${this.collection.slug}` }, 403);
|
|
1491
|
+
}
|
|
1245
1492
|
let before = null;
|
|
1246
1493
|
if (this.collection.audit) {
|
|
1247
1494
|
before = originalDoc;
|
|
1248
1495
|
}
|
|
1496
|
+
data = await applyFieldWriteAccess({
|
|
1497
|
+
config,
|
|
1498
|
+
fields: this.collection.fields,
|
|
1499
|
+
user,
|
|
1500
|
+
req: this.toHookRequestContext(c),
|
|
1501
|
+
doc: originalDoc,
|
|
1502
|
+
data
|
|
1503
|
+
}, data);
|
|
1249
1504
|
data = await executeFieldBeforeChange(this.collection.fields, data, originalDoc, user, readonlyDb);
|
|
1250
1505
|
data = await runCollectionHooks(this.collection.hooks?.beforeChange, {
|
|
1251
1506
|
data,
|
|
@@ -1281,7 +1536,14 @@ var CollectionController = class {
|
|
|
1281
1536
|
db: readonlyDb
|
|
1282
1537
|
});
|
|
1283
1538
|
const finalDoc = await executeFieldAfterRead(this.collection.fields, readDoc, user, readonlyDb);
|
|
1284
|
-
|
|
1539
|
+
const accessibleDoc = await applyFieldReadAccess({
|
|
1540
|
+
config,
|
|
1541
|
+
fields: this.collection.fields,
|
|
1542
|
+
user,
|
|
1543
|
+
req: this.toHookRequestContext(c),
|
|
1544
|
+
doc: finalDoc
|
|
1545
|
+
}, finalDoc);
|
|
1546
|
+
return c.json(accessibleDoc);
|
|
1285
1547
|
}
|
|
1286
1548
|
async transition(c) {
|
|
1287
1549
|
const config = c.get("config");
|
|
@@ -1318,7 +1580,7 @@ var CollectionController = class {
|
|
|
1318
1580
|
const readAccess = this.collection.access?.read;
|
|
1319
1581
|
if (readAccess !== void 0 && readAccess !== null) {
|
|
1320
1582
|
const args = { user: c.get("user"), req: c.req, doc: document };
|
|
1321
|
-
const result2 =
|
|
1583
|
+
const result2 = await resolveAccess(config, readAccess, args);
|
|
1322
1584
|
let allowed = result2 === true;
|
|
1323
1585
|
if (result2 && typeof result2 === "object") {
|
|
1324
1586
|
const match = await config.db.find({
|
|
@@ -1429,6 +1691,10 @@ var CollectionController = class {
|
|
|
1429
1691
|
const user = c.get("user");
|
|
1430
1692
|
const doc = await db.findOne({ collection: this.collection.slug, id });
|
|
1431
1693
|
if (!doc) return c.json({ message: "Not Found" }, 404);
|
|
1694
|
+
const deleteAccess = await this.evaluateAccess(c, "delete", { id, doc });
|
|
1695
|
+
if (!deleteAccess.allowed) {
|
|
1696
|
+
return c.json({ error: true, message: `Access denied: delete on ${this.collection.slug}` }, 403);
|
|
1697
|
+
}
|
|
1432
1698
|
let before = null;
|
|
1433
1699
|
if (this.collection.audit) {
|
|
1434
1700
|
before = doc;
|
|
@@ -1488,6 +1754,11 @@ var CollectionController = class {
|
|
|
1488
1754
|
failed.push({ id, error: "Not Found" });
|
|
1489
1755
|
continue;
|
|
1490
1756
|
}
|
|
1757
|
+
const deleteAccess = await this.evaluateAccess(c, "delete", { id, doc });
|
|
1758
|
+
if (!deleteAccess.allowed) {
|
|
1759
|
+
failed.push({ id, error: "Access denied" });
|
|
1760
|
+
continue;
|
|
1761
|
+
}
|
|
1491
1762
|
let before = null;
|
|
1492
1763
|
if (this.collection.audit) {
|
|
1493
1764
|
before = doc;
|
|
@@ -1581,6 +1852,14 @@ var GlobalController = class {
|
|
|
1581
1852
|
await db.updateGlobal({ slug: this.global.slug, data: this.global.initialData });
|
|
1582
1853
|
data = this.global.initialData;
|
|
1583
1854
|
}
|
|
1855
|
+
const canRead = await resolveBooleanAccess(config, this.global.access?.read, {
|
|
1856
|
+
user,
|
|
1857
|
+
req: toHookRequestContext(c.req),
|
|
1858
|
+
doc: data
|
|
1859
|
+
});
|
|
1860
|
+
if (!canRead) {
|
|
1861
|
+
return c.json({ error: true, message: `Access denied: read on ${this.global.slug}` }, 403);
|
|
1862
|
+
}
|
|
1584
1863
|
const dataWithDefaults = DefaultsService.apply(this.global.fields, data);
|
|
1585
1864
|
const docWithCollectionHooks = await runCollectionHooks(this.global.hooks?.afterRead, {
|
|
1586
1865
|
doc: dataWithDefaults,
|
|
@@ -1589,17 +1868,24 @@ var GlobalController = class {
|
|
|
1589
1868
|
db: readonlyDb
|
|
1590
1869
|
});
|
|
1591
1870
|
const docWithFieldHooks = await executeFieldAfterRead(this.global.fields, docWithCollectionHooks, user, readonlyDb);
|
|
1592
|
-
|
|
1871
|
+
const docWithFieldAccess = await applyFieldReadAccess({
|
|
1872
|
+
config,
|
|
1873
|
+
fields: this.global.fields,
|
|
1874
|
+
user,
|
|
1875
|
+
req: toHookRequestContext(c.req),
|
|
1876
|
+
doc: docWithFieldHooks
|
|
1877
|
+
}, docWithFieldHooks);
|
|
1878
|
+
if (depth > 0 && docWithFieldAccess) {
|
|
1593
1879
|
const populationService = new PopulationService(db, config.collections);
|
|
1594
1880
|
const populatedData = await populationService.populate({
|
|
1595
|
-
data:
|
|
1881
|
+
data: docWithFieldAccess,
|
|
1596
1882
|
fields: this.global.fields,
|
|
1597
1883
|
currentDepth: 0,
|
|
1598
1884
|
maxDepth: depth
|
|
1599
1885
|
});
|
|
1600
1886
|
return c.json(populatedData);
|
|
1601
1887
|
}
|
|
1602
|
-
return c.json(
|
|
1888
|
+
return c.json(docWithFieldAccess);
|
|
1603
1889
|
}
|
|
1604
1890
|
async update(c) {
|
|
1605
1891
|
const config = c.get("config");
|
|
@@ -1609,7 +1895,24 @@ var GlobalController = class {
|
|
|
1609
1895
|
const body = await c.req.json();
|
|
1610
1896
|
const user = c.get("user");
|
|
1611
1897
|
const originalDoc = await db.getGlobal({ slug: this.global.slug }) || {};
|
|
1612
|
-
|
|
1898
|
+
const canUpdate = await resolveBooleanAccess(config, this.global.access?.update, {
|
|
1899
|
+
user,
|
|
1900
|
+
req: toHookRequestContext(c.req),
|
|
1901
|
+
doc: originalDoc,
|
|
1902
|
+
data: body
|
|
1903
|
+
});
|
|
1904
|
+
if (!canUpdate) {
|
|
1905
|
+
return c.json({ error: true, message: `Access denied: update on ${this.global.slug}` }, 403);
|
|
1906
|
+
}
|
|
1907
|
+
let sanitizedBody = await applyFieldWriteAccess({
|
|
1908
|
+
config,
|
|
1909
|
+
fields: this.global.fields,
|
|
1910
|
+
user,
|
|
1911
|
+
req: toHookRequestContext(c.req),
|
|
1912
|
+
doc: originalDoc,
|
|
1913
|
+
data: body
|
|
1914
|
+
}, body);
|
|
1915
|
+
let data = await executeFieldBeforeChange(this.global.fields, sanitizedBody, originalDoc, user, readonlyDb);
|
|
1613
1916
|
data = await runCollectionHooks(this.global.hooks?.beforeChange, {
|
|
1614
1917
|
data,
|
|
1615
1918
|
doc: originalDoc,
|
|
@@ -1634,7 +1937,14 @@ var GlobalController = class {
|
|
|
1634
1937
|
db: readonlyDb
|
|
1635
1938
|
});
|
|
1636
1939
|
const finalDoc = await executeFieldAfterRead(this.global.fields, readDoc, user, readonlyDb);
|
|
1637
|
-
|
|
1940
|
+
const accessibleDoc = await applyFieldReadAccess({
|
|
1941
|
+
config,
|
|
1942
|
+
fields: this.global.fields,
|
|
1943
|
+
user,
|
|
1944
|
+
req: toHookRequestContext(c.req),
|
|
1945
|
+
doc: finalDoc
|
|
1946
|
+
}, finalDoc);
|
|
1947
|
+
return c.json(accessibleDoc);
|
|
1638
1948
|
}
|
|
1639
1949
|
async seed(c) {
|
|
1640
1950
|
const config = c.get("config");
|
|
@@ -1816,7 +2126,7 @@ var MediaController = class {
|
|
|
1816
2126
|
var import_jose = require("jose");
|
|
1817
2127
|
var import_node_util2 = require("util");
|
|
1818
2128
|
function getSecret() {
|
|
1819
|
-
const secret = process.env.DYRECTED_JWT_SECRET
|
|
2129
|
+
const secret = process.env.DYRECTED_JWT_SECRET;
|
|
1820
2130
|
if (!secret) {
|
|
1821
2131
|
throw new Error(
|
|
1822
2132
|
"[dyrected/core] DYRECTED_JWT_SECRET is not set. Add it to your environment variables to enable auth collections."
|
|
@@ -2686,7 +2996,7 @@ var AdminAuthController = class {
|
|
|
2686
2996
|
return state;
|
|
2687
2997
|
}
|
|
2688
2998
|
getStateSecret() {
|
|
2689
|
-
const secret = process.env.DYRECTED_JWT_SECRET
|
|
2999
|
+
const secret = process.env.DYRECTED_JWT_SECRET;
|
|
2690
3000
|
if (!secret) {
|
|
2691
3001
|
throw new Error("[dyrected/core] DYRECTED_JWT_SECRET is not set.");
|
|
2692
3002
|
}
|
|
@@ -2704,7 +3014,7 @@ var import_jose3 = require("jose");
|
|
|
2704
3014
|
var import_node_util4 = require("util");
|
|
2705
3015
|
var PreviewController = class {
|
|
2706
3016
|
getSecret() {
|
|
2707
|
-
const secret = process.env.DYRECTED_JWT_SECRET ||
|
|
3017
|
+
const secret = process.env.DYRECTED_JWT_SECRET || "dyrected-preview-secret-change-me";
|
|
2708
3018
|
return new import_node_util4.TextEncoder().encode(secret);
|
|
2709
3019
|
}
|
|
2710
3020
|
/**
|
|
@@ -2743,30 +3053,74 @@ var PreviewController = class {
|
|
|
2743
3053
|
};
|
|
2744
3054
|
|
|
2745
3055
|
// src/middleware/auth.ts
|
|
2746
|
-
function
|
|
3056
|
+
function getBearerToken(c) {
|
|
3057
|
+
const authHeader = c.req.header("Authorization");
|
|
3058
|
+
return authHeader?.replace(/^Bearer\s+/i, "") || void 0;
|
|
3059
|
+
}
|
|
3060
|
+
async function resolveUser(token, config) {
|
|
3061
|
+
const payload = await verifyCollectionToken(token);
|
|
3062
|
+
if (payload.purpose) {
|
|
3063
|
+
return payload;
|
|
3064
|
+
}
|
|
3065
|
+
const db = config?.db;
|
|
3066
|
+
if (!db) {
|
|
3067
|
+
return payload;
|
|
3068
|
+
}
|
|
3069
|
+
let doc;
|
|
3070
|
+
try {
|
|
3071
|
+
doc = await db.findOne({
|
|
3072
|
+
collection: payload.collection,
|
|
3073
|
+
id: payload.sub
|
|
3074
|
+
});
|
|
3075
|
+
} catch (err) {
|
|
3076
|
+
console.error("[dyrected/core] Failed to hydrate user from token:", err);
|
|
3077
|
+
return payload;
|
|
3078
|
+
}
|
|
3079
|
+
if (!doc) {
|
|
3080
|
+
return null;
|
|
3081
|
+
}
|
|
3082
|
+
const { password: _password, ...safe } = doc;
|
|
3083
|
+
return {
|
|
3084
|
+
...safe,
|
|
3085
|
+
sub: payload.sub,
|
|
3086
|
+
email: payload.email ?? safe.email,
|
|
3087
|
+
collection: payload.collection
|
|
3088
|
+
};
|
|
3089
|
+
}
|
|
3090
|
+
function requireAuth(config) {
|
|
2747
3091
|
return async (c, next) => {
|
|
2748
|
-
|
|
2749
|
-
|
|
3092
|
+
if (c.get("user")) {
|
|
3093
|
+
return next();
|
|
3094
|
+
}
|
|
3095
|
+
const token = getBearerToken(c);
|
|
2750
3096
|
if (!token) {
|
|
2751
3097
|
return c.json({ error: true, message: "Authentication required." }, 401);
|
|
2752
3098
|
}
|
|
3099
|
+
let user;
|
|
2753
3100
|
try {
|
|
2754
|
-
|
|
2755
|
-
c.set("user", user);
|
|
2756
|
-
await next();
|
|
3101
|
+
user = await resolveUser(token, config ?? c.get("config"));
|
|
2757
3102
|
} catch {
|
|
2758
3103
|
return c.json({ error: true, message: "Invalid or expired token." }, 401);
|
|
2759
3104
|
}
|
|
3105
|
+
if (!user) {
|
|
3106
|
+
return c.json({ error: true, message: "Invalid or expired token." }, 401);
|
|
3107
|
+
}
|
|
3108
|
+
c.set("user", user);
|
|
3109
|
+
await next();
|
|
2760
3110
|
};
|
|
2761
3111
|
}
|
|
2762
|
-
function optionalAuth() {
|
|
3112
|
+
function optionalAuth(config) {
|
|
2763
3113
|
return async (c, next) => {
|
|
2764
|
-
|
|
2765
|
-
|
|
3114
|
+
if (c.get("user")) {
|
|
3115
|
+
return next();
|
|
3116
|
+
}
|
|
3117
|
+
const token = getBearerToken(c);
|
|
2766
3118
|
if (token) {
|
|
2767
3119
|
try {
|
|
2768
|
-
const user = await
|
|
2769
|
-
|
|
3120
|
+
const user = await resolveUser(token, config ?? c.get("config"));
|
|
3121
|
+
if (user) {
|
|
3122
|
+
c.set("user", user);
|
|
3123
|
+
}
|
|
2770
3124
|
} catch {
|
|
2771
3125
|
}
|
|
2772
3126
|
}
|
|
@@ -3654,37 +4008,23 @@ function getSwaggerHtml(specUrl) {
|
|
|
3654
4008
|
}
|
|
3655
4009
|
|
|
3656
4010
|
// src/router.ts
|
|
3657
|
-
function accessGate(target, action) {
|
|
4011
|
+
function accessGate(config, target, action) {
|
|
3658
4012
|
return async (c, next) => {
|
|
3659
4013
|
const user = c.get("user");
|
|
3660
4014
|
const accessExpr = target.access?.[action];
|
|
3661
4015
|
if (accessExpr === void 0 || accessExpr === null) {
|
|
3662
4016
|
return await next();
|
|
3663
4017
|
}
|
|
3664
|
-
const
|
|
3665
|
-
|
|
4018
|
+
const allowed = await resolveBooleanAccess(config, accessExpr, {
|
|
4019
|
+
user,
|
|
4020
|
+
req: toHookRequestContext(c.req)
|
|
4021
|
+
});
|
|
3666
4022
|
if (!allowed) {
|
|
3667
4023
|
return c.json({ error: true, message: `Access denied: ${action} on ${target.slug}` }, 403);
|
|
3668
4024
|
}
|
|
3669
4025
|
await next();
|
|
3670
4026
|
};
|
|
3671
4027
|
}
|
|
3672
|
-
async function checkAccess(access, accessArgs) {
|
|
3673
|
-
if (access === void 0 || access === null) return true;
|
|
3674
|
-
if (typeof access === "function") {
|
|
3675
|
-
try {
|
|
3676
|
-
const result = await access(accessArgs);
|
|
3677
|
-
return typeof result === "boolean" ? result : !!result;
|
|
3678
|
-
} catch (err) {
|
|
3679
|
-
console.error("[dyrected/core] Functional access check failed:", err);
|
|
3680
|
-
return false;
|
|
3681
|
-
}
|
|
3682
|
-
}
|
|
3683
|
-
if (typeof access === "string" || typeof access === "boolean") {
|
|
3684
|
-
return evaluateAccess(access, accessArgs);
|
|
3685
|
-
}
|
|
3686
|
-
return true;
|
|
3687
|
-
}
|
|
3688
4028
|
function serializeFieldForApi(f) {
|
|
3689
4029
|
if (!f) return f;
|
|
3690
4030
|
const serialized = { ...f };
|
|
@@ -3713,7 +4053,7 @@ function serializeFieldForApi(f) {
|
|
|
3713
4053
|
return serialized;
|
|
3714
4054
|
}
|
|
3715
4055
|
function registerRoutes(app, config) {
|
|
3716
|
-
app.get("/api/schemas", optionalAuth(), async (c) => {
|
|
4056
|
+
app.get("/api/schemas", optionalAuth(config), async (c) => {
|
|
3717
4057
|
const siteId = c.req.header("X-Site-Id");
|
|
3718
4058
|
let collections = [...config.collections];
|
|
3719
4059
|
let globals = [...config.globals];
|
|
@@ -3729,11 +4069,11 @@ function registerRoutes(app, config) {
|
|
|
3729
4069
|
}
|
|
3730
4070
|
}
|
|
3731
4071
|
const user = c.get("user");
|
|
3732
|
-
const accessArgs = { user, req: c.req
|
|
4072
|
+
const accessArgs = { user, req: toHookRequestContext(c.req) };
|
|
3733
4073
|
const serializeAccess = async (access) => {
|
|
3734
4074
|
if (typeof access === "string") return access;
|
|
3735
4075
|
if (typeof access === "boolean") return access;
|
|
3736
|
-
return
|
|
4076
|
+
return resolveBooleanAccess(config, access, accessArgs);
|
|
3737
4077
|
};
|
|
3738
4078
|
const filteredCollections = await Promise.all(collections.filter((col) => !siteId || col.shared || !col.siteId || col.siteId === siteId).map(async (col) => ({
|
|
3739
4079
|
slug: col.slug,
|
|
@@ -3819,7 +4159,7 @@ function registerRoutes(app, config) {
|
|
|
3819
4159
|
adminAuth: getPublicAdminAuthConfig(config.adminAuth, collections)
|
|
3820
4160
|
});
|
|
3821
4161
|
});
|
|
3822
|
-
app.get("/api/dyrected/options/:collection/:field", optionalAuth(), async (c) => {
|
|
4162
|
+
app.get("/api/dyrected/options/:collection/:field", optionalAuth(config), async (c) => {
|
|
3823
4163
|
const { collection: colSlug, field: fieldName } = c.req.param();
|
|
3824
4164
|
const siteId = c.req.header("X-Site-Id");
|
|
3825
4165
|
let collections = [...config.collections];
|
|
@@ -3833,8 +4173,10 @@ function registerRoutes(app, config) {
|
|
|
3833
4173
|
if (collection) {
|
|
3834
4174
|
const accessExpr = collection.access?.read;
|
|
3835
4175
|
if (accessExpr !== void 0 && accessExpr !== null) {
|
|
3836
|
-
const
|
|
3837
|
-
|
|
4176
|
+
const allowed = await resolveBooleanAccess(config, accessExpr, {
|
|
4177
|
+
user,
|
|
4178
|
+
req: toHookRequestContext(c.req)
|
|
4179
|
+
});
|
|
3838
4180
|
if (!allowed) {
|
|
3839
4181
|
return c.json({ error: true, message: `Access denied: read on ${colSlug}` }, 403);
|
|
3840
4182
|
}
|
|
@@ -3852,8 +4194,10 @@ function registerRoutes(app, config) {
|
|
|
3852
4194
|
}
|
|
3853
4195
|
const accessExpr = glb.access?.read;
|
|
3854
4196
|
if (accessExpr !== void 0 && accessExpr !== null) {
|
|
3855
|
-
const
|
|
3856
|
-
|
|
4197
|
+
const allowed = await resolveBooleanAccess(config, accessExpr, {
|
|
4198
|
+
user,
|
|
4199
|
+
req: toHookRequestContext(c.req)
|
|
4200
|
+
});
|
|
3857
4201
|
if (!allowed) {
|
|
3858
4202
|
return c.json({ error: true, message: `Access denied: read on global ${colSlug}` }, 403);
|
|
3859
4203
|
}
|
|
@@ -3897,7 +4241,7 @@ function registerRoutes(app, config) {
|
|
|
3897
4241
|
app.get("/api/docs", (c) => {
|
|
3898
4242
|
return c.html(getSwaggerHtml());
|
|
3899
4243
|
});
|
|
3900
|
-
app.get("/api/preferences/:key", requireAuth(), async (c) => {
|
|
4244
|
+
app.get("/api/preferences/:key", requireAuth(config), async (c) => {
|
|
3901
4245
|
const db = config.db;
|
|
3902
4246
|
const user = c.get("user");
|
|
3903
4247
|
const key = c.req.param("key");
|
|
@@ -3922,7 +4266,7 @@ function registerRoutes(app, config) {
|
|
|
3922
4266
|
const globalValue = await getGlobalPreference();
|
|
3923
4267
|
return c.json({ key, value: globalValue });
|
|
3924
4268
|
});
|
|
3925
|
-
app.put("/api/preferences/:key", requireAuth(), async (c) => {
|
|
4269
|
+
app.put("/api/preferences/:key", requireAuth(config), async (c) => {
|
|
3926
4270
|
const db = config.db;
|
|
3927
4271
|
const user = c.get("user");
|
|
3928
4272
|
const key = c.req.param("key");
|
|
@@ -3962,7 +4306,7 @@ function registerRoutes(app, config) {
|
|
|
3962
4306
|
});
|
|
3963
4307
|
return c.json({ key, value: body.value });
|
|
3964
4308
|
});
|
|
3965
|
-
app.delete("/api/preferences/:key", requireAuth(), async (c) => {
|
|
4309
|
+
app.delete("/api/preferences/:key", requireAuth(config), async (c) => {
|
|
3966
4310
|
const db = config.db;
|
|
3967
4311
|
const user = c.get("user");
|
|
3968
4312
|
const key = c.req.param("key");
|
|
@@ -4002,10 +4346,10 @@ function registerRoutes(app, config) {
|
|
|
4002
4346
|
for (const col of uploadCollections) {
|
|
4003
4347
|
const mediaController = new MediaController(col.slug);
|
|
4004
4348
|
const prefix = `/api/collections/${col.slug}`;
|
|
4005
|
-
app.get(`${prefix}/media`, accessGate(col, "read"), (c) => mediaController.find(c));
|
|
4349
|
+
app.get(`${prefix}/media`, accessGate(config, col, "read"), (c) => mediaController.find(c));
|
|
4006
4350
|
app.get(`${prefix}/media/:filename{.+$}`, (c) => mediaController.serve(c));
|
|
4007
|
-
app.post(`${prefix}/media`, accessGate(col, "create"), (c) => mediaController.upload(c));
|
|
4008
|
-
app.delete(`${prefix}/media/:id`, accessGate(col, "delete"), (c) => mediaController.delete(c));
|
|
4351
|
+
app.post(`${prefix}/media`, accessGate(config, col, "create"), (c) => mediaController.upload(c));
|
|
4352
|
+
app.delete(`${prefix}/media/:id`, accessGate(config, col, "delete"), (c) => mediaController.delete(c));
|
|
4009
4353
|
}
|
|
4010
4354
|
}
|
|
4011
4355
|
const adminAuthController = new AdminAuthController(config);
|
|
@@ -4022,43 +4366,43 @@ function registerRoutes(app, config) {
|
|
|
4022
4366
|
app.post(`${path}/logout`, (c) => authController.logout(c));
|
|
4023
4367
|
app.get(`${path}/init`, (c) => authController.init(c));
|
|
4024
4368
|
app.post(`${path}/first-user`, (c) => authController.registerFirstUser(c));
|
|
4025
|
-
app.get(`${path}/me`, requireAuth(), (c) => authController.me(c));
|
|
4026
|
-
app.post(`${path}/refresh-token`, requireAuth(), (c) => authController.refreshToken(c));
|
|
4369
|
+
app.get(`${path}/me`, requireAuth(config), (c) => authController.me(c));
|
|
4370
|
+
app.post(`${path}/refresh-token`, requireAuth(config), (c) => authController.refreshToken(c));
|
|
4027
4371
|
app.post(`${path}/forgot-password`, (c) => authController.forgotPassword(c));
|
|
4028
4372
|
app.post(`${path}/reset-password`, (c) => authController.resetPassword(c));
|
|
4029
|
-
app.post(`${path}/invite`, requireAuth(), (c) => authController.invite(c));
|
|
4373
|
+
app.post(`${path}/invite`, requireAuth(config), (c) => authController.invite(c));
|
|
4030
4374
|
app.post(`${path}/accept-invite`, (c) => authController.acceptInvite(c));
|
|
4031
4375
|
}
|
|
4032
4376
|
for (const collection of config.collections) {
|
|
4033
4377
|
const path = `/api/collections/${collection.slug}`;
|
|
4034
4378
|
const controller = new CollectionController(collection);
|
|
4035
|
-
app.get(path,
|
|
4036
|
-
app.post(path,
|
|
4037
|
-
app.post(`${path}/media`,
|
|
4038
|
-
app.delete(`${path}/delete-many`,
|
|
4039
|
-
app.get(`${path}/:id`,
|
|
4040
|
-
app.patch(`${path}/:id`,
|
|
4041
|
-
app.delete(`${path}/:id`,
|
|
4379
|
+
app.get(path, (c) => controller.find(c));
|
|
4380
|
+
app.post(path, (c) => controller.create(c));
|
|
4381
|
+
app.post(`${path}/media`, (c) => controller.create(c));
|
|
4382
|
+
app.delete(`${path}/delete-many`, (c) => controller.deleteMany(c));
|
|
4383
|
+
app.get(`${path}/:id`, (c) => controller.findOne(c));
|
|
4384
|
+
app.patch(`${path}/:id`, (c) => controller.update(c));
|
|
4385
|
+
app.delete(`${path}/:id`, (c) => controller.delete(c));
|
|
4042
4386
|
app.post(`${path}/seed`, (c) => controller.seed(c));
|
|
4043
4387
|
if (collection.auth) {
|
|
4044
|
-
app.post(`${path}/:id/change-password`, requireAuth(), (c) => controller.changePassword(c));
|
|
4388
|
+
app.post(`${path}/:id/change-password`, requireAuth(config), (c) => controller.changePassword(c));
|
|
4045
4389
|
}
|
|
4046
4390
|
if (collection.workflow) {
|
|
4047
|
-
app.post(`${path}/:id/transitions/:transition`, requireAuth(), (c) => controller.transition(c));
|
|
4048
|
-
app.get(`${path}/:id/workflow-history`, requireAuth(), (c) => controller.workflowHistory(c));
|
|
4391
|
+
app.post(`${path}/:id/transitions/:transition`, requireAuth(config), (c) => controller.transition(c));
|
|
4392
|
+
app.get(`${path}/:id/workflow-history`, requireAuth(config), (c) => controller.workflowHistory(c));
|
|
4049
4393
|
}
|
|
4050
4394
|
}
|
|
4051
4395
|
for (const global of config.globals) {
|
|
4052
4396
|
const path = `/api/globals/${global.slug}`;
|
|
4053
4397
|
const controller = new GlobalController(global);
|
|
4054
|
-
app.get(path,
|
|
4055
|
-
app.patch(path,
|
|
4398
|
+
app.get(path, (c) => controller.get(c));
|
|
4399
|
+
app.patch(path, (c) => controller.update(c));
|
|
4056
4400
|
app.post(`${path}/seed`, (c) => controller.seed(c));
|
|
4057
4401
|
}
|
|
4058
4402
|
const previewController = new PreviewController();
|
|
4059
|
-
app.post("/api/preview-token", requireAuth(), (c) => previewController.createToken(c));
|
|
4403
|
+
app.post("/api/preview-token", requireAuth(config), (c) => previewController.createToken(c));
|
|
4060
4404
|
app.get("/api/preview-data", (c) => previewController.getData(c));
|
|
4061
|
-
app.post("/api/collections/:slug/:id/transitions/:transition", requireAuth(), async (c) => {
|
|
4405
|
+
app.post("/api/collections/:slug/:id/transitions/:transition", requireAuth(config), async (c) => {
|
|
4062
4406
|
const slug = c.req.param("slug");
|
|
4063
4407
|
const siteId = c.req.header("X-Site-Id") || c.get("siteId");
|
|
4064
4408
|
const config2 = c.get("config");
|
|
@@ -4076,7 +4420,7 @@ function registerRoutes(app, config) {
|
|
|
4076
4420
|
const controller = new CollectionController(collection);
|
|
4077
4421
|
return controller.transition(c);
|
|
4078
4422
|
});
|
|
4079
|
-
app.get("/api/collections/:slug/:id/workflow-history", requireAuth(), async (c) => {
|
|
4423
|
+
app.get("/api/collections/:slug/:id/workflow-history", requireAuth(config), async (c) => {
|
|
4080
4424
|
const slug = c.req.param("slug");
|
|
4081
4425
|
const siteId = c.req.header("X-Site-Id") || c.get("siteId");
|
|
4082
4426
|
const config2 = c.get("config");
|
|
@@ -4403,7 +4747,7 @@ async function createDyrectedApp(rawConfig) {
|
|
|
4403
4747
|
await config.db.sync(config.collections, config.globals);
|
|
4404
4748
|
}
|
|
4405
4749
|
app.use("*", (0, import_request_id.requestId)());
|
|
4406
|
-
app.use("*", optionalAuth());
|
|
4750
|
+
app.use("*", optionalAuth(config));
|
|
4407
4751
|
app.use("*", async (c, next) => {
|
|
4408
4752
|
const start = Date.now();
|
|
4409
4753
|
await next();
|