@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.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,148 @@ 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
|
+
function resolveFieldAccessId(context) {
|
|
717
|
+
const candidate = context.id ?? context.doc?.id ?? context.data?.id;
|
|
718
|
+
return typeof candidate === "string" && candidate.length > 0 ? candidate : void 0;
|
|
719
|
+
}
|
|
720
|
+
async function applyFieldReadAccess(context, doc) {
|
|
721
|
+
if (!doc || typeof doc !== "object") return doc;
|
|
722
|
+
const result = { ...doc };
|
|
723
|
+
for (const field of context.fields) {
|
|
724
|
+
if (field.type === "row" && field.fields) {
|
|
725
|
+
Object.assign(result, await applyFieldReadAccess({ ...context, fields: field.fields }, result));
|
|
726
|
+
continue;
|
|
727
|
+
}
|
|
728
|
+
if (!field.name) continue;
|
|
729
|
+
const value = result[field.name];
|
|
730
|
+
const canRead = await resolveBooleanAccess(context.config, field.access?.read, {
|
|
731
|
+
user: context.user,
|
|
732
|
+
req: context.req,
|
|
733
|
+
doc: context.doc,
|
|
734
|
+
data: context.data,
|
|
735
|
+
id: resolveFieldAccessId(context)
|
|
736
|
+
});
|
|
737
|
+
if (!canRead) {
|
|
738
|
+
delete result[field.name];
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
if (value === void 0 || value === null) continue;
|
|
742
|
+
if (field.type === "object" && field.fields && typeof value === "object" && !Array.isArray(value)) {
|
|
743
|
+
result[field.name] = await applyFieldReadAccess({ ...context, fields: field.fields }, value);
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
if (field.type === "array" && field.fields && Array.isArray(value)) {
|
|
747
|
+
const childFields = field.fields;
|
|
748
|
+
result[field.name] = await Promise.all(
|
|
749
|
+
value.map(
|
|
750
|
+
(item) => typeof item === "object" && item !== null ? applyFieldReadAccess({ ...context, fields: childFields }, item) : item
|
|
751
|
+
)
|
|
752
|
+
);
|
|
753
|
+
continue;
|
|
754
|
+
}
|
|
755
|
+
if (field.type === "blocks" && field.blocks && Array.isArray(value)) {
|
|
756
|
+
result[field.name] = await Promise.all(
|
|
757
|
+
value.map(async (item) => {
|
|
758
|
+
if (typeof item !== "object" || item === null) return item;
|
|
759
|
+
const typedItem = item;
|
|
760
|
+
const block = field.blocks?.find((candidate) => candidate.slug === typedItem.blockType);
|
|
761
|
+
if (!block || !block.fields) return item;
|
|
762
|
+
return applyFieldReadAccess({ ...context, fields: block.fields }, typedItem);
|
|
763
|
+
})
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
return result;
|
|
768
|
+
}
|
|
769
|
+
async function applyFieldWriteAccess(context, data) {
|
|
770
|
+
if (!data || typeof data !== "object") return data;
|
|
771
|
+
const result = { ...data };
|
|
772
|
+
for (const field of context.fields) {
|
|
773
|
+
if (field.type === "row" && field.fields) {
|
|
774
|
+
Object.assign(result, await applyFieldWriteAccess({ ...context, fields: field.fields }, result));
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
777
|
+
if (!field.name || !(field.name in result)) continue;
|
|
778
|
+
const canUpdate = await resolveBooleanAccess(context.config, field.access?.update, {
|
|
779
|
+
user: context.user,
|
|
780
|
+
req: context.req,
|
|
781
|
+
doc: context.doc,
|
|
782
|
+
data: context.data,
|
|
783
|
+
id: resolveFieldAccessId(context)
|
|
784
|
+
});
|
|
785
|
+
if (!canUpdate) {
|
|
786
|
+
delete result[field.name];
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
const value = result[field.name];
|
|
790
|
+
if (value === void 0 || value === null) continue;
|
|
791
|
+
if (field.type === "object" && field.fields && typeof value === "object" && !Array.isArray(value)) {
|
|
792
|
+
result[field.name] = await applyFieldWriteAccess({ ...context, fields: field.fields }, value);
|
|
793
|
+
continue;
|
|
794
|
+
}
|
|
795
|
+
if (field.type === "array" && field.fields && Array.isArray(value)) {
|
|
796
|
+
const childFields = field.fields;
|
|
797
|
+
result[field.name] = await Promise.all(
|
|
798
|
+
value.map(
|
|
799
|
+
(item) => typeof item === "object" && item !== null ? applyFieldWriteAccess({ ...context, fields: childFields }, item) : item
|
|
800
|
+
)
|
|
801
|
+
);
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
if (field.type === "blocks" && field.blocks && Array.isArray(value)) {
|
|
805
|
+
result[field.name] = await Promise.all(
|
|
806
|
+
value.map(async (item) => {
|
|
807
|
+
if (typeof item !== "object" || item === null) return item;
|
|
808
|
+
const typedItem = item;
|
|
809
|
+
const block = field.blocks?.find((candidate) => candidate.slug === typedItem.blockType);
|
|
810
|
+
if (!block || !block.fields) return item;
|
|
811
|
+
return applyFieldWriteAccess({ ...context, fields: block.fields }, typedItem);
|
|
812
|
+
})
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
return result;
|
|
817
|
+
}
|
|
818
|
+
function mergeWhereConstraint(where, constraint) {
|
|
819
|
+
return where ? { AND: [where, constraint] } : constraint;
|
|
820
|
+
}
|
|
821
|
+
|
|
644
822
|
// src/workflows.ts
|
|
645
823
|
var WORKFLOW_HISTORY_COLLECTION = "__workflow_history";
|
|
646
824
|
var LIFECYCLE_EVENTS_COLLECTION = "__lifecycle_events";
|
|
@@ -879,11 +1057,23 @@ var CollectionController = class {
|
|
|
879
1057
|
return config.adminAuth?.providers?.find((p) => p.members) || null;
|
|
880
1058
|
}
|
|
881
1059
|
toHookRequestContext(c) {
|
|
882
|
-
return
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
1060
|
+
return toHookRequestContext(c.req);
|
|
1061
|
+
}
|
|
1062
|
+
async evaluateAccess(c, action, options = {}) {
|
|
1063
|
+
const config = c.get("config");
|
|
1064
|
+
return resolveCollectionAccess(
|
|
1065
|
+
config,
|
|
1066
|
+
this.collection.slug,
|
|
1067
|
+
action,
|
|
1068
|
+
this.collection.access?.[action],
|
|
1069
|
+
{
|
|
1070
|
+
id: options.id,
|
|
1071
|
+
user: c.get("user"),
|
|
1072
|
+
req: this.toHookRequestContext(c),
|
|
1073
|
+
doc: options.doc ?? void 0,
|
|
1074
|
+
data: options.data
|
|
1075
|
+
}
|
|
1076
|
+
);
|
|
887
1077
|
}
|
|
888
1078
|
async find(c) {
|
|
889
1079
|
const config = c.get("config");
|
|
@@ -963,6 +1153,13 @@ var CollectionController = class {
|
|
|
963
1153
|
if (this.collection.workflow && !canViewWorkflowDraft(this.collection.workflow, user)) {
|
|
964
1154
|
where = where ? { AND: [where, { __published: { exists: true } }] } : { __published: { exists: true } };
|
|
965
1155
|
}
|
|
1156
|
+
const access = await this.evaluateAccess(c, "read");
|
|
1157
|
+
if (!access.allowed) {
|
|
1158
|
+
return c.json({ error: true, message: `Access denied: read on ${this.collection.slug}` }, 403);
|
|
1159
|
+
}
|
|
1160
|
+
if (access.constraint) {
|
|
1161
|
+
where = mergeWhereConstraint(where, access.constraint);
|
|
1162
|
+
}
|
|
966
1163
|
let result = await db.find({
|
|
967
1164
|
collection: this.collection.slug,
|
|
968
1165
|
limit,
|
|
@@ -995,7 +1192,14 @@ var CollectionController = class {
|
|
|
995
1192
|
db: readonlyDb
|
|
996
1193
|
});
|
|
997
1194
|
const docWithFieldHooks = await executeFieldAfterRead(this.collection.fields, docWithCollectionHooks, user, readonlyDb);
|
|
998
|
-
|
|
1195
|
+
const docWithFieldAccess = await applyFieldReadAccess({
|
|
1196
|
+
config,
|
|
1197
|
+
fields: this.collection.fields,
|
|
1198
|
+
user,
|
|
1199
|
+
req: this.toHookRequestContext(c),
|
|
1200
|
+
doc: docWithFieldHooks
|
|
1201
|
+
}, docWithFieldHooks);
|
|
1202
|
+
processedDocs.push(docWithFieldAccess);
|
|
999
1203
|
}
|
|
1000
1204
|
result.docs = processedDocs;
|
|
1001
1205
|
if (depth > 0) {
|
|
@@ -1040,6 +1244,10 @@ var CollectionController = class {
|
|
|
1040
1244
|
doc = materializeWorkflowDocument(doc, this.collection.workflow, user);
|
|
1041
1245
|
if (!doc) return c.json({ message: "Not Found" }, 404);
|
|
1042
1246
|
}
|
|
1247
|
+
const access = await this.evaluateAccess(c, "read", { id, doc });
|
|
1248
|
+
if (!access.allowed) {
|
|
1249
|
+
return c.json({ error: true, message: `Access denied: read on ${this.collection.slug}` }, 403);
|
|
1250
|
+
}
|
|
1043
1251
|
const docWithDefaults = DefaultsService.apply(this.collection.fields, doc);
|
|
1044
1252
|
const docWithCollectionHooks = await runCollectionHooks(this.collection.hooks?.afterRead, {
|
|
1045
1253
|
doc: docWithDefaults,
|
|
@@ -1048,17 +1256,24 @@ var CollectionController = class {
|
|
|
1048
1256
|
db: readonlyDb
|
|
1049
1257
|
});
|
|
1050
1258
|
const docWithFieldHooks = await executeFieldAfterRead(this.collection.fields, docWithCollectionHooks, user, readonlyDb);
|
|
1051
|
-
|
|
1259
|
+
const docWithFieldAccess = await applyFieldReadAccess({
|
|
1260
|
+
config,
|
|
1261
|
+
fields: this.collection.fields,
|
|
1262
|
+
user,
|
|
1263
|
+
req: this.toHookRequestContext(c),
|
|
1264
|
+
doc: docWithFieldHooks
|
|
1265
|
+
}, docWithFieldHooks);
|
|
1266
|
+
if (depth > 0 && docWithFieldAccess) {
|
|
1052
1267
|
const populationService = new PopulationService(db, config.collections);
|
|
1053
1268
|
const populatedDoc = await populationService.populate({
|
|
1054
|
-
data:
|
|
1269
|
+
data: docWithFieldAccess,
|
|
1055
1270
|
fields: this.collection.fields,
|
|
1056
1271
|
currentDepth: 0,
|
|
1057
1272
|
maxDepth: depth
|
|
1058
1273
|
});
|
|
1059
1274
|
return c.json(populatedDoc);
|
|
1060
1275
|
}
|
|
1061
|
-
return c.json(
|
|
1276
|
+
return c.json(docWithFieldAccess);
|
|
1062
1277
|
}
|
|
1063
1278
|
async create(c) {
|
|
1064
1279
|
const config = c.get("config");
|
|
@@ -1097,9 +1312,20 @@ var CollectionController = class {
|
|
|
1097
1312
|
if (this.collection.workflow) {
|
|
1098
1313
|
data = initializeWorkflowDocument(data, this.collection.workflow);
|
|
1099
1314
|
}
|
|
1315
|
+
const createAccess = await this.evaluateAccess(c, "create", { data });
|
|
1316
|
+
if (!createAccess.allowed) {
|
|
1317
|
+
return c.json({ error: true, message: `Access denied: create on ${this.collection.slug}` }, 403);
|
|
1318
|
+
}
|
|
1100
1319
|
if (this.collection.auth && data.password) {
|
|
1101
1320
|
data.password = await hashPassword(data.password);
|
|
1102
1321
|
}
|
|
1322
|
+
data = await applyFieldWriteAccess({
|
|
1323
|
+
config,
|
|
1324
|
+
fields: this.collection.fields,
|
|
1325
|
+
user,
|
|
1326
|
+
req: this.toHookRequestContext(c),
|
|
1327
|
+
data
|
|
1328
|
+
}, data);
|
|
1103
1329
|
data = await executeFieldBeforeChange(this.collection.fields, data, null, user, readonlyDb);
|
|
1104
1330
|
data = await runCollectionHooks(this.collection.hooks?.beforeChange, {
|
|
1105
1331
|
data,
|
|
@@ -1134,7 +1360,14 @@ var CollectionController = class {
|
|
|
1134
1360
|
db: readonlyDb
|
|
1135
1361
|
});
|
|
1136
1362
|
const finalDoc = await executeFieldAfterRead(this.collection.fields, readDoc, user, readonlyDb);
|
|
1137
|
-
|
|
1363
|
+
const accessibleDoc = await applyFieldReadAccess({
|
|
1364
|
+
config,
|
|
1365
|
+
fields: this.collection.fields,
|
|
1366
|
+
user,
|
|
1367
|
+
req: this.toHookRequestContext(c),
|
|
1368
|
+
doc: finalDoc
|
|
1369
|
+
}, finalDoc);
|
|
1370
|
+
return c.json(accessibleDoc, 201);
|
|
1138
1371
|
}
|
|
1139
1372
|
async upload(c) {
|
|
1140
1373
|
const config = c.get("config");
|
|
@@ -1177,6 +1410,17 @@ var CollectionController = class {
|
|
|
1177
1410
|
createdBy: user?.sub ?? null,
|
|
1178
1411
|
updatedBy: user?.sub ?? null
|
|
1179
1412
|
};
|
|
1413
|
+
const createAccess = await this.evaluateAccess(c, "create", { data });
|
|
1414
|
+
if (!createAccess.allowed) {
|
|
1415
|
+
return c.json({ error: true, message: `Access denied: create on ${this.collection.slug}` }, 403);
|
|
1416
|
+
}
|
|
1417
|
+
data = await applyFieldWriteAccess({
|
|
1418
|
+
config,
|
|
1419
|
+
fields: this.collection.fields,
|
|
1420
|
+
user,
|
|
1421
|
+
req: this.toHookRequestContext(c),
|
|
1422
|
+
data
|
|
1423
|
+
}, data);
|
|
1180
1424
|
data = await executeFieldBeforeChange(this.collection.fields, data, null, user, readonlyDb);
|
|
1181
1425
|
data = await runCollectionHooks(this.collection.hooks?.beforeChange, {
|
|
1182
1426
|
data,
|
|
@@ -1204,7 +1448,14 @@ var CollectionController = class {
|
|
|
1204
1448
|
db: readonlyDb
|
|
1205
1449
|
});
|
|
1206
1450
|
const finalDoc = await executeFieldAfterRead(this.collection.fields, readDoc, user, readonlyDb);
|
|
1207
|
-
|
|
1451
|
+
const accessibleDoc = await applyFieldReadAccess({
|
|
1452
|
+
config,
|
|
1453
|
+
fields: this.collection.fields,
|
|
1454
|
+
user,
|
|
1455
|
+
req: this.toHookRequestContext(c),
|
|
1456
|
+
doc: finalDoc
|
|
1457
|
+
}, finalDoc);
|
|
1458
|
+
return c.json(accessibleDoc, 201);
|
|
1208
1459
|
}
|
|
1209
1460
|
async update(c) {
|
|
1210
1461
|
const config = c.get("config");
|
|
@@ -1242,10 +1493,22 @@ var CollectionController = class {
|
|
|
1242
1493
|
});
|
|
1243
1494
|
const originalDoc = await db.findOne({ collection: this.collection.slug, id });
|
|
1244
1495
|
if (!originalDoc) return c.json({ message: "Not Found" }, 404);
|
|
1496
|
+
const updateAccess = await this.evaluateAccess(c, "update", { id, doc: originalDoc, data });
|
|
1497
|
+
if (!updateAccess.allowed) {
|
|
1498
|
+
return c.json({ error: true, message: `Access denied: update on ${this.collection.slug}` }, 403);
|
|
1499
|
+
}
|
|
1245
1500
|
let before = null;
|
|
1246
1501
|
if (this.collection.audit) {
|
|
1247
1502
|
before = originalDoc;
|
|
1248
1503
|
}
|
|
1504
|
+
data = await applyFieldWriteAccess({
|
|
1505
|
+
config,
|
|
1506
|
+
fields: this.collection.fields,
|
|
1507
|
+
user,
|
|
1508
|
+
req: this.toHookRequestContext(c),
|
|
1509
|
+
doc: originalDoc,
|
|
1510
|
+
data
|
|
1511
|
+
}, data);
|
|
1249
1512
|
data = await executeFieldBeforeChange(this.collection.fields, data, originalDoc, user, readonlyDb);
|
|
1250
1513
|
data = await runCollectionHooks(this.collection.hooks?.beforeChange, {
|
|
1251
1514
|
data,
|
|
@@ -1281,7 +1544,14 @@ var CollectionController = class {
|
|
|
1281
1544
|
db: readonlyDb
|
|
1282
1545
|
});
|
|
1283
1546
|
const finalDoc = await executeFieldAfterRead(this.collection.fields, readDoc, user, readonlyDb);
|
|
1284
|
-
|
|
1547
|
+
const accessibleDoc = await applyFieldReadAccess({
|
|
1548
|
+
config,
|
|
1549
|
+
fields: this.collection.fields,
|
|
1550
|
+
user,
|
|
1551
|
+
req: this.toHookRequestContext(c),
|
|
1552
|
+
doc: finalDoc
|
|
1553
|
+
}, finalDoc);
|
|
1554
|
+
return c.json(accessibleDoc);
|
|
1285
1555
|
}
|
|
1286
1556
|
async transition(c) {
|
|
1287
1557
|
const config = c.get("config");
|
|
@@ -1318,7 +1588,7 @@ var CollectionController = class {
|
|
|
1318
1588
|
const readAccess = this.collection.access?.read;
|
|
1319
1589
|
if (readAccess !== void 0 && readAccess !== null) {
|
|
1320
1590
|
const args = { user: c.get("user"), req: c.req, doc: document };
|
|
1321
|
-
const result2 =
|
|
1591
|
+
const result2 = await resolveAccess(config, readAccess, args);
|
|
1322
1592
|
let allowed = result2 === true;
|
|
1323
1593
|
if (result2 && typeof result2 === "object") {
|
|
1324
1594
|
const match = await config.db.find({
|
|
@@ -1429,6 +1699,10 @@ var CollectionController = class {
|
|
|
1429
1699
|
const user = c.get("user");
|
|
1430
1700
|
const doc = await db.findOne({ collection: this.collection.slug, id });
|
|
1431
1701
|
if (!doc) return c.json({ message: "Not Found" }, 404);
|
|
1702
|
+
const deleteAccess = await this.evaluateAccess(c, "delete", { id, doc });
|
|
1703
|
+
if (!deleteAccess.allowed) {
|
|
1704
|
+
return c.json({ error: true, message: `Access denied: delete on ${this.collection.slug}` }, 403);
|
|
1705
|
+
}
|
|
1432
1706
|
let before = null;
|
|
1433
1707
|
if (this.collection.audit) {
|
|
1434
1708
|
before = doc;
|
|
@@ -1488,6 +1762,11 @@ var CollectionController = class {
|
|
|
1488
1762
|
failed.push({ id, error: "Not Found" });
|
|
1489
1763
|
continue;
|
|
1490
1764
|
}
|
|
1765
|
+
const deleteAccess = await this.evaluateAccess(c, "delete", { id, doc });
|
|
1766
|
+
if (!deleteAccess.allowed) {
|
|
1767
|
+
failed.push({ id, error: "Access denied" });
|
|
1768
|
+
continue;
|
|
1769
|
+
}
|
|
1491
1770
|
let before = null;
|
|
1492
1771
|
if (this.collection.audit) {
|
|
1493
1772
|
before = doc;
|
|
@@ -1581,6 +1860,14 @@ var GlobalController = class {
|
|
|
1581
1860
|
await db.updateGlobal({ slug: this.global.slug, data: this.global.initialData });
|
|
1582
1861
|
data = this.global.initialData;
|
|
1583
1862
|
}
|
|
1863
|
+
const canRead = await resolveBooleanAccess(config, this.global.access?.read, {
|
|
1864
|
+
user,
|
|
1865
|
+
req: toHookRequestContext(c.req),
|
|
1866
|
+
doc: data
|
|
1867
|
+
});
|
|
1868
|
+
if (!canRead) {
|
|
1869
|
+
return c.json({ error: true, message: `Access denied: read on ${this.global.slug}` }, 403);
|
|
1870
|
+
}
|
|
1584
1871
|
const dataWithDefaults = DefaultsService.apply(this.global.fields, data);
|
|
1585
1872
|
const docWithCollectionHooks = await runCollectionHooks(this.global.hooks?.afterRead, {
|
|
1586
1873
|
doc: dataWithDefaults,
|
|
@@ -1589,17 +1876,24 @@ var GlobalController = class {
|
|
|
1589
1876
|
db: readonlyDb
|
|
1590
1877
|
});
|
|
1591
1878
|
const docWithFieldHooks = await executeFieldAfterRead(this.global.fields, docWithCollectionHooks, user, readonlyDb);
|
|
1592
|
-
|
|
1879
|
+
const docWithFieldAccess = await applyFieldReadAccess({
|
|
1880
|
+
config,
|
|
1881
|
+
fields: this.global.fields,
|
|
1882
|
+
user,
|
|
1883
|
+
req: toHookRequestContext(c.req),
|
|
1884
|
+
doc: docWithFieldHooks
|
|
1885
|
+
}, docWithFieldHooks);
|
|
1886
|
+
if (depth > 0 && docWithFieldAccess) {
|
|
1593
1887
|
const populationService = new PopulationService(db, config.collections);
|
|
1594
1888
|
const populatedData = await populationService.populate({
|
|
1595
|
-
data:
|
|
1889
|
+
data: docWithFieldAccess,
|
|
1596
1890
|
fields: this.global.fields,
|
|
1597
1891
|
currentDepth: 0,
|
|
1598
1892
|
maxDepth: depth
|
|
1599
1893
|
});
|
|
1600
1894
|
return c.json(populatedData);
|
|
1601
1895
|
}
|
|
1602
|
-
return c.json(
|
|
1896
|
+
return c.json(docWithFieldAccess);
|
|
1603
1897
|
}
|
|
1604
1898
|
async update(c) {
|
|
1605
1899
|
const config = c.get("config");
|
|
@@ -1609,7 +1903,24 @@ var GlobalController = class {
|
|
|
1609
1903
|
const body = await c.req.json();
|
|
1610
1904
|
const user = c.get("user");
|
|
1611
1905
|
const originalDoc = await db.getGlobal({ slug: this.global.slug }) || {};
|
|
1612
|
-
|
|
1906
|
+
const canUpdate = await resolveBooleanAccess(config, this.global.access?.update, {
|
|
1907
|
+
user,
|
|
1908
|
+
req: toHookRequestContext(c.req),
|
|
1909
|
+
doc: originalDoc,
|
|
1910
|
+
data: body
|
|
1911
|
+
});
|
|
1912
|
+
if (!canUpdate) {
|
|
1913
|
+
return c.json({ error: true, message: `Access denied: update on ${this.global.slug}` }, 403);
|
|
1914
|
+
}
|
|
1915
|
+
let sanitizedBody = await applyFieldWriteAccess({
|
|
1916
|
+
config,
|
|
1917
|
+
fields: this.global.fields,
|
|
1918
|
+
user,
|
|
1919
|
+
req: toHookRequestContext(c.req),
|
|
1920
|
+
doc: originalDoc,
|
|
1921
|
+
data: body
|
|
1922
|
+
}, body);
|
|
1923
|
+
let data = await executeFieldBeforeChange(this.global.fields, sanitizedBody, originalDoc, user, readonlyDb);
|
|
1613
1924
|
data = await runCollectionHooks(this.global.hooks?.beforeChange, {
|
|
1614
1925
|
data,
|
|
1615
1926
|
doc: originalDoc,
|
|
@@ -1634,7 +1945,14 @@ var GlobalController = class {
|
|
|
1634
1945
|
db: readonlyDb
|
|
1635
1946
|
});
|
|
1636
1947
|
const finalDoc = await executeFieldAfterRead(this.global.fields, readDoc, user, readonlyDb);
|
|
1637
|
-
|
|
1948
|
+
const accessibleDoc = await applyFieldReadAccess({
|
|
1949
|
+
config,
|
|
1950
|
+
fields: this.global.fields,
|
|
1951
|
+
user,
|
|
1952
|
+
req: toHookRequestContext(c.req),
|
|
1953
|
+
doc: finalDoc
|
|
1954
|
+
}, finalDoc);
|
|
1955
|
+
return c.json(accessibleDoc);
|
|
1638
1956
|
}
|
|
1639
1957
|
async seed(c) {
|
|
1640
1958
|
const config = c.get("config");
|
|
@@ -1816,7 +2134,7 @@ var MediaController = class {
|
|
|
1816
2134
|
var import_jose = require("jose");
|
|
1817
2135
|
var import_node_util2 = require("util");
|
|
1818
2136
|
function getSecret() {
|
|
1819
|
-
const secret = process.env.DYRECTED_JWT_SECRET
|
|
2137
|
+
const secret = process.env.DYRECTED_JWT_SECRET;
|
|
1820
2138
|
if (!secret) {
|
|
1821
2139
|
throw new Error(
|
|
1822
2140
|
"[dyrected/core] DYRECTED_JWT_SECRET is not set. Add it to your environment variables to enable auth collections."
|
|
@@ -2686,7 +3004,7 @@ var AdminAuthController = class {
|
|
|
2686
3004
|
return state;
|
|
2687
3005
|
}
|
|
2688
3006
|
getStateSecret() {
|
|
2689
|
-
const secret = process.env.DYRECTED_JWT_SECRET
|
|
3007
|
+
const secret = process.env.DYRECTED_JWT_SECRET;
|
|
2690
3008
|
if (!secret) {
|
|
2691
3009
|
throw new Error("[dyrected/core] DYRECTED_JWT_SECRET is not set.");
|
|
2692
3010
|
}
|
|
@@ -2704,7 +3022,7 @@ var import_jose3 = require("jose");
|
|
|
2704
3022
|
var import_node_util4 = require("util");
|
|
2705
3023
|
var PreviewController = class {
|
|
2706
3024
|
getSecret() {
|
|
2707
|
-
const secret = process.env.DYRECTED_JWT_SECRET ||
|
|
3025
|
+
const secret = process.env.DYRECTED_JWT_SECRET || "dyrected-preview-secret-change-me";
|
|
2708
3026
|
return new import_node_util4.TextEncoder().encode(secret);
|
|
2709
3027
|
}
|
|
2710
3028
|
/**
|
|
@@ -2743,30 +3061,74 @@ var PreviewController = class {
|
|
|
2743
3061
|
};
|
|
2744
3062
|
|
|
2745
3063
|
// src/middleware/auth.ts
|
|
2746
|
-
function
|
|
3064
|
+
function getBearerToken(c) {
|
|
3065
|
+
const authHeader = c.req.header("Authorization");
|
|
3066
|
+
return authHeader?.replace(/^Bearer\s+/i, "") || void 0;
|
|
3067
|
+
}
|
|
3068
|
+
async function resolveUser(token, config) {
|
|
3069
|
+
const payload = await verifyCollectionToken(token);
|
|
3070
|
+
if (payload.purpose) {
|
|
3071
|
+
return payload;
|
|
3072
|
+
}
|
|
3073
|
+
const db = config?.db;
|
|
3074
|
+
if (!db) {
|
|
3075
|
+
return payload;
|
|
3076
|
+
}
|
|
3077
|
+
let doc;
|
|
3078
|
+
try {
|
|
3079
|
+
doc = await db.findOne({
|
|
3080
|
+
collection: payload.collection,
|
|
3081
|
+
id: payload.sub
|
|
3082
|
+
});
|
|
3083
|
+
} catch (err) {
|
|
3084
|
+
console.error("[dyrected/core] Failed to hydrate user from token:", err);
|
|
3085
|
+
return payload;
|
|
3086
|
+
}
|
|
3087
|
+
if (!doc) {
|
|
3088
|
+
return null;
|
|
3089
|
+
}
|
|
3090
|
+
const { password: _password, ...safe } = doc;
|
|
3091
|
+
return {
|
|
3092
|
+
...safe,
|
|
3093
|
+
sub: payload.sub,
|
|
3094
|
+
email: payload.email ?? safe.email,
|
|
3095
|
+
collection: payload.collection
|
|
3096
|
+
};
|
|
3097
|
+
}
|
|
3098
|
+
function requireAuth(config) {
|
|
2747
3099
|
return async (c, next) => {
|
|
2748
|
-
|
|
2749
|
-
|
|
3100
|
+
if (c.get("user")) {
|
|
3101
|
+
return next();
|
|
3102
|
+
}
|
|
3103
|
+
const token = getBearerToken(c);
|
|
2750
3104
|
if (!token) {
|
|
2751
3105
|
return c.json({ error: true, message: "Authentication required." }, 401);
|
|
2752
3106
|
}
|
|
3107
|
+
let user;
|
|
2753
3108
|
try {
|
|
2754
|
-
|
|
2755
|
-
c.set("user", user);
|
|
2756
|
-
await next();
|
|
3109
|
+
user = await resolveUser(token, config ?? c.get("config"));
|
|
2757
3110
|
} catch {
|
|
2758
3111
|
return c.json({ error: true, message: "Invalid or expired token." }, 401);
|
|
2759
3112
|
}
|
|
3113
|
+
if (!user) {
|
|
3114
|
+
return c.json({ error: true, message: "Invalid or expired token." }, 401);
|
|
3115
|
+
}
|
|
3116
|
+
c.set("user", user);
|
|
3117
|
+
await next();
|
|
2760
3118
|
};
|
|
2761
3119
|
}
|
|
2762
|
-
function optionalAuth() {
|
|
3120
|
+
function optionalAuth(config) {
|
|
2763
3121
|
return async (c, next) => {
|
|
2764
|
-
|
|
2765
|
-
|
|
3122
|
+
if (c.get("user")) {
|
|
3123
|
+
return next();
|
|
3124
|
+
}
|
|
3125
|
+
const token = getBearerToken(c);
|
|
2766
3126
|
if (token) {
|
|
2767
3127
|
try {
|
|
2768
|
-
const user = await
|
|
2769
|
-
|
|
3128
|
+
const user = await resolveUser(token, config ?? c.get("config"));
|
|
3129
|
+
if (user) {
|
|
3130
|
+
c.set("user", user);
|
|
3131
|
+
}
|
|
2770
3132
|
} catch {
|
|
2771
3133
|
}
|
|
2772
3134
|
}
|
|
@@ -3654,37 +4016,23 @@ function getSwaggerHtml(specUrl) {
|
|
|
3654
4016
|
}
|
|
3655
4017
|
|
|
3656
4018
|
// src/router.ts
|
|
3657
|
-
function accessGate(target, action) {
|
|
4019
|
+
function accessGate(config, target, action) {
|
|
3658
4020
|
return async (c, next) => {
|
|
3659
4021
|
const user = c.get("user");
|
|
3660
4022
|
const accessExpr = target.access?.[action];
|
|
3661
4023
|
if (accessExpr === void 0 || accessExpr === null) {
|
|
3662
4024
|
return await next();
|
|
3663
4025
|
}
|
|
3664
|
-
const
|
|
3665
|
-
|
|
4026
|
+
const allowed = await resolveBooleanAccess(config, accessExpr, {
|
|
4027
|
+
user,
|
|
4028
|
+
req: toHookRequestContext(c.req)
|
|
4029
|
+
});
|
|
3666
4030
|
if (!allowed) {
|
|
3667
4031
|
return c.json({ error: true, message: `Access denied: ${action} on ${target.slug}` }, 403);
|
|
3668
4032
|
}
|
|
3669
4033
|
await next();
|
|
3670
4034
|
};
|
|
3671
4035
|
}
|
|
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
4036
|
function serializeFieldForApi(f) {
|
|
3689
4037
|
if (!f) return f;
|
|
3690
4038
|
const serialized = { ...f };
|
|
@@ -3713,7 +4061,7 @@ function serializeFieldForApi(f) {
|
|
|
3713
4061
|
return serialized;
|
|
3714
4062
|
}
|
|
3715
4063
|
function registerRoutes(app, config) {
|
|
3716
|
-
app.get("/api/schemas", optionalAuth(), async (c) => {
|
|
4064
|
+
app.get("/api/schemas", optionalAuth(config), async (c) => {
|
|
3717
4065
|
const siteId = c.req.header("X-Site-Id");
|
|
3718
4066
|
let collections = [...config.collections];
|
|
3719
4067
|
let globals = [...config.globals];
|
|
@@ -3729,11 +4077,11 @@ function registerRoutes(app, config) {
|
|
|
3729
4077
|
}
|
|
3730
4078
|
}
|
|
3731
4079
|
const user = c.get("user");
|
|
3732
|
-
const accessArgs = { user, req: c.req
|
|
4080
|
+
const accessArgs = { user, req: toHookRequestContext(c.req) };
|
|
3733
4081
|
const serializeAccess = async (access) => {
|
|
3734
4082
|
if (typeof access === "string") return access;
|
|
3735
4083
|
if (typeof access === "boolean") return access;
|
|
3736
|
-
return
|
|
4084
|
+
return resolveBooleanAccess(config, access, accessArgs);
|
|
3737
4085
|
};
|
|
3738
4086
|
const filteredCollections = await Promise.all(collections.filter((col) => !siteId || col.shared || !col.siteId || col.siteId === siteId).map(async (col) => ({
|
|
3739
4087
|
slug: col.slug,
|
|
@@ -3819,7 +4167,7 @@ function registerRoutes(app, config) {
|
|
|
3819
4167
|
adminAuth: getPublicAdminAuthConfig(config.adminAuth, collections)
|
|
3820
4168
|
});
|
|
3821
4169
|
});
|
|
3822
|
-
app.get("/api/dyrected/options/:collection/:field", optionalAuth(), async (c) => {
|
|
4170
|
+
app.get("/api/dyrected/options/:collection/:field", optionalAuth(config), async (c) => {
|
|
3823
4171
|
const { collection: colSlug, field: fieldName } = c.req.param();
|
|
3824
4172
|
const siteId = c.req.header("X-Site-Id");
|
|
3825
4173
|
let collections = [...config.collections];
|
|
@@ -3833,8 +4181,10 @@ function registerRoutes(app, config) {
|
|
|
3833
4181
|
if (collection) {
|
|
3834
4182
|
const accessExpr = collection.access?.read;
|
|
3835
4183
|
if (accessExpr !== void 0 && accessExpr !== null) {
|
|
3836
|
-
const
|
|
3837
|
-
|
|
4184
|
+
const allowed = await resolveBooleanAccess(config, accessExpr, {
|
|
4185
|
+
user,
|
|
4186
|
+
req: toHookRequestContext(c.req)
|
|
4187
|
+
});
|
|
3838
4188
|
if (!allowed) {
|
|
3839
4189
|
return c.json({ error: true, message: `Access denied: read on ${colSlug}` }, 403);
|
|
3840
4190
|
}
|
|
@@ -3852,8 +4202,10 @@ function registerRoutes(app, config) {
|
|
|
3852
4202
|
}
|
|
3853
4203
|
const accessExpr = glb.access?.read;
|
|
3854
4204
|
if (accessExpr !== void 0 && accessExpr !== null) {
|
|
3855
|
-
const
|
|
3856
|
-
|
|
4205
|
+
const allowed = await resolveBooleanAccess(config, accessExpr, {
|
|
4206
|
+
user,
|
|
4207
|
+
req: toHookRequestContext(c.req)
|
|
4208
|
+
});
|
|
3857
4209
|
if (!allowed) {
|
|
3858
4210
|
return c.json({ error: true, message: `Access denied: read on global ${colSlug}` }, 403);
|
|
3859
4211
|
}
|
|
@@ -3897,7 +4249,7 @@ function registerRoutes(app, config) {
|
|
|
3897
4249
|
app.get("/api/docs", (c) => {
|
|
3898
4250
|
return c.html(getSwaggerHtml());
|
|
3899
4251
|
});
|
|
3900
|
-
app.get("/api/preferences/:key", requireAuth(), async (c) => {
|
|
4252
|
+
app.get("/api/preferences/:key", requireAuth(config), async (c) => {
|
|
3901
4253
|
const db = config.db;
|
|
3902
4254
|
const user = c.get("user");
|
|
3903
4255
|
const key = c.req.param("key");
|
|
@@ -3922,7 +4274,7 @@ function registerRoutes(app, config) {
|
|
|
3922
4274
|
const globalValue = await getGlobalPreference();
|
|
3923
4275
|
return c.json({ key, value: globalValue });
|
|
3924
4276
|
});
|
|
3925
|
-
app.put("/api/preferences/:key", requireAuth(), async (c) => {
|
|
4277
|
+
app.put("/api/preferences/:key", requireAuth(config), async (c) => {
|
|
3926
4278
|
const db = config.db;
|
|
3927
4279
|
const user = c.get("user");
|
|
3928
4280
|
const key = c.req.param("key");
|
|
@@ -3962,7 +4314,7 @@ function registerRoutes(app, config) {
|
|
|
3962
4314
|
});
|
|
3963
4315
|
return c.json({ key, value: body.value });
|
|
3964
4316
|
});
|
|
3965
|
-
app.delete("/api/preferences/:key", requireAuth(), async (c) => {
|
|
4317
|
+
app.delete("/api/preferences/:key", requireAuth(config), async (c) => {
|
|
3966
4318
|
const db = config.db;
|
|
3967
4319
|
const user = c.get("user");
|
|
3968
4320
|
const key = c.req.param("key");
|
|
@@ -4002,10 +4354,10 @@ function registerRoutes(app, config) {
|
|
|
4002
4354
|
for (const col of uploadCollections) {
|
|
4003
4355
|
const mediaController = new MediaController(col.slug);
|
|
4004
4356
|
const prefix = `/api/collections/${col.slug}`;
|
|
4005
|
-
app.get(`${prefix}/media`, accessGate(col, "read"), (c) => mediaController.find(c));
|
|
4357
|
+
app.get(`${prefix}/media`, accessGate(config, col, "read"), (c) => mediaController.find(c));
|
|
4006
4358
|
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));
|
|
4359
|
+
app.post(`${prefix}/media`, accessGate(config, col, "create"), (c) => mediaController.upload(c));
|
|
4360
|
+
app.delete(`${prefix}/media/:id`, accessGate(config, col, "delete"), (c) => mediaController.delete(c));
|
|
4009
4361
|
}
|
|
4010
4362
|
}
|
|
4011
4363
|
const adminAuthController = new AdminAuthController(config);
|
|
@@ -4022,43 +4374,43 @@ function registerRoutes(app, config) {
|
|
|
4022
4374
|
app.post(`${path}/logout`, (c) => authController.logout(c));
|
|
4023
4375
|
app.get(`${path}/init`, (c) => authController.init(c));
|
|
4024
4376
|
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));
|
|
4377
|
+
app.get(`${path}/me`, requireAuth(config), (c) => authController.me(c));
|
|
4378
|
+
app.post(`${path}/refresh-token`, requireAuth(config), (c) => authController.refreshToken(c));
|
|
4027
4379
|
app.post(`${path}/forgot-password`, (c) => authController.forgotPassword(c));
|
|
4028
4380
|
app.post(`${path}/reset-password`, (c) => authController.resetPassword(c));
|
|
4029
|
-
app.post(`${path}/invite`, requireAuth(), (c) => authController.invite(c));
|
|
4381
|
+
app.post(`${path}/invite`, requireAuth(config), (c) => authController.invite(c));
|
|
4030
4382
|
app.post(`${path}/accept-invite`, (c) => authController.acceptInvite(c));
|
|
4031
4383
|
}
|
|
4032
4384
|
for (const collection of config.collections) {
|
|
4033
4385
|
const path = `/api/collections/${collection.slug}`;
|
|
4034
4386
|
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`,
|
|
4387
|
+
app.get(path, (c) => controller.find(c));
|
|
4388
|
+
app.post(path, (c) => controller.create(c));
|
|
4389
|
+
app.post(`${path}/media`, (c) => controller.create(c));
|
|
4390
|
+
app.delete(`${path}/delete-many`, (c) => controller.deleteMany(c));
|
|
4391
|
+
app.get(`${path}/:id`, (c) => controller.findOne(c));
|
|
4392
|
+
app.patch(`${path}/:id`, (c) => controller.update(c));
|
|
4393
|
+
app.delete(`${path}/:id`, (c) => controller.delete(c));
|
|
4042
4394
|
app.post(`${path}/seed`, (c) => controller.seed(c));
|
|
4043
4395
|
if (collection.auth) {
|
|
4044
|
-
app.post(`${path}/:id/change-password`, requireAuth(), (c) => controller.changePassword(c));
|
|
4396
|
+
app.post(`${path}/:id/change-password`, requireAuth(config), (c) => controller.changePassword(c));
|
|
4045
4397
|
}
|
|
4046
4398
|
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));
|
|
4399
|
+
app.post(`${path}/:id/transitions/:transition`, requireAuth(config), (c) => controller.transition(c));
|
|
4400
|
+
app.get(`${path}/:id/workflow-history`, requireAuth(config), (c) => controller.workflowHistory(c));
|
|
4049
4401
|
}
|
|
4050
4402
|
}
|
|
4051
4403
|
for (const global of config.globals) {
|
|
4052
4404
|
const path = `/api/globals/${global.slug}`;
|
|
4053
4405
|
const controller = new GlobalController(global);
|
|
4054
|
-
app.get(path,
|
|
4055
|
-
app.patch(path,
|
|
4406
|
+
app.get(path, (c) => controller.get(c));
|
|
4407
|
+
app.patch(path, (c) => controller.update(c));
|
|
4056
4408
|
app.post(`${path}/seed`, (c) => controller.seed(c));
|
|
4057
4409
|
}
|
|
4058
4410
|
const previewController = new PreviewController();
|
|
4059
|
-
app.post("/api/preview-token", requireAuth(), (c) => previewController.createToken(c));
|
|
4411
|
+
app.post("/api/preview-token", requireAuth(config), (c) => previewController.createToken(c));
|
|
4060
4412
|
app.get("/api/preview-data", (c) => previewController.getData(c));
|
|
4061
|
-
app.post("/api/collections/:slug/:id/transitions/:transition", requireAuth(), async (c) => {
|
|
4413
|
+
app.post("/api/collections/:slug/:id/transitions/:transition", requireAuth(config), async (c) => {
|
|
4062
4414
|
const slug = c.req.param("slug");
|
|
4063
4415
|
const siteId = c.req.header("X-Site-Id") || c.get("siteId");
|
|
4064
4416
|
const config2 = c.get("config");
|
|
@@ -4076,7 +4428,7 @@ function registerRoutes(app, config) {
|
|
|
4076
4428
|
const controller = new CollectionController(collection);
|
|
4077
4429
|
return controller.transition(c);
|
|
4078
4430
|
});
|
|
4079
|
-
app.get("/api/collections/:slug/:id/workflow-history", requireAuth(), async (c) => {
|
|
4431
|
+
app.get("/api/collections/:slug/:id/workflow-history", requireAuth(config), async (c) => {
|
|
4080
4432
|
const slug = c.req.param("slug");
|
|
4081
4433
|
const siteId = c.req.header("X-Site-Id") || c.get("siteId");
|
|
4082
4434
|
const config2 = c.get("config");
|
|
@@ -4403,7 +4755,7 @@ async function createDyrectedApp(rawConfig) {
|
|
|
4403
4755
|
await config.db.sync(config.collections, config.globals);
|
|
4404
4756
|
}
|
|
4405
4757
|
app.use("*", (0, import_request_id.requestId)());
|
|
4406
|
-
app.use("*", optionalAuth());
|
|
4758
|
+
app.use("*", optionalAuth(config));
|
|
4407
4759
|
app.use("*", async (c, next) => {
|
|
4408
4760
|
const start = Date.now();
|
|
4409
4761
|
await next();
|