@dyrected/core 2.17.0 → 2.18.0
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-CLdT_0se.d.cts → app-Dd9cBbCs.d.cts} +171 -5
- package/dist/{app-CLdT_0se.d.ts → app-Dd9cBbCs.d.ts} +171 -5
- package/dist/index.cjs +412 -6
- package/dist/index.d.cts +98 -4
- package/dist/index.d.ts +98 -4
- package/dist/index.js +401 -6
- package/dist/server.cjs +217 -27
- package/dist/server.d.cts +20 -4
- package/dist/server.d.ts +20 -4
- package/dist/server.js +216 -27
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -6084,6 +6084,72 @@ function createReadonlyDb(db) {
|
|
|
6084
6084
|
});
|
|
6085
6085
|
}
|
|
6086
6086
|
|
|
6087
|
+
// src/utils/errors.ts
|
|
6088
|
+
var DyrectedError = class _DyrectedError extends Error {
|
|
6089
|
+
statusCode;
|
|
6090
|
+
constructor(message, statusCode = 500) {
|
|
6091
|
+
super(message);
|
|
6092
|
+
this.name = "DyrectedError";
|
|
6093
|
+
this.statusCode = statusCode;
|
|
6094
|
+
Object.setPrototypeOf(this, _DyrectedError.prototype);
|
|
6095
|
+
}
|
|
6096
|
+
};
|
|
6097
|
+
var ValidationError = class _ValidationError extends DyrectedError {
|
|
6098
|
+
constructor(message) {
|
|
6099
|
+
super(message, 400);
|
|
6100
|
+
this.name = "ValidationError";
|
|
6101
|
+
Object.setPrototypeOf(this, _ValidationError.prototype);
|
|
6102
|
+
}
|
|
6103
|
+
};
|
|
6104
|
+
|
|
6105
|
+
// src/utils/field-integrity.ts
|
|
6106
|
+
function* storedFields(fields) {
|
|
6107
|
+
for (const field of fields ?? []) {
|
|
6108
|
+
if (field.type === "row") {
|
|
6109
|
+
yield* storedFields(field.fields);
|
|
6110
|
+
} else if (field.name) {
|
|
6111
|
+
yield field;
|
|
6112
|
+
}
|
|
6113
|
+
}
|
|
6114
|
+
}
|
|
6115
|
+
function sameValue(a, b) {
|
|
6116
|
+
if (a === b) return true;
|
|
6117
|
+
if (a === null || b === null || a === void 0 || b === void 0) return a == b;
|
|
6118
|
+
if (typeof a === "object" || typeof b === "object") {
|
|
6119
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
6120
|
+
}
|
|
6121
|
+
return false;
|
|
6122
|
+
}
|
|
6123
|
+
function assertImmutableFieldsUnchanged(fields, data, original) {
|
|
6124
|
+
for (const field of storedFields(fields)) {
|
|
6125
|
+
if (!field.immutable || data[field.name] === void 0) continue;
|
|
6126
|
+
if (!sameValue(data[field.name], original[field.name])) {
|
|
6127
|
+
throw new ValidationError(
|
|
6128
|
+
`Field "${field.label ?? field.name}" is immutable and cannot be modified.`
|
|
6129
|
+
);
|
|
6130
|
+
}
|
|
6131
|
+
}
|
|
6132
|
+
}
|
|
6133
|
+
function normalizeMoneyFields(fields, data) {
|
|
6134
|
+
let next = data;
|
|
6135
|
+
for (const field of storedFields(fields)) {
|
|
6136
|
+
if (field.type !== "money") continue;
|
|
6137
|
+
const value = data[field.name];
|
|
6138
|
+
if (value === void 0 || value === null || value === "") continue;
|
|
6139
|
+
let minor;
|
|
6140
|
+
if (typeof value === "number") minor = value;
|
|
6141
|
+
else if (typeof value === "string" && /^-?\d+$/.test(value.trim())) minor = Number(value.trim());
|
|
6142
|
+
if (minor === void 0 || !Number.isSafeInteger(minor)) {
|
|
6143
|
+
throw new ValidationError(
|
|
6144
|
+
`Field "${field.label ?? field.name}" must be a whole number of minor units (for example kobo or cents).`
|
|
6145
|
+
);
|
|
6146
|
+
}
|
|
6147
|
+
if (next === data) next = { ...data };
|
|
6148
|
+
next[field.name] = minor;
|
|
6149
|
+
}
|
|
6150
|
+
return next;
|
|
6151
|
+
}
|
|
6152
|
+
|
|
6087
6153
|
// src/utils/upload-validation.ts
|
|
6088
6154
|
function isMimeAllowed(mimeType, allowed) {
|
|
6089
6155
|
const type = (mimeType || "").toLowerCase().trim();
|
|
@@ -6679,7 +6745,7 @@ async function dispatchLifecycleEvent(config, event) {
|
|
|
6679
6745
|
}
|
|
6680
6746
|
}
|
|
6681
6747
|
async function saveWorkflowDraft(args) {
|
|
6682
|
-
const { config, collection, id, originalDoc, data, user } = args;
|
|
6748
|
+
const { config, collection, id, originalDoc, data, user, beforeCommit } = args;
|
|
6683
6749
|
const db = config.db;
|
|
6684
6750
|
const workflow = collection.workflow;
|
|
6685
6751
|
if (!db.transaction) throw new Error(`The configured database adapter does not support workflow transactions.`);
|
|
@@ -6713,13 +6779,14 @@ async function saveWorkflowDraft(args) {
|
|
|
6713
6779
|
}
|
|
6714
6780
|
const updated = await tx.update({ collection: collection.slug, id, data: updateData });
|
|
6715
6781
|
await persistEvent(tx, event);
|
|
6782
|
+
await beforeCommit?.(tx, updated);
|
|
6716
6783
|
return updated;
|
|
6717
6784
|
});
|
|
6718
6785
|
void dispatchLifecycleEvent(config, event);
|
|
6719
6786
|
return { doc, event };
|
|
6720
6787
|
}
|
|
6721
6788
|
async function createWorkflowDocument(args) {
|
|
6722
|
-
const { config, collection, data, user } = args;
|
|
6789
|
+
const { config, collection, data, user, beforeCommit } = args;
|
|
6723
6790
|
const db = config.db;
|
|
6724
6791
|
if (!db.transaction) throw new Error(`The configured database adapter does not support workflow transactions.`);
|
|
6725
6792
|
let event;
|
|
@@ -6733,13 +6800,14 @@ async function createWorkflowDocument(args) {
|
|
|
6733
6800
|
payload: { revision: 1, previousRevision: null }
|
|
6734
6801
|
});
|
|
6735
6802
|
await persistEvent(tx, event);
|
|
6803
|
+
await beforeCommit?.(tx, created);
|
|
6736
6804
|
return created;
|
|
6737
6805
|
});
|
|
6738
6806
|
void dispatchLifecycleEvent(config, event);
|
|
6739
6807
|
return { doc, event };
|
|
6740
6808
|
}
|
|
6741
6809
|
async function transitionWorkflow(args) {
|
|
6742
|
-
const { config, collection, id, transitionName, expectedRevision, comment, user, req } = args;
|
|
6810
|
+
const { config, collection, id, transitionName, expectedRevision, comment, input, user, req } = args;
|
|
6743
6811
|
const db = config.db;
|
|
6744
6812
|
const workflow = collection.workflow;
|
|
6745
6813
|
if (!db.transaction) throw new Error(`The configured database adapter does not support workflow transactions.`);
|
|
@@ -6765,7 +6833,7 @@ async function transitionWorkflow(args) {
|
|
|
6765
6833
|
if (transition.requireComment && !comment?.trim()) {
|
|
6766
6834
|
throw Object.assign(new Error(`A comment is required for "${transition.label}".`), { statusCode: 400 });
|
|
6767
6835
|
}
|
|
6768
|
-
const hookContext = { transition, from: meta.state, to: transition.to, doc: original, user, comment, req, db };
|
|
6836
|
+
const hookContext = { transition, from: meta.state, to: transition.to, doc: original, user, comment, input, req, db };
|
|
6769
6837
|
for (const hook of workflow.hooks?.beforeTransition ?? []) await hook(hookContext);
|
|
6770
6838
|
const events = [
|
|
6771
6839
|
createLifecycleEvent({
|
|
@@ -6819,6 +6887,19 @@ async function transitionWorkflow(args) {
|
|
|
6819
6887
|
if (targetState.published) data.__published = working;
|
|
6820
6888
|
if (transition.unpublish) data.__published = null;
|
|
6821
6889
|
const next = await tx.update({ collection: collection.slug, id, data });
|
|
6890
|
+
if (transition.onTransition) {
|
|
6891
|
+
await transition.onTransition({
|
|
6892
|
+
transition,
|
|
6893
|
+
from: lockedMeta.state,
|
|
6894
|
+
to: transition.to,
|
|
6895
|
+
doc: locked,
|
|
6896
|
+
input,
|
|
6897
|
+
comment,
|
|
6898
|
+
user,
|
|
6899
|
+
req,
|
|
6900
|
+
tx
|
|
6901
|
+
});
|
|
6902
|
+
}
|
|
6822
6903
|
await tx.create({
|
|
6823
6904
|
collection: WORKFLOW_HISTORY_COLLECTION,
|
|
6824
6905
|
data: {
|
|
@@ -7458,6 +7539,7 @@ var CollectionController = class {
|
|
|
7458
7539
|
updatedBy: user?.sub ?? null
|
|
7459
7540
|
};
|
|
7460
7541
|
data = DefaultsService.apply(this.collection.fields, data);
|
|
7542
|
+
data = normalizeMoneyFields(this.collection.fields, data);
|
|
7461
7543
|
if (this.collection.workflow) {
|
|
7462
7544
|
data = initializeWorkflowDocument(data, this.collection.workflow);
|
|
7463
7545
|
}
|
|
@@ -7505,12 +7587,18 @@ var CollectionController = class {
|
|
|
7505
7587
|
if (!data.id) {
|
|
7506
7588
|
data = { ...data, id: generateDocumentId(this.collection) };
|
|
7507
7589
|
}
|
|
7590
|
+
const beforeCommit = this.buildBeforeCommit(c, "create", user);
|
|
7508
7591
|
const doc = this.collection.workflow ? (await createWorkflowDocument({
|
|
7509
7592
|
config,
|
|
7510
7593
|
collection: this.collection,
|
|
7511
7594
|
data,
|
|
7512
|
-
user
|
|
7513
|
-
|
|
7595
|
+
user,
|
|
7596
|
+
beforeCommit
|
|
7597
|
+
})).doc : await this.persistInTransaction(
|
|
7598
|
+
db,
|
|
7599
|
+
beforeCommit,
|
|
7600
|
+
(writer) => writer.create({ collection: this.collection.slug, data })
|
|
7601
|
+
);
|
|
7514
7602
|
if (this.collection.audit && db) {
|
|
7515
7603
|
AuditService.log(db, {
|
|
7516
7604
|
operation: "create",
|
|
@@ -7744,6 +7832,36 @@ var CollectionController = class {
|
|
|
7744
7832
|
const body = await c.req.json();
|
|
7745
7833
|
return this.performUpdate(c, id, body);
|
|
7746
7834
|
}
|
|
7835
|
+
/**
|
|
7836
|
+
* Builds the callback that runs the collection's `beforeCommit` hooks inside
|
|
7837
|
+
* the write transaction, or `undefined` when the collection has none.
|
|
7838
|
+
*/
|
|
7839
|
+
buildBeforeCommit(c, operation, user, previousDoc) {
|
|
7840
|
+
const hooks = this.collection.hooks?.beforeCommit;
|
|
7841
|
+
if (!hooks?.length) return void 0;
|
|
7842
|
+
return async (tx, doc) => {
|
|
7843
|
+
for (const hook of hooks) {
|
|
7844
|
+
await hook({ doc, previousDoc, req: this.toHookRequestContext(c), user, operation, tx });
|
|
7845
|
+
}
|
|
7846
|
+
};
|
|
7847
|
+
}
|
|
7848
|
+
/**
|
|
7849
|
+
* Runs a single write. When `beforeCommit` hooks exist the write and the
|
|
7850
|
+
* hooks share one transaction, so a throwing hook rolls the write back.
|
|
7851
|
+
*/
|
|
7852
|
+
async persistInTransaction(db, beforeCommit, write) {
|
|
7853
|
+
if (!beforeCommit) return write(db);
|
|
7854
|
+
if (!db.transaction) {
|
|
7855
|
+
throw new Error(
|
|
7856
|
+
`Collection "${this.collection.slug}" defines beforeCommit hooks but the configured database adapter does not support transactions.`
|
|
7857
|
+
);
|
|
7858
|
+
}
|
|
7859
|
+
return db.transaction(async (tx) => {
|
|
7860
|
+
const doc = await write(tx);
|
|
7861
|
+
await beforeCommit(tx, doc);
|
|
7862
|
+
return doc;
|
|
7863
|
+
});
|
|
7864
|
+
}
|
|
7747
7865
|
/**
|
|
7748
7866
|
* Core update pipeline shared by PATCH requests and operational actions:
|
|
7749
7867
|
* auth-field stripping, timestamps, access enforcement, field write access,
|
|
@@ -7785,6 +7903,7 @@ var CollectionController = class {
|
|
|
7785
7903
|
403
|
|
7786
7904
|
);
|
|
7787
7905
|
}
|
|
7906
|
+
data = normalizeMoneyFields(this.collection.fields, data);
|
|
7788
7907
|
let before = null;
|
|
7789
7908
|
if (this.collection.audit) {
|
|
7790
7909
|
before = originalDoc;
|
|
@@ -7818,14 +7937,21 @@ var CollectionController = class {
|
|
|
7818
7937
|
surface: "collection.beforeChange",
|
|
7819
7938
|
path: `collection:${this.collection.slug}.hooks.beforeChange`
|
|
7820
7939
|
});
|
|
7940
|
+
assertImmutableFieldsUnchanged(this.collection.fields, data, originalDoc);
|
|
7941
|
+
const beforeCommit = this.buildBeforeCommit(c, "update", user, originalDoc);
|
|
7821
7942
|
const doc = this.collection.workflow ? (await saveWorkflowDraft({
|
|
7822
7943
|
config,
|
|
7823
7944
|
collection: this.collection,
|
|
7824
7945
|
id,
|
|
7825
7946
|
originalDoc,
|
|
7826
7947
|
data,
|
|
7827
|
-
user
|
|
7828
|
-
|
|
7948
|
+
user,
|
|
7949
|
+
beforeCommit
|
|
7950
|
+
})).doc : await this.persistInTransaction(
|
|
7951
|
+
db,
|
|
7952
|
+
beforeCommit,
|
|
7953
|
+
(writer) => writer.update({ collection: this.collection.slug, id, data })
|
|
7954
|
+
);
|
|
7829
7955
|
if (this.collection.audit && db) {
|
|
7830
7956
|
AuditService.log(db, {
|
|
7831
7957
|
operation: "update",
|
|
@@ -8052,6 +8178,7 @@ var CollectionController = class {
|
|
|
8052
8178
|
transitionName,
|
|
8053
8179
|
expectedRevision: body.expectedRevision,
|
|
8054
8180
|
comment: body.comment,
|
|
8181
|
+
input: body.input,
|
|
8055
8182
|
user: c.get("user"),
|
|
8056
8183
|
req: { query: c.req.query(), headers: c.req.header(), raw: c.req.raw }
|
|
8057
8184
|
});
|
|
@@ -9383,6 +9510,20 @@ var MediaFolderController = class {
|
|
|
9383
9510
|
}
|
|
9384
9511
|
};
|
|
9385
9512
|
|
|
9513
|
+
// src/utils/admin-url.ts
|
|
9514
|
+
var DEFAULT_ADMIN_PATH = "/admin";
|
|
9515
|
+
function resolveAdminUrl(c, config, clientUrl) {
|
|
9516
|
+
const configured = config.admin?.adminUrl?.trim() || process.env.DYRECTED_ADMIN_URL?.trim() || (typeof clientUrl === "string" ? clientUrl.trim() : "") || DEFAULT_ADMIN_PATH;
|
|
9517
|
+
try {
|
|
9518
|
+
return new URL(configured, c.req.url).toString();
|
|
9519
|
+
} catch {
|
|
9520
|
+
return configured;
|
|
9521
|
+
}
|
|
9522
|
+
}
|
|
9523
|
+
function appendQueryParam(url, key, value) {
|
|
9524
|
+
return `${url}${url.includes("?") ? "&" : "?"}${key}=${encodeURIComponent(value)}`;
|
|
9525
|
+
}
|
|
9526
|
+
|
|
9386
9527
|
// src/controllers/auth.controller.ts
|
|
9387
9528
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
9388
9529
|
|
|
@@ -10058,8 +10199,7 @@ var AuthController = class {
|
|
|
10058
10199
|
},
|
|
10059
10200
|
"1h"
|
|
10060
10201
|
);
|
|
10061
|
-
const
|
|
10062
|
-
const url = resetUrl ? `${resetUrl}${resetUrl.includes("?") ? "&" : "?"}token=${encodeURIComponent(resetToken)}` : void 0;
|
|
10202
|
+
const url = appendQueryParam(resolveAdminUrl(c, config, body?.resetUrl), "token", resetToken);
|
|
10063
10203
|
try {
|
|
10064
10204
|
const { subject, html } = buildResetPasswordEmail(config, {
|
|
10065
10205
|
token: resetToken,
|
|
@@ -10191,8 +10331,7 @@ var AuthController = class {
|
|
|
10191
10331
|
},
|
|
10192
10332
|
"7d"
|
|
10193
10333
|
);
|
|
10194
|
-
const
|
|
10195
|
-
const url = inviteUrl ? `${inviteUrl}${inviteUrl.includes("?") ? "&" : "?"}inviteToken=${encodeURIComponent(inviteToken)}` : void 0;
|
|
10334
|
+
const url = appendQueryParam(resolveAdminUrl(c, config, body?.inviteUrl), "inviteToken", inviteToken);
|
|
10196
10335
|
try {
|
|
10197
10336
|
const { subject, html } = buildInviteEmail(config, {
|
|
10198
10337
|
token: inviteToken,
|
|
@@ -12471,7 +12610,12 @@ function generateOpenApi(config) {
|
|
|
12471
12610
|
type: "object",
|
|
12472
12611
|
properties: {
|
|
12473
12612
|
expectedRevision: { type: "integer", minimum: 1 },
|
|
12474
|
-
comment: { type: "string" }
|
|
12613
|
+
comment: { type: "string" },
|
|
12614
|
+
input: {
|
|
12615
|
+
type: "object",
|
|
12616
|
+
additionalProperties: true,
|
|
12617
|
+
description: "Extra data passed to the transition's onTransition handler."
|
|
12618
|
+
}
|
|
12475
12619
|
}
|
|
12476
12620
|
},
|
|
12477
12621
|
WorkflowHistoryEntry: {
|
|
@@ -13634,6 +13778,9 @@ function fieldToSchema(field) {
|
|
|
13634
13778
|
case "number":
|
|
13635
13779
|
schema = { type: "number" };
|
|
13636
13780
|
break;
|
|
13781
|
+
case "money":
|
|
13782
|
+
schema = { type: "integer", description: "Amount in minor units (for example kobo or cents)." };
|
|
13783
|
+
break;
|
|
13637
13784
|
case "boolean":
|
|
13638
13785
|
schema = { type: "boolean" };
|
|
13639
13786
|
break;
|
|
@@ -14453,6 +14600,30 @@ function serializeDetailForApi(detail) {
|
|
|
14453
14600
|
if (!detail || !Array.isArray(detail)) return void 0;
|
|
14454
14601
|
return detail.map(serializeDetailItemForApi);
|
|
14455
14602
|
}
|
|
14603
|
+
async function serializeActionsForApi(actions, serializeAccess) {
|
|
14604
|
+
const serializeAccessConfig = async (access) => {
|
|
14605
|
+
if (!access) return void 0;
|
|
14606
|
+
const serialized = {};
|
|
14607
|
+
for (const key of ["read", "create", "update", "delete"]) {
|
|
14608
|
+
if (access[key] !== void 0) serialized[key] = await serializeAccess(access[key]);
|
|
14609
|
+
}
|
|
14610
|
+
return serialized;
|
|
14611
|
+
};
|
|
14612
|
+
return Promise.all(
|
|
14613
|
+
(actions || []).map(async (action) => ({
|
|
14614
|
+
name: action.name,
|
|
14615
|
+
label: action.label,
|
|
14616
|
+
icon: action.icon,
|
|
14617
|
+
type: action.type ?? "row",
|
|
14618
|
+
confirm: action.confirm,
|
|
14619
|
+
submitLabel: action.submitLabel,
|
|
14620
|
+
fields: action.fields?.map(serializeFieldForApi),
|
|
14621
|
+
mutation: action.mutation,
|
|
14622
|
+
// Self-hosted handlers are intentionally omitted: they never leave the server.
|
|
14623
|
+
access: action.access ? await serializeAccessConfig(action.access) : void 0
|
|
14624
|
+
}))
|
|
14625
|
+
);
|
|
14626
|
+
}
|
|
14456
14627
|
async function serializeViewForApi(view, serializeAccess) {
|
|
14457
14628
|
if (!view) return view;
|
|
14458
14629
|
const serializeAccessConfig = async (access) => {
|
|
@@ -14479,20 +14650,7 @@ async function serializeViewForApi(view, serializeAccess) {
|
|
|
14479
14650
|
features: view.features,
|
|
14480
14651
|
actionOrder: view.actionOrder,
|
|
14481
14652
|
components: view.components,
|
|
14482
|
-
actions: await
|
|
14483
|
-
(view.actions || []).map(async (action) => ({
|
|
14484
|
-
name: action.name,
|
|
14485
|
-
label: action.label,
|
|
14486
|
-
icon: action.icon,
|
|
14487
|
-
type: action.type ?? "row",
|
|
14488
|
-
confirm: action.confirm,
|
|
14489
|
-
submitLabel: action.submitLabel,
|
|
14490
|
-
fields: action.fields?.map(serializeFieldForApi),
|
|
14491
|
-
mutation: action.mutation,
|
|
14492
|
-
// Self-hosted handlers are intentionally omitted: they never leave the server.
|
|
14493
|
-
access: action.access ? await serializeAccessConfig(action.access) : void 0
|
|
14494
|
-
}))
|
|
14495
|
-
),
|
|
14653
|
+
actions: await serializeActionsForApi(view.actions, serializeAccess),
|
|
14496
14654
|
access: view.access ? await serializeAccessConfig(view.access) : void 0
|
|
14497
14655
|
};
|
|
14498
14656
|
}
|
|
@@ -14574,6 +14732,7 @@ function registerRoutes(app, config) {
|
|
|
14574
14732
|
drafts: !!col.drafts,
|
|
14575
14733
|
defaultView: col.defaultView ?? col.admin?.defaultView,
|
|
14576
14734
|
views: await Promise.all((col.views || []).map((view) => serializeViewForApi(view, serializeAccess))),
|
|
14735
|
+
actions: await serializeActionsForApi(col.actions, serializeAccess),
|
|
14577
14736
|
admin: col.admin,
|
|
14578
14737
|
detail: serializeDetailForApi(col.detail),
|
|
14579
14738
|
workflow: col.workflow ? {
|
|
@@ -15454,6 +15613,31 @@ function registerRoutes(app, config) {
|
|
|
15454
15613
|
});
|
|
15455
15614
|
}
|
|
15456
15615
|
|
|
15616
|
+
// src/tasks.ts
|
|
15617
|
+
var TASK_LOCKS_COLLECTION = "__task_locks";
|
|
15618
|
+
var TASK_LOCKS_COLLECTION_CONFIG = {
|
|
15619
|
+
slug: TASK_LOCKS_COLLECTION,
|
|
15620
|
+
labels: { singular: "Task lock", plural: "Task locks" },
|
|
15621
|
+
fields: [
|
|
15622
|
+
{ name: "name", type: "text", required: true },
|
|
15623
|
+
// Numeric and promoted so the lock compare-and-set is a real column comparison.
|
|
15624
|
+
{ name: "lockedUntil", type: "number", required: true, promoted: true },
|
|
15625
|
+
{ name: "lockedBy", type: "text" },
|
|
15626
|
+
{ name: "lastScheduledFor", type: "date" },
|
|
15627
|
+
{ name: "lastStartedAt", type: "date" },
|
|
15628
|
+
{ name: "lastFinishedAt", type: "date" },
|
|
15629
|
+
{ name: "lastStatus", type: "text" },
|
|
15630
|
+
{ name: "lastError", type: "textarea" }
|
|
15631
|
+
],
|
|
15632
|
+
access: {
|
|
15633
|
+
read: ({ user }) => !!user?.roles?.includes("admin"),
|
|
15634
|
+
create: () => false,
|
|
15635
|
+
update: () => false,
|
|
15636
|
+
delete: () => false
|
|
15637
|
+
},
|
|
15638
|
+
admin: { hidden: true }
|
|
15639
|
+
};
|
|
15640
|
+
|
|
15457
15641
|
// src/utils/config.ts
|
|
15458
15642
|
var AUDIT_COLLECTION_SLUG = "__audit";
|
|
15459
15643
|
var SYSTEM_FIELDS = [
|
|
@@ -15605,6 +15789,7 @@ function normalizeConfig(config) {
|
|
|
15605
15789
|
const globals = schemaAwareConfig?.globals || [];
|
|
15606
15790
|
const needsAudit = collections.some((col) => col.audit);
|
|
15607
15791
|
const needsWorkflow = collections.some((col) => col.workflow || col.drafts);
|
|
15792
|
+
const needsTasks = (config.tasks?.length ?? 0) > 0;
|
|
15608
15793
|
const needsAuthSessions = collections.some((col) => !!col.auth);
|
|
15609
15794
|
const adminAuthCollectionSlug = getAdminAuthCollection({
|
|
15610
15795
|
collections,
|
|
@@ -15837,6 +16022,9 @@ function normalizeConfig(config) {
|
|
|
15837
16022
|
if (needsWorkflow && !normalizedCollections.some((col) => col.slug === LIFECYCLE_EVENTS_COLLECTION)) {
|
|
15838
16023
|
systemCollections.push(LIFECYCLE_EVENTS_COLLECTION_CONFIG);
|
|
15839
16024
|
}
|
|
16025
|
+
if (needsTasks && !normalizedCollections.some((col) => col.slug === TASK_LOCKS_COLLECTION)) {
|
|
16026
|
+
systemCollections.push(TASK_LOCKS_COLLECTION_CONFIG);
|
|
16027
|
+
}
|
|
15840
16028
|
if (needsAuthSessions && !normalizedCollections.some((col) => col.slug === AUTH_SESSIONS_COLLECTION)) {
|
|
15841
16029
|
systemCollections.push(AUTH_SESSIONS_COLLECTION_CONFIG);
|
|
15842
16030
|
}
|
|
@@ -16206,6 +16394,7 @@ export {
|
|
|
16206
16394
|
revokeAllAuthSessions,
|
|
16207
16395
|
revokeAuthSession,
|
|
16208
16396
|
sendEmail,
|
|
16397
|
+
serializeActionsForApi,
|
|
16209
16398
|
serializeDetailForApi,
|
|
16210
16399
|
serializeViewForApi,
|
|
16211
16400
|
shouldSampleRequest,
|