@dyrected/core 2.5.61 → 2.5.63
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-BcuZIpvL.d.cts +1951 -0
- package/dist/app-config-BcuZIpvL.d.ts +1951 -0
- package/dist/app-config-Bm9-2Am6.d.cts +1875 -0
- package/dist/app-config-Bm9-2Am6.d.ts +1875 -0
- package/dist/app-config-CJAGGPrk.d.cts +1900 -0
- package/dist/app-config-CJAGGPrk.d.ts +1900 -0
- package/dist/app-config-CiEDJm0e.d.cts +1912 -0
- package/dist/app-config-CiEDJm0e.d.ts +1912 -0
- package/dist/app-config-DCDh8Gbx.d.cts +1926 -0
- package/dist/app-config-DCDh8Gbx.d.ts +1926 -0
- package/dist/app-config-Dv5XACR4.d.cts +2065 -0
- package/dist/app-config-Dv5XACR4.d.ts +2065 -0
- package/dist/app-config-_kkj71CB.d.cts +2010 -0
- package/dist/app-config-_kkj71CB.d.ts +2010 -0
- package/dist/app-config-tITj_0sn.d.cts +1926 -0
- package/dist/app-config-tITj_0sn.d.ts +1926 -0
- package/dist/chunk-57FNM42D.js +2392 -0
- package/dist/chunk-BAMX7YUC.js +1815 -0
- package/dist/chunk-BQV3QW3Y.js +2588 -0
- package/dist/chunk-EH3MJGB5.js +2571 -0
- package/dist/chunk-M3HKRN7E.js +1665 -0
- package/dist/chunk-MQZH7RQC.js +1667 -0
- package/dist/chunk-T626OZMH.js +2602 -0
- package/dist/chunk-TEGRS6J7.js +2609 -0
- package/dist/chunk-VCYYBN5J.js +1873 -0
- package/dist/chunk-WVD7PORQ.js +1672 -0
- package/dist/chunk-XZLIBQSO.js +2397 -0
- package/dist/index.cjs +364 -45
- package/dist/index.d.cts +36 -4
- package/dist/index.d.ts +36 -4
- package/dist/index.js +5 -1
- package/dist/server.cjs +2981 -716
- package/dist/server.d.cts +124 -12
- package/dist/server.d.ts +124 -12
- package/dist/server.js +2085 -703
- package/package.json +10 -1
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -46,6 +56,7 @@ __export(index_exports, {
|
|
|
46
56
|
defineMultiSelectField: () => defineMultiSelectField,
|
|
47
57
|
defineNumberField: () => defineNumberField,
|
|
48
58
|
defineObjectField: () => defineObjectField,
|
|
59
|
+
definePublishingWorkflow: () => definePublishingWorkflow,
|
|
49
60
|
defineRadioField: () => defineRadioField,
|
|
50
61
|
defineRelationshipField: () => defineRelationshipField,
|
|
51
62
|
defineRichTextField: () => defineRichTextField,
|
|
@@ -70,6 +81,7 @@ __export(index_exports, {
|
|
|
70
81
|
parseMongoWhere: () => parseMongoWhere,
|
|
71
82
|
parseSort: () => parseSort,
|
|
72
83
|
parseSqlWhere: () => parseSqlWhere,
|
|
84
|
+
publishedStateName: () => publishedStateName,
|
|
73
85
|
publishingWorkflow: () => publishingWorkflow,
|
|
74
86
|
resolveAdminAuthCollection: () => resolveAdminAuthCollection,
|
|
75
87
|
runCollectionHooks: () => runCollectionHooks,
|
|
@@ -88,10 +100,47 @@ var LIFECYCLE_EVENT_NAMES = [
|
|
|
88
100
|
"entry.unpublished"
|
|
89
101
|
];
|
|
90
102
|
|
|
103
|
+
// src/auth/token.ts
|
|
104
|
+
var import_jose = require("jose");
|
|
105
|
+
var import_node_util = require("util");
|
|
106
|
+
|
|
107
|
+
// src/auth/sessions.ts
|
|
108
|
+
var AUTH_SESSIONS_COLLECTION = "__auth_sessions";
|
|
109
|
+
|
|
110
|
+
// src/observability.ts
|
|
111
|
+
var import_api = require("@opentelemetry/api");
|
|
112
|
+
var import_exporter_metrics_otlp_http = require("@opentelemetry/exporter-metrics-otlp-http");
|
|
113
|
+
var import_exporter_prometheus = require("@opentelemetry/exporter-prometheus");
|
|
114
|
+
var import_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otlp-http");
|
|
115
|
+
var import_resources = require("@opentelemetry/resources");
|
|
116
|
+
var import_sdk_metrics = require("@opentelemetry/sdk-metrics");
|
|
117
|
+
var import_sdk_trace_base = require("@opentelemetry/sdk-trace-base");
|
|
118
|
+
var import_pino = __toESM(require("pino"), 1);
|
|
119
|
+
var import_pino_pretty = __toESM(require("pino-pretty"), 1);
|
|
120
|
+
var import_node_stream = require("stream");
|
|
121
|
+
var runtimeByConfig = /* @__PURE__ */ new WeakMap();
|
|
122
|
+
var fallbackLogger = (0, import_pino.default)({
|
|
123
|
+
name: "dyrected",
|
|
124
|
+
enabled: process.env.DISABLE_LOGGING !== "true",
|
|
125
|
+
timestamp: import_pino.stdTimeFunctions.isoTime
|
|
126
|
+
});
|
|
127
|
+
function getObservabilityRuntime(config) {
|
|
128
|
+
if (!config || typeof config !== "object") return void 0;
|
|
129
|
+
return runtimeByConfig.get(config);
|
|
130
|
+
}
|
|
131
|
+
function getConfigLogger(config, component) {
|
|
132
|
+
const base = config?.logger && !("options" in config.logger) ? config.logger : fallbackLogger;
|
|
133
|
+
return base.child({ component });
|
|
134
|
+
}
|
|
135
|
+
|
|
91
136
|
// src/workflows.ts
|
|
92
137
|
var WORKFLOW_HISTORY_COLLECTION = "__workflow_history";
|
|
93
138
|
var LIFECYCLE_EVENTS_COLLECTION = "__lifecycle_events";
|
|
94
|
-
|
|
139
|
+
var EDITOR_CAPABILITIES = ["entry.edit", "entry.submit"];
|
|
140
|
+
var PUBLISHER_CAPABILITIES = ["entry.edit", "entry.submit", "entry.publish", "entry.unpublish"];
|
|
141
|
+
function definePublishingWorkflow(options = {}) {
|
|
142
|
+
const editors = options.editors ?? ["editor"];
|
|
143
|
+
const publishers = options.publishers ?? ["publisher", "admin"];
|
|
95
144
|
return {
|
|
96
145
|
initialState: "draft",
|
|
97
146
|
draftState: "draft",
|
|
@@ -107,12 +156,14 @@ function publishingWorkflow() {
|
|
|
107
156
|
{ name: "unpublish", label: "Unpublish", from: "published", to: "draft", requiredCapabilities: ["entry.unpublish"], unpublish: true }
|
|
108
157
|
],
|
|
109
158
|
roles: [
|
|
110
|
-
{ role
|
|
111
|
-
{ role
|
|
112
|
-
{ role: "admin", capabilities: ["entry.edit", "entry.submit", "entry.publish", "entry.unpublish"] }
|
|
159
|
+
...editors.map((role) => ({ role, capabilities: [...EDITOR_CAPABILITIES] })),
|
|
160
|
+
...publishers.map((role) => ({ role, capabilities: [...PUBLISHER_CAPABILITIES] }))
|
|
113
161
|
]
|
|
114
162
|
};
|
|
115
163
|
}
|
|
164
|
+
function publishingWorkflow() {
|
|
165
|
+
return definePublishingWorkflow();
|
|
166
|
+
}
|
|
116
167
|
function simplePublishingWorkflow() {
|
|
117
168
|
return {
|
|
118
169
|
initialState: "draft",
|
|
@@ -125,6 +176,9 @@ function simplePublishingWorkflow() {
|
|
|
125
176
|
{ name: "publish", label: "Publish", from: "draft", to: "published" },
|
|
126
177
|
{ name: "unpublish", label: "Unpublish", from: "published", to: "draft", unpublish: true }
|
|
127
178
|
]
|
|
179
|
+
// Intentionally no `roles`: this lightweight workflow (synthesized from
|
|
180
|
+
// `drafts: true`) does no capability-based gating. Draft visibility for a
|
|
181
|
+
// no-roles workflow is handled in `canViewWorkflowDraft`.
|
|
128
182
|
};
|
|
129
183
|
}
|
|
130
184
|
function publicMetadata(meta) {
|
|
@@ -143,9 +197,13 @@ function canViewWorkflowDraft(workflow, user) {
|
|
|
143
197
|
if (!user) return false;
|
|
144
198
|
const capabilities = workflowCapabilities(workflow, user);
|
|
145
199
|
if (capabilities.has("entry.edit")) return true;
|
|
146
|
-
|
|
200
|
+
if (workflow.transitions.some(
|
|
147
201
|
(transition) => (transition.requiredCapabilities ?? []).some((capability) => capabilities.has(capability))
|
|
148
|
-
)
|
|
202
|
+
)) {
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
if (!workflow.roles || workflow.roles.length === 0) return true;
|
|
206
|
+
return false;
|
|
149
207
|
}
|
|
150
208
|
function availableWorkflowTransitions(workflow, state, user) {
|
|
151
209
|
const capabilities = workflowCapabilities(workflow, user);
|
|
@@ -165,6 +223,7 @@ function publishedStateName(workflow) {
|
|
|
165
223
|
}
|
|
166
224
|
function materializeWorkflowDocument(doc, workflow, user) {
|
|
167
225
|
const meta = doc.__workflow;
|
|
226
|
+
const publishedState = publishedStateName(workflow);
|
|
168
227
|
if (!meta) {
|
|
169
228
|
const { __published: _legacyPublished, __workflow: _legacyWorkflow, ...working2 } = doc;
|
|
170
229
|
const state = publishedStateName(workflow);
|
|
@@ -179,7 +238,12 @@ function materializeWorkflowDocument(doc, workflow, user) {
|
|
|
179
238
|
}
|
|
180
239
|
const { __published, __workflow, ...working } = doc;
|
|
181
240
|
if (!canViewWorkflowDraft(workflow, user)) {
|
|
182
|
-
if (!__published || typeof __published !== "object")
|
|
241
|
+
if (!__published || typeof __published !== "object") {
|
|
242
|
+
if (meta.state === publishedState) {
|
|
243
|
+
return { ...working, _workflow: publicMetadata(meta) };
|
|
244
|
+
}
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
183
247
|
return { id: doc.id, ...__published, _workflow: publicMetadata(meta) };
|
|
184
248
|
}
|
|
185
249
|
return {
|
|
@@ -259,20 +323,34 @@ async function saveWorkflowDraft(args) {
|
|
|
259
323
|
const workflow = collection.workflow;
|
|
260
324
|
if (!db.transaction) throw new Error(`The configured database adapter does not support workflow transactions.`);
|
|
261
325
|
const previous = originalDoc.__workflow;
|
|
326
|
+
const isLegacyPublishedDoc = !previous;
|
|
327
|
+
const baselineRevision = previous?.revision ?? 1;
|
|
262
328
|
const event = createLifecycleEvent({
|
|
263
329
|
name: "revision.created",
|
|
264
330
|
collection: collection.slug,
|
|
265
331
|
documentId: id,
|
|
266
332
|
actorId: user?.sub,
|
|
267
|
-
payload: {
|
|
333
|
+
payload: {
|
|
334
|
+
revision: baselineRevision + 1,
|
|
335
|
+
previousRevision: isLegacyPublishedDoc ? baselineRevision : previous?.revision ?? null
|
|
336
|
+
}
|
|
268
337
|
});
|
|
269
338
|
const doc = await db.transaction(async (tx) => {
|
|
270
339
|
const nextMeta = {
|
|
271
|
-
...previous,
|
|
272
|
-
state: originalDoc.__published ? workflow.draftState ?? workflow.initialState : previous.state,
|
|
273
|
-
revision:
|
|
340
|
+
...previous ?? {},
|
|
341
|
+
state: isLegacyPublishedDoc ? workflow.draftState ?? workflow.initialState : originalDoc.__published ? workflow.draftState ?? workflow.initialState : previous.state,
|
|
342
|
+
revision: baselineRevision + 1,
|
|
343
|
+
...isLegacyPublishedDoc ? { publishedRevision: baselineRevision } : {}
|
|
344
|
+
};
|
|
345
|
+
const updateData = {
|
|
346
|
+
...data,
|
|
347
|
+
__workflow: nextMeta
|
|
274
348
|
};
|
|
275
|
-
|
|
349
|
+
if (isLegacyPublishedDoc) {
|
|
350
|
+
const { __workflow: _legacyWorkflow, __published: _legacyPublished, ...publishedSnapshot } = originalDoc;
|
|
351
|
+
updateData.__published = publishedSnapshot;
|
|
352
|
+
}
|
|
353
|
+
const updated = await tx.update({ collection: collection.slug, id, data: updateData });
|
|
276
354
|
await persistEvent(tx, event);
|
|
277
355
|
return updated;
|
|
278
356
|
});
|
|
@@ -371,7 +449,17 @@ async function transitionWorkflow(args) {
|
|
|
371
449
|
try {
|
|
372
450
|
await hook({ ...hookContext, doc: updated, event: events[0] });
|
|
373
451
|
} catch (error) {
|
|
374
|
-
|
|
452
|
+
getObservabilityRuntime(config)?.recordWorkflowHookFailure({
|
|
453
|
+
collection: collection.slug,
|
|
454
|
+
transition: transition.name
|
|
455
|
+
});
|
|
456
|
+
getConfigLogger(config, "workflow").error({
|
|
457
|
+
err: error,
|
|
458
|
+
msg: "afterTransition hook failed",
|
|
459
|
+
collection: collection.slug,
|
|
460
|
+
transition: transition.name,
|
|
461
|
+
documentId: id
|
|
462
|
+
});
|
|
375
463
|
}
|
|
376
464
|
}
|
|
377
465
|
return updated;
|
|
@@ -412,6 +500,98 @@ function humanizeProviderName(id, type) {
|
|
|
412
500
|
return cleaned.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
413
501
|
}
|
|
414
502
|
|
|
503
|
+
// src/utils/block-references.ts
|
|
504
|
+
function mergeUniqueBlocks(blocks) {
|
|
505
|
+
const seen = /* @__PURE__ */ new Map();
|
|
506
|
+
for (const block of blocks) {
|
|
507
|
+
const existing = seen.get(block.slug);
|
|
508
|
+
if (existing && existing !== block) {
|
|
509
|
+
throw new Error(
|
|
510
|
+
`Duplicate block slug "${block.slug}" found in the reusable block registry. Block slugs must be unique.`
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
if (!existing) seen.set(block.slug, block);
|
|
514
|
+
}
|
|
515
|
+
return Array.from(seen.values());
|
|
516
|
+
}
|
|
517
|
+
function buildRegistry(blocks) {
|
|
518
|
+
return new Map(blocks.map((block) => [block.slug, block]));
|
|
519
|
+
}
|
|
520
|
+
function resolveField(field, registry, blockCache) {
|
|
521
|
+
const next = { ...field };
|
|
522
|
+
if (next.fields) {
|
|
523
|
+
next.fields = next.fields.map(
|
|
524
|
+
(child) => resolveField(child, registry, blockCache)
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
if (next.type !== "blocks") return next;
|
|
528
|
+
const hasInlineBlocks = Array.isArray(next.blocks) && next.blocks.length > 0;
|
|
529
|
+
const hasReferences = Array.isArray(next.blockReferences) && next.blockReferences.length > 0;
|
|
530
|
+
if (hasInlineBlocks && hasReferences) {
|
|
531
|
+
throw new Error(
|
|
532
|
+
`Blocks field "${next.name ?? "(unnamed)"}" cannot define both "blocks" and "blockReferences". Use one or the other.`
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
if (hasReferences) {
|
|
536
|
+
next.blocks = next.blockReferences.map((slug) => {
|
|
537
|
+
const block = registry.get(slug);
|
|
538
|
+
if (!block) {
|
|
539
|
+
throw new Error(
|
|
540
|
+
`Unknown block reference "${slug}" on blocks field "${next.name ?? "(unnamed)"}". Add it to defineConfig({ blocks: [...] }).`
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
return resolveBlock(block, registry, blockCache);
|
|
544
|
+
});
|
|
545
|
+
return next;
|
|
546
|
+
}
|
|
547
|
+
if (hasInlineBlocks) {
|
|
548
|
+
next.blocks = next.blocks.map(
|
|
549
|
+
(block) => resolveBlock(block, registry, blockCache)
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
return next;
|
|
553
|
+
}
|
|
554
|
+
function resolveBlock(block, registry, blockCache) {
|
|
555
|
+
const cached = blockCache.get(block);
|
|
556
|
+
if (cached) return cached;
|
|
557
|
+
const resolved = {
|
|
558
|
+
...block,
|
|
559
|
+
fields: []
|
|
560
|
+
};
|
|
561
|
+
blockCache.set(block, resolved);
|
|
562
|
+
resolved.fields = block.fields.map(
|
|
563
|
+
(field) => resolveField(field, registry, blockCache)
|
|
564
|
+
);
|
|
565
|
+
return resolved;
|
|
566
|
+
}
|
|
567
|
+
function resolveFields(fields, registry, blockCache) {
|
|
568
|
+
return fields.map((field) => resolveField(field, registry, blockCache));
|
|
569
|
+
}
|
|
570
|
+
function normalizeSchemaFragment(fragment) {
|
|
571
|
+
const reusableBlocks = mergeUniqueBlocks(fragment.blocks ?? []);
|
|
572
|
+
const registry = buildRegistry(reusableBlocks);
|
|
573
|
+
const blockCache = /* @__PURE__ */ new WeakMap();
|
|
574
|
+
const resolvedBlocks = reusableBlocks.map(
|
|
575
|
+
(block) => resolveBlock(block, registry, blockCache)
|
|
576
|
+
);
|
|
577
|
+
const resolvedCollections = (fragment.collections ?? []).map(
|
|
578
|
+
(collection) => ({
|
|
579
|
+
...collection,
|
|
580
|
+
fields: resolveFields(collection.fields ?? [], registry, blockCache)
|
|
581
|
+
})
|
|
582
|
+
);
|
|
583
|
+
const resolvedGlobals = (fragment.globals ?? []).map((global) => ({
|
|
584
|
+
...global,
|
|
585
|
+
fields: resolveFields(global.fields ?? [], registry, blockCache)
|
|
586
|
+
}));
|
|
587
|
+
return {
|
|
588
|
+
...fragment,
|
|
589
|
+
...fragment.blocks ? { blocks: resolvedBlocks } : {},
|
|
590
|
+
...fragment.collections ? { collections: resolvedCollections } : {},
|
|
591
|
+
...fragment.globals ? { globals: resolvedGlobals } : {}
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
|
|
415
595
|
// src/utils/config.ts
|
|
416
596
|
var AUDIT_COLLECTION_SLUG = "__audit";
|
|
417
597
|
var SYSTEM_FIELDS = [
|
|
@@ -446,12 +626,23 @@ var AUDIT_COLLECTION = {
|
|
|
446
626
|
fields: [
|
|
447
627
|
{ name: "collection", type: "text", label: "Collection", required: true },
|
|
448
628
|
{ name: "documentId", type: "text", label: "Document ID" },
|
|
449
|
-
{
|
|
629
|
+
{
|
|
630
|
+
name: "operation",
|
|
631
|
+
type: "select",
|
|
632
|
+
label: "Operation",
|
|
633
|
+
options: ["create", "update", "delete"],
|
|
634
|
+
required: true
|
|
635
|
+
},
|
|
450
636
|
{ name: "user", type: "text", label: "User ID" },
|
|
451
637
|
{ name: "timestamp", type: "date", label: "Timestamp", required: true },
|
|
452
638
|
{ name: "changes", type: "json", label: "Changes" }
|
|
453
639
|
],
|
|
454
|
-
access: {
|
|
640
|
+
access: {
|
|
641
|
+
read: () => false,
|
|
642
|
+
create: () => false,
|
|
643
|
+
update: () => false,
|
|
644
|
+
delete: () => false
|
|
645
|
+
},
|
|
455
646
|
admin: { hidden: true }
|
|
456
647
|
};
|
|
457
648
|
var WORKFLOW_HISTORY_COLLECTION_CONFIG = {
|
|
@@ -468,7 +659,12 @@ var WORKFLOW_HISTORY_COLLECTION_CONFIG = {
|
|
|
468
659
|
{ name: "actorId", type: "text" },
|
|
469
660
|
{ name: "createdAt", type: "date", required: true }
|
|
470
661
|
],
|
|
471
|
-
access: {
|
|
662
|
+
access: {
|
|
663
|
+
read: ({ user }) => !!user,
|
|
664
|
+
create: () => false,
|
|
665
|
+
update: () => false,
|
|
666
|
+
delete: () => false
|
|
667
|
+
},
|
|
472
668
|
admin: { hidden: true }
|
|
473
669
|
};
|
|
474
670
|
var LIFECYCLE_EVENTS_COLLECTION_CONFIG = {
|
|
@@ -482,22 +678,56 @@ var LIFECYCLE_EVENTS_COLLECTION_CONFIG = {
|
|
|
482
678
|
{ name: "actorId", type: "text" },
|
|
483
679
|
{ name: "payload", type: "json", required: true },
|
|
484
680
|
{ name: "attempts", type: "number", required: true },
|
|
485
|
-
{
|
|
681
|
+
{
|
|
682
|
+
name: "status",
|
|
683
|
+
type: "select",
|
|
684
|
+
options: ["pending", "processing", "delivered", "failed"],
|
|
685
|
+
required: true
|
|
686
|
+
},
|
|
486
687
|
{ name: "nextAttemptAt", type: "date" },
|
|
487
688
|
{ name: "deliveredAt", type: "date" },
|
|
488
689
|
{ name: "lastError", type: "textarea" }
|
|
489
690
|
],
|
|
490
|
-
access: {
|
|
691
|
+
access: {
|
|
692
|
+
read: ({ user }) => !!user?.roles?.includes("admin"),
|
|
693
|
+
create: () => false,
|
|
694
|
+
update: () => false,
|
|
695
|
+
delete: () => false
|
|
696
|
+
},
|
|
697
|
+
admin: { hidden: true }
|
|
698
|
+
};
|
|
699
|
+
var AUTH_SESSIONS_COLLECTION_CONFIG = {
|
|
700
|
+
slug: AUTH_SESSIONS_COLLECTION,
|
|
701
|
+
labels: { singular: "Auth session", plural: "Auth sessions" },
|
|
702
|
+
fields: [
|
|
703
|
+
{ name: "userId", type: "text", required: true },
|
|
704
|
+
{ name: "email", type: "email", required: true },
|
|
705
|
+
{ name: "collection", type: "text", required: true },
|
|
706
|
+
{ name: "authSource", type: "text" },
|
|
707
|
+
{ name: "providerId", type: "text" },
|
|
708
|
+
{ name: "lastIp", type: "text" },
|
|
709
|
+
{ name: "lastSeenAt", type: "date" },
|
|
710
|
+
{ name: "expiresAt", type: "date" },
|
|
711
|
+
{ name: "revokedAt", type: "date" }
|
|
712
|
+
],
|
|
713
|
+
access: {
|
|
714
|
+
read: () => false,
|
|
715
|
+
create: () => false,
|
|
716
|
+
update: () => false,
|
|
717
|
+
delete: () => false
|
|
718
|
+
},
|
|
491
719
|
admin: { hidden: true }
|
|
492
720
|
};
|
|
493
721
|
function normalizeConfig(config) {
|
|
494
|
-
const
|
|
495
|
-
const
|
|
722
|
+
const schemaAwareConfig = normalizeSchemaFragment(config);
|
|
723
|
+
const collections = schemaAwareConfig?.collections || [];
|
|
724
|
+
const globals = schemaAwareConfig?.globals || [];
|
|
496
725
|
const needsAudit = collections.some((col) => col.audit);
|
|
497
726
|
const needsWorkflow = collections.some((col) => col.workflow || col.drafts);
|
|
727
|
+
const needsAuthSessions = collections.some((col) => !!col.auth);
|
|
498
728
|
const adminAuthCollectionSlug = getAdminAuthCollection({
|
|
499
729
|
collections,
|
|
500
|
-
adminAuth:
|
|
730
|
+
adminAuth: schemaAwareConfig.adminAuth
|
|
501
731
|
})?.slug;
|
|
502
732
|
const normalizedCollections = collections.map((col) => {
|
|
503
733
|
let fields = col.fields || [];
|
|
@@ -594,7 +824,7 @@ function normalizeConfig(config) {
|
|
|
594
824
|
return field;
|
|
595
825
|
});
|
|
596
826
|
}
|
|
597
|
-
if (adminAuthCollectionSlug && col.slug === adminAuthCollectionSlug &&
|
|
827
|
+
if (adminAuthCollectionSlug && col.slug === adminAuthCollectionSlug && schemaAwareConfig.adminAuth?.mode === "external") {
|
|
598
828
|
const externalAdminFields = [
|
|
599
829
|
{
|
|
600
830
|
name: "authProvider",
|
|
@@ -628,7 +858,9 @@ function normalizeConfig(config) {
|
|
|
628
858
|
}
|
|
629
859
|
}
|
|
630
860
|
const updatedFieldNames = new Set(fields.map((f) => f.name));
|
|
631
|
-
const fieldsToInject = SYSTEM_FIELDS.filter(
|
|
861
|
+
const fieldsToInject = SYSTEM_FIELDS.filter(
|
|
862
|
+
(f) => !updatedFieldNames.has(f.name)
|
|
863
|
+
);
|
|
632
864
|
const workflow = col.workflow || (col.drafts ? simplePublishingWorkflow() : void 0);
|
|
633
865
|
return {
|
|
634
866
|
...col,
|
|
@@ -636,17 +868,27 @@ function normalizeConfig(config) {
|
|
|
636
868
|
fields: [...fields, ...fieldsToInject]
|
|
637
869
|
};
|
|
638
870
|
});
|
|
639
|
-
const hasAuditCollection = normalizedCollections.some(
|
|
871
|
+
const hasAuditCollection = normalizedCollections.some(
|
|
872
|
+
(col) => col.slug === AUDIT_COLLECTION_SLUG
|
|
873
|
+
);
|
|
640
874
|
const systemCollections = [];
|
|
641
|
-
if (needsAudit && !hasAuditCollection)
|
|
642
|
-
|
|
875
|
+
if (needsAudit && !hasAuditCollection)
|
|
876
|
+
systemCollections.push(AUDIT_COLLECTION);
|
|
877
|
+
if (needsWorkflow && !normalizedCollections.some(
|
|
878
|
+
(col) => col.slug === WORKFLOW_HISTORY_COLLECTION
|
|
879
|
+
)) {
|
|
643
880
|
systemCollections.push(WORKFLOW_HISTORY_COLLECTION_CONFIG);
|
|
644
881
|
}
|
|
645
|
-
if (needsWorkflow && !normalizedCollections.some(
|
|
882
|
+
if (needsWorkflow && !normalizedCollections.some(
|
|
883
|
+
(col) => col.slug === LIFECYCLE_EVENTS_COLLECTION
|
|
884
|
+
)) {
|
|
646
885
|
systemCollections.push(LIFECYCLE_EVENTS_COLLECTION_CONFIG);
|
|
647
886
|
}
|
|
887
|
+
if (needsAuthSessions && !normalizedCollections.some((col) => col.slug === AUTH_SESSIONS_COLLECTION)) {
|
|
888
|
+
systemCollections.push(AUTH_SESSIONS_COLLECTION_CONFIG);
|
|
889
|
+
}
|
|
648
890
|
return {
|
|
649
|
-
...
|
|
891
|
+
...schemaAwareConfig,
|
|
650
892
|
collections: [...normalizedCollections, ...systemCollections],
|
|
651
893
|
globals
|
|
652
894
|
};
|
|
@@ -872,7 +1114,10 @@ async function runCollectionHooks(hooks, args, options = {}) {
|
|
|
872
1114
|
}
|
|
873
1115
|
} catch (err) {
|
|
874
1116
|
if (options.isolated) {
|
|
875
|
-
|
|
1117
|
+
getConfigLogger(void 0, "hooks").error({
|
|
1118
|
+
err,
|
|
1119
|
+
msg: "Side-effect hook failed after a successful write"
|
|
1120
|
+
});
|
|
876
1121
|
} else {
|
|
877
1122
|
throw err;
|
|
878
1123
|
}
|
|
@@ -917,7 +1162,13 @@ async function executeFieldBeforeChange(fields, data, originalDoc, user, db) {
|
|
|
917
1162
|
const item = updatedValue[i];
|
|
918
1163
|
const origItem = Array.isArray(origValue) ? origValue[i] : null;
|
|
919
1164
|
arrayResult.push(
|
|
920
|
-
await executeFieldBeforeChange(
|
|
1165
|
+
await executeFieldBeforeChange(
|
|
1166
|
+
field.fields,
|
|
1167
|
+
item,
|
|
1168
|
+
origItem,
|
|
1169
|
+
user,
|
|
1170
|
+
db
|
|
1171
|
+
)
|
|
921
1172
|
);
|
|
922
1173
|
}
|
|
923
1174
|
result[field.name] = arrayResult;
|
|
@@ -1058,7 +1309,15 @@ function generateOpenApi(config) {
|
|
|
1058
1309
|
},
|
|
1059
1310
|
WorkflowHistoryEntry: {
|
|
1060
1311
|
type: "object",
|
|
1061
|
-
required: [
|
|
1312
|
+
required: [
|
|
1313
|
+
"collection",
|
|
1314
|
+
"documentId",
|
|
1315
|
+
"transition",
|
|
1316
|
+
"from",
|
|
1317
|
+
"to",
|
|
1318
|
+
"revision",
|
|
1319
|
+
"createdAt"
|
|
1320
|
+
],
|
|
1062
1321
|
properties: {
|
|
1063
1322
|
id: { type: "string" },
|
|
1064
1323
|
collection: { type: "string" },
|
|
@@ -1083,7 +1342,11 @@ function generateOpenApi(config) {
|
|
|
1083
1342
|
user: { type: "string", nullable: true },
|
|
1084
1343
|
timestamp: { type: "string", format: "date-time" },
|
|
1085
1344
|
changes: {
|
|
1086
|
-
oneOf: [
|
|
1345
|
+
oneOf: [
|
|
1346
|
+
{ type: "string" },
|
|
1347
|
+
{ type: "object", additionalProperties: true },
|
|
1348
|
+
{ type: "null" }
|
|
1349
|
+
]
|
|
1087
1350
|
}
|
|
1088
1351
|
}
|
|
1089
1352
|
},
|
|
@@ -1175,13 +1438,17 @@ function generateOpenApi(config) {
|
|
|
1175
1438
|
get: {
|
|
1176
1439
|
tags: ["Preferences"],
|
|
1177
1440
|
summary: "Get an authenticated user preference",
|
|
1178
|
-
parameters: [
|
|
1441
|
+
parameters: [
|
|
1442
|
+
{ name: "key", in: "path", required: true, schema: { type: "string" } }
|
|
1443
|
+
],
|
|
1179
1444
|
responses: { 200: { description: "Preference value" } }
|
|
1180
1445
|
},
|
|
1181
1446
|
put: {
|
|
1182
1447
|
tags: ["Preferences"],
|
|
1183
1448
|
summary: "Set an authenticated user preference",
|
|
1184
|
-
parameters: [
|
|
1449
|
+
parameters: [
|
|
1450
|
+
{ name: "key", in: "path", required: true, schema: { type: "string" } }
|
|
1451
|
+
],
|
|
1185
1452
|
requestBody: {
|
|
1186
1453
|
required: true,
|
|
1187
1454
|
content: { "application/json": { schema: { type: "object" } } }
|
|
@@ -1217,10 +1484,23 @@ function generateOpenApi(config) {
|
|
|
1217
1484
|
tags: ["Audit"],
|
|
1218
1485
|
summary: "Get audit entries across all readable audited collections",
|
|
1219
1486
|
parameters: [
|
|
1220
|
-
{
|
|
1487
|
+
{
|
|
1488
|
+
name: "limit",
|
|
1489
|
+
in: "query",
|
|
1490
|
+
schema: { type: "integer", default: 50, maximum: 100 }
|
|
1491
|
+
},
|
|
1221
1492
|
{ name: "page", in: "query", schema: { type: "integer", default: 1 } },
|
|
1222
|
-
{
|
|
1223
|
-
|
|
1493
|
+
{
|
|
1494
|
+
name: "where",
|
|
1495
|
+
in: "query",
|
|
1496
|
+
schema: { type: "string" },
|
|
1497
|
+
description: "JSON filter"
|
|
1498
|
+
},
|
|
1499
|
+
{
|
|
1500
|
+
name: "sort",
|
|
1501
|
+
in: "query",
|
|
1502
|
+
schema: { type: "string", default: "-timestamp" }
|
|
1503
|
+
}
|
|
1224
1504
|
],
|
|
1225
1505
|
responses: {
|
|
1226
1506
|
200: {
|
|
@@ -1425,10 +1705,27 @@ function generateOpenApi(config) {
|
|
|
1425
1705
|
tags: [collectionTag],
|
|
1426
1706
|
summary: `Get ${labels.singular} audit entries`,
|
|
1427
1707
|
parameters: [
|
|
1428
|
-
{
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1708
|
+
{
|
|
1709
|
+
name: "limit",
|
|
1710
|
+
in: "query",
|
|
1711
|
+
schema: { type: "integer", default: 50, maximum: 100 }
|
|
1712
|
+
},
|
|
1713
|
+
{
|
|
1714
|
+
name: "page",
|
|
1715
|
+
in: "query",
|
|
1716
|
+
schema: { type: "integer", default: 1 }
|
|
1717
|
+
},
|
|
1718
|
+
{
|
|
1719
|
+
name: "where",
|
|
1720
|
+
in: "query",
|
|
1721
|
+
schema: { type: "string" },
|
|
1722
|
+
description: "JSON filter"
|
|
1723
|
+
},
|
|
1724
|
+
{
|
|
1725
|
+
name: "sort",
|
|
1726
|
+
in: "query",
|
|
1727
|
+
schema: { type: "string", default: "-timestamp" }
|
|
1728
|
+
}
|
|
1432
1729
|
],
|
|
1433
1730
|
responses: {
|
|
1434
1731
|
200: {
|
|
@@ -1554,6 +1851,15 @@ function generateOpenApi(config) {
|
|
|
1554
1851
|
post: {
|
|
1555
1852
|
tags: [collectionTag],
|
|
1556
1853
|
summary: `Log out of ${labels.plural}`,
|
|
1854
|
+
parameters: [
|
|
1855
|
+
{
|
|
1856
|
+
name: "allSessions",
|
|
1857
|
+
in: "query",
|
|
1858
|
+
required: false,
|
|
1859
|
+
schema: { type: "boolean" },
|
|
1860
|
+
description: "Revoke every active session for this account instead of only the current one."
|
|
1861
|
+
}
|
|
1862
|
+
],
|
|
1557
1863
|
responses: { 200: { description: "Logged out" } }
|
|
1558
1864
|
}
|
|
1559
1865
|
};
|
|
@@ -1579,7 +1885,11 @@ function generateOpenApi(config) {
|
|
|
1579
1885
|
post: {
|
|
1580
1886
|
tags: [collectionTag],
|
|
1581
1887
|
summary: "Refresh an authentication token",
|
|
1582
|
-
responses: {
|
|
1888
|
+
responses: {
|
|
1889
|
+
200: {
|
|
1890
|
+
description: "Refreshed token for the current active session"
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1583
1893
|
}
|
|
1584
1894
|
};
|
|
1585
1895
|
spec.paths[`${path}/forgot-password`] = {
|
|
@@ -1610,7 +1920,11 @@ function generateOpenApi(config) {
|
|
|
1610
1920
|
schema: { type: "string" }
|
|
1611
1921
|
}
|
|
1612
1922
|
],
|
|
1613
|
-
responses: {
|
|
1923
|
+
responses: {
|
|
1924
|
+
200: {
|
|
1925
|
+
description: "Password changed and active sessions revoked"
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1614
1928
|
}
|
|
1615
1929
|
};
|
|
1616
1930
|
}
|
|
@@ -1819,7 +2133,7 @@ function fieldsToProperties(fields) {
|
|
|
1819
2133
|
return { properties: props, required };
|
|
1820
2134
|
}
|
|
1821
2135
|
function fieldToSchema(field) {
|
|
1822
|
-
let schema
|
|
2136
|
+
let schema;
|
|
1823
2137
|
switch (field.type) {
|
|
1824
2138
|
case "text":
|
|
1825
2139
|
case "textarea":
|
|
@@ -1828,7 +2142,10 @@ function fieldToSchema(field) {
|
|
|
1828
2142
|
break;
|
|
1829
2143
|
case "url":
|
|
1830
2144
|
schema = {
|
|
1831
|
-
oneOf: [
|
|
2145
|
+
oneOf: [
|
|
2146
|
+
{ type: "string" },
|
|
2147
|
+
{ type: "object", additionalProperties: true }
|
|
2148
|
+
]
|
|
1832
2149
|
};
|
|
1833
2150
|
break;
|
|
1834
2151
|
case "icon":
|
|
@@ -1998,6 +2315,7 @@ function defineTab(args) {
|
|
|
1998
2315
|
defineMultiSelectField,
|
|
1999
2316
|
defineNumberField,
|
|
2000
2317
|
defineObjectField,
|
|
2318
|
+
definePublishingWorkflow,
|
|
2001
2319
|
defineRadioField,
|
|
2002
2320
|
defineRelationshipField,
|
|
2003
2321
|
defineRichTextField,
|
|
@@ -2022,6 +2340,7 @@ function defineTab(args) {
|
|
|
2022
2340
|
parseMongoWhere,
|
|
2023
2341
|
parseSort,
|
|
2024
2342
|
parseSqlWhere,
|
|
2343
|
+
publishedStateName,
|
|
2025
2344
|
publishingWorkflow,
|
|
2026
2345
|
resolveAdminAuthCollection,
|
|
2027
2346
|
runCollectionHooks,
|