@easy-cms/core 0.63.0 → 0.65.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/{chunk-LN4M4BUP.js → chunk-BGGQ36LR.js} +141 -43
- package/dist/{chunk-FPFWLQB5.js → chunk-RTKWMVAC.js} +7 -0
- package/dist/{config-Dgs6FE0S.d.ts → config-DE8ez-a9.d.ts} +12 -1
- package/dist/{handler-CcQjkj5Q.d.ts → handler-Cm1ehtkm.d.ts} +10 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/internal.d.ts +3 -3
- package/dist/internal.js +4 -2
- package/dist/{plugin-8hrkYMuR.d.ts → plugin-CX7E7RZZ.d.ts} +1 -1
- package/dist/plugin.d.ts +2 -2
- package/dist/plugin.js +1 -1
- package/package.json +1 -1
|
@@ -33,8 +33,9 @@ import {
|
|
|
33
33
|
typeFromName,
|
|
34
34
|
uniqueWithinOf,
|
|
35
35
|
warnDeprecated,
|
|
36
|
+
withCharset,
|
|
36
37
|
withPrivacy
|
|
37
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-RTKWMVAC.js";
|
|
38
39
|
|
|
39
40
|
// src/access.ts
|
|
40
41
|
var isSystemAdmin = (user) => user?.role === "admin" && !user.apiKey && user.scoped !== true;
|
|
@@ -521,6 +522,7 @@ var CSV_LIMIT = 1e4;
|
|
|
521
522
|
var DAY = 864e5;
|
|
522
523
|
var BULKY = /* @__PURE__ */ new Set(["richText", "blocks", "array", "json"]);
|
|
523
524
|
var auditContext = new AsyncLocalStorage();
|
|
525
|
+
var SIGN_IN_FAILURES = /* @__PURE__ */ new Set(["login.failed", "login.locked", "sso.failed"]);
|
|
524
526
|
function stable(value) {
|
|
525
527
|
if (value === void 0 || value === null) return "null";
|
|
526
528
|
if (Array.isArray(value)) return `[${value.map(stable).join(",")}]`;
|
|
@@ -577,7 +579,15 @@ var AuditLog = class {
|
|
|
577
579
|
* counted, and the dashboard says so.
|
|
578
580
|
*/
|
|
579
581
|
async record(entry) {
|
|
580
|
-
if (!this.enabled)
|
|
582
|
+
if (!this.enabled) {
|
|
583
|
+
if (SIGN_IN_FAILURES.has(entry.action)) {
|
|
584
|
+
const ip = auditContext.getStore()?.ip;
|
|
585
|
+
this.cms.logger.warn(
|
|
586
|
+
`${entry.action}: ${entry.email ?? entry.user?.email ?? "unknown"}${ip ? ` from ${ip}` : ""}`
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
581
591
|
try {
|
|
582
592
|
const context = auditContext.getStore();
|
|
583
593
|
const user = entry.user !== void 0 ? entry.user : context?.user ?? null;
|
|
@@ -730,8 +740,11 @@ var AuditLog = class {
|
|
|
730
740
|
await this.record({
|
|
731
741
|
action: "audit.verify",
|
|
732
742
|
target: "audit",
|
|
733
|
-
detail: { checked, invalid: invalid.length, gaps }
|
|
743
|
+
detail: { checked, invalid: invalid.length, gaps, lastId: previous ?? null }
|
|
734
744
|
});
|
|
745
|
+
this.cms.logger.info(
|
|
746
|
+
`Audit log checked: ${checked} entries, last #${previous ?? "-"}; ${invalid.length} changed, ${gaps} missing`
|
|
747
|
+
);
|
|
735
748
|
return verification;
|
|
736
749
|
}
|
|
737
750
|
/**
|
|
@@ -1015,6 +1028,21 @@ async function verifyTurnstile(secretKey, response, ip, fetcher = fetch) {
|
|
|
1015
1028
|
|
|
1016
1029
|
// src/auth/cookie.ts
|
|
1017
1030
|
var SESSION_COOKIE = "ecms-session";
|
|
1031
|
+
var hostCookie = (name) => `__Host-${name}`;
|
|
1032
|
+
function cookieValue(header, name) {
|
|
1033
|
+
if (!header) return void 0;
|
|
1034
|
+
let plain;
|
|
1035
|
+
for (const part of header.split(";")) {
|
|
1036
|
+
const eq = part.indexOf("=");
|
|
1037
|
+
if (eq <= 0) continue;
|
|
1038
|
+
const key = part.slice(0, eq).trim();
|
|
1039
|
+
if (key !== name && key !== hostCookie(name)) continue;
|
|
1040
|
+
const value = decodeURIComponent(part.slice(eq + 1).trim());
|
|
1041
|
+
if (key !== name) return value;
|
|
1042
|
+
plain ??= value;
|
|
1043
|
+
}
|
|
1044
|
+
return plain;
|
|
1045
|
+
}
|
|
1018
1046
|
|
|
1019
1047
|
// src/auth/emails.ts
|
|
1020
1048
|
var esc = (value) => value.replace(
|
|
@@ -1911,12 +1939,8 @@ var Auth = class {
|
|
|
1911
1939
|
async userFromHeaders(headers) {
|
|
1912
1940
|
const authorization = headers.get("authorization");
|
|
1913
1941
|
if (authorization?.startsWith("Bearer ")) return this.verify(authorization.slice(7).trim());
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
if (eq > 0 && part.slice(0, eq).trim() === SESSION_COOKIE)
|
|
1917
|
-
return this.verify(decodeURIComponent(part.slice(eq + 1).trim()));
|
|
1918
|
-
}
|
|
1919
|
-
return null;
|
|
1942
|
+
const cookie2 = cookieValue(headers.get("cookie"), SESSION_COOKIE);
|
|
1943
|
+
return cookie2 ? this.verify(cookie2) : null;
|
|
1920
1944
|
}
|
|
1921
1945
|
/**
|
|
1922
1946
|
* Returns the user for a signed session token, or for an API key (`ecms_…`, with `apiKey` set
|
|
@@ -2820,12 +2844,38 @@ function diskStorage(options = {}) {
|
|
|
2820
2844
|
}
|
|
2821
2845
|
|
|
2822
2846
|
// src/backups.ts
|
|
2847
|
+
import { createCipheriv, createDecipheriv, createHash as createHash3, randomBytes as randomBytes5 } from "crypto";
|
|
2823
2848
|
import { mkdtemp, readFile as readFile2, rm as rm2 } from "fs/promises";
|
|
2824
2849
|
import { tmpdir } from "os";
|
|
2825
2850
|
import { join as join2 } from "path";
|
|
2826
2851
|
import { promisify } from "util";
|
|
2827
2852
|
import { gzip } from "zlib";
|
|
2828
2853
|
var gzipAsync = promisify(gzip);
|
|
2854
|
+
var MAGIC = Buffer.from("ECMSBAK1");
|
|
2855
|
+
var IV_BYTES = 12;
|
|
2856
|
+
var TAG_BYTES = 16;
|
|
2857
|
+
var keyOf = (encryptionKey) => createHash3("sha256").update(encryptionKey).digest();
|
|
2858
|
+
function encryptBackup(data, encryptionKey) {
|
|
2859
|
+
const iv = randomBytes5(IV_BYTES);
|
|
2860
|
+
const cipher = createCipheriv("aes-256-gcm", keyOf(encryptionKey), iv);
|
|
2861
|
+
const body = Buffer.concat([cipher.update(data), cipher.final()]);
|
|
2862
|
+
return new Uint8Array(Buffer.concat([MAGIC, iv, body, cipher.getAuthTag()]));
|
|
2863
|
+
}
|
|
2864
|
+
function decryptBackup(data, encryptionKey) {
|
|
2865
|
+
const buffer = Buffer.from(data);
|
|
2866
|
+
if (buffer.length < MAGIC.length + IV_BYTES + TAG_BYTES || !buffer.subarray(0, 8).equals(MAGIC))
|
|
2867
|
+
throw new EasyCMSError("Not an encrypted Easy CMS backup", 400);
|
|
2868
|
+
const iv = buffer.subarray(MAGIC.length, MAGIC.length + IV_BYTES);
|
|
2869
|
+
const tag = buffer.subarray(buffer.length - TAG_BYTES);
|
|
2870
|
+
const body = buffer.subarray(MAGIC.length + IV_BYTES, buffer.length - TAG_BYTES);
|
|
2871
|
+
const decipher = createDecipheriv("aes-256-gcm", keyOf(encryptionKey), iv);
|
|
2872
|
+
decipher.setAuthTag(tag);
|
|
2873
|
+
try {
|
|
2874
|
+
return new Uint8Array(Buffer.concat([decipher.update(body), decipher.final()]));
|
|
2875
|
+
} catch {
|
|
2876
|
+
throw new EasyCMSError("The backup could not be decrypted: wrong key, or a changed file", 400);
|
|
2877
|
+
}
|
|
2878
|
+
}
|
|
2829
2879
|
var DEFAULT_BACKUP_TIME = "03:00";
|
|
2830
2880
|
var DEFAULT_BACKUP_KEEP = 7;
|
|
2831
2881
|
var DEFAULT_DIR = "backups";
|
|
@@ -2970,11 +3020,15 @@ async function runBackup(cms, record) {
|
|
|
2970
3020
|
try {
|
|
2971
3021
|
const file = join2(dir, "backup.db");
|
|
2972
3022
|
await writeBackupFile(cms, file);
|
|
2973
|
-
const
|
|
3023
|
+
const encryptionKey = cms.config.backups?.encryptionKey;
|
|
3024
|
+
const gzipped = new Uint8Array(await gzipAsync(await readFile2(file)));
|
|
3025
|
+
const data = encryptionKey ? encryptBackup(gzipped, encryptionKey) : gzipped;
|
|
2974
3026
|
const storage = await backupStorage(cms);
|
|
2975
|
-
const filename = filenameFor(cms, started)
|
|
2976
|
-
const key = await storage.get(filename) ? filename.replace(/\.db\.gz
|
|
2977
|
-
await storage.put(key, data, {
|
|
3027
|
+
const filename = `${filenameFor(cms, started)}${encryptionKey ? ".enc" : ""}`;
|
|
3028
|
+
const key = await storage.get(filename) ? filename.replace(/\.db\.gz(\.enc)?$/, `-${row.id}.db.gz$1`) : filename;
|
|
3029
|
+
await storage.put(key, data, {
|
|
3030
|
+
contentType: encryptionKey ? "application/octet-stream" : "application/gzip"
|
|
3031
|
+
});
|
|
2978
3032
|
await pruneBackups(cms, 1);
|
|
2979
3033
|
row = await update(cms, row, {
|
|
2980
3034
|
state: "done",
|
|
@@ -3101,8 +3155,20 @@ async function downloadBackup(cms, id, by) {
|
|
|
3101
3155
|
const file = await (await backupStorage(cms)).get(row.filename);
|
|
3102
3156
|
if (!file)
|
|
3103
3157
|
throw new EasyCMSError(`The file ${row.filename} is no longer in the backup storage`, 404);
|
|
3158
|
+
let { filename } = row;
|
|
3159
|
+
let body = file.body;
|
|
3160
|
+
if (filename.endsWith(".enc")) {
|
|
3161
|
+
const encryptionKey = cms.config.backups?.encryptionKey;
|
|
3162
|
+
if (!encryptionKey)
|
|
3163
|
+
throw new EasyCMSError(
|
|
3164
|
+
"This backup is encrypted: set backups.encryptionKey to download it",
|
|
3165
|
+
400
|
|
3166
|
+
);
|
|
3167
|
+
body = decryptBackup(body, encryptionKey);
|
|
3168
|
+
filename = filename.slice(0, -".enc".length);
|
|
3169
|
+
}
|
|
3104
3170
|
await update(cms, row, { downloadedBy: by, downloadedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
3105
|
-
return { filename
|
|
3171
|
+
return { filename, body };
|
|
3106
3172
|
}
|
|
3107
3173
|
async function deleteBackup(cms, id) {
|
|
3108
3174
|
const row = await cms.db.findById({ collection: DATABASE_BACKUPS, id });
|
|
@@ -4195,7 +4261,7 @@ var AUTH = keys(`roles rbac tokenExpiration maxLoginAttempts lockWindow trustedO
|
|
|
4195
4261
|
resetPasswordExpiration inviteExpiration providers providerSignUp password setupCode members emails`);
|
|
4196
4262
|
var MEMBERS = keys("roles signUp pages emails");
|
|
4197
4263
|
var UPLOAD = keys("dir maxFileSize mimeTypes storage imageSizes fromURL folders privateStorage");
|
|
4198
|
-
var BACKUPS = keys("frequency at keep dir storage sqlite");
|
|
4264
|
+
var BACKUPS = keys("frequency at keep dir storage sqlite encryptionKey");
|
|
4199
4265
|
var AUDIT = keys("keepDays values failedLogins scope");
|
|
4200
4266
|
var COLLECTION = keys(`slug labels fields useAsTitle drafts versions schedule preview access hooks
|
|
4201
4267
|
admin`);
|
|
@@ -4637,7 +4703,7 @@ function validateConfig(config) {
|
|
|
4637
4703
|
validateAdmin(config, add);
|
|
4638
4704
|
validateAdminViews(config, add);
|
|
4639
4705
|
validateUpload(config, add);
|
|
4640
|
-
validateBackups(config, add);
|
|
4706
|
+
validateBackups(config, add, (issue) => issues.push(issue));
|
|
4641
4707
|
validateAudit(config, add);
|
|
4642
4708
|
validateAuth(config, add);
|
|
4643
4709
|
validateCors(config.cors, add);
|
|
@@ -4725,6 +4791,14 @@ function validateCronSecret(value, push) {
|
|
|
4725
4791
|
severity: "warning"
|
|
4726
4792
|
});
|
|
4727
4793
|
}
|
|
4794
|
+
function publicURLs(storage) {
|
|
4795
|
+
const url = storage.url;
|
|
4796
|
+
try {
|
|
4797
|
+
return typeof url === "function" && url.call(storage, "backup.db.gz") !== void 0;
|
|
4798
|
+
} catch {
|
|
4799
|
+
return false;
|
|
4800
|
+
}
|
|
4801
|
+
}
|
|
4728
4802
|
function guessable(secret) {
|
|
4729
4803
|
return /change[-_ ]?me|example|placeholder|secret-?key/i.test(secret) || new Set(secret).size < 8;
|
|
4730
4804
|
}
|
|
@@ -5242,14 +5316,21 @@ function validateAudit(config, add) {
|
|
|
5242
5316
|
}
|
|
5243
5317
|
}
|
|
5244
5318
|
}
|
|
5245
|
-
function validateBackups(config, add) {
|
|
5319
|
+
function validateBackups(config, add, push) {
|
|
5320
|
+
const pushWarning = (path, message2, hint) => push({ path, message: message2, hint, severity: "warning" });
|
|
5246
5321
|
const backups = config.backups;
|
|
5247
5322
|
if (backups === void 0) return;
|
|
5248
5323
|
if (typeof backups !== "object" || backups === null) {
|
|
5249
5324
|
add("backups", "must be an object", "e.g. backups: { frequency: 'daily', keep: 7 }");
|
|
5250
5325
|
return;
|
|
5251
5326
|
}
|
|
5252
|
-
const { frequency, at, keep, dir, storage, sqlite } = backups;
|
|
5327
|
+
const { frequency, at, keep, dir, storage, sqlite, encryptionKey } = backups;
|
|
5328
|
+
if (encryptionKey !== void 0 && (typeof encryptionKey !== "string" || encryptionKey.length < MIN_SECRET_LENGTH))
|
|
5329
|
+
add(
|
|
5330
|
+
"backups.encryptionKey",
|
|
5331
|
+
`must be at least ${MIN_SECRET_LENGTH} characters`,
|
|
5332
|
+
"e.g. `openssl rand -hex 32`, kept somewhere other than the backups"
|
|
5333
|
+
);
|
|
5253
5334
|
if (frequency !== void 0 && frequency !== "daily" && frequency !== "weekly")
|
|
5254
5335
|
add("backups.frequency", 'must be "daily" or "weekly"');
|
|
5255
5336
|
if (at !== void 0 && (typeof at !== "string" || !/^([01]\d|2[0-3]):[0-5]\d$/.test(at)))
|
|
@@ -5262,6 +5343,14 @@ function validateBackups(config, add) {
|
|
|
5262
5343
|
add("backups.dir", "must not be the uploads folder: uploads are served publicly");
|
|
5263
5344
|
if (storage !== void 0 && (typeof storage !== "object" || storage === null || typeof storage.put !== "function"))
|
|
5264
5345
|
add("backups.storage", "must be a storage adapter, e.g. s3Storage()");
|
|
5346
|
+
else if (storage !== void 0 && publicURLs(storage)) {
|
|
5347
|
+
const message2 = encryptionKey ? "gives files public URLs; the backups are encrypted, but keep them in a private bucket" : "gives files public URLs: anyone with the address of a backup could download the whole database";
|
|
5348
|
+
pushWarning(
|
|
5349
|
+
"backups.storage",
|
|
5350
|
+
message2,
|
|
5351
|
+
"use a private bucket (no publicURL), or set backups.encryptionKey"
|
|
5352
|
+
);
|
|
5353
|
+
}
|
|
5265
5354
|
if (sqlite !== void 0 && typeof sqlite !== "function")
|
|
5266
5355
|
add(
|
|
5267
5356
|
"backups.sqlite",
|
|
@@ -6057,7 +6146,7 @@ function resolveAudit(audit) {
|
|
|
6057
6146
|
}
|
|
6058
6147
|
|
|
6059
6148
|
// src/local-api.ts
|
|
6060
|
-
import { createHmac as createHmac6, randomBytes as
|
|
6149
|
+
import { createHmac as createHmac6, randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual5 } from "crypto";
|
|
6061
6150
|
|
|
6062
6151
|
// src/auth/common-passwords.ts
|
|
6063
6152
|
var LIST = `
|
|
@@ -6821,13 +6910,13 @@ var EasyCMS = class {
|
|
|
6821
6910
|
}
|
|
6822
6911
|
]);
|
|
6823
6912
|
}
|
|
6824
|
-
const random2 =
|
|
6913
|
+
const random2 = randomBytes6(4).toString("hex");
|
|
6825
6914
|
const isPrivate = await this.folders.isPrivate(folderOf(MEDIA, data));
|
|
6826
6915
|
const storage = this.storageFor(isPrivate);
|
|
6827
6916
|
const filename = withPrivacy(storageKey(file.name, mimeType, random2), isPrivate);
|
|
6828
6917
|
const stored = [];
|
|
6829
6918
|
try {
|
|
6830
|
-
await storage.put(filename, file.data, { contentType: mimeType });
|
|
6919
|
+
await storage.put(filename, file.data, { contentType: withCharset(mimeType) });
|
|
6831
6920
|
stored.push(filename);
|
|
6832
6921
|
const dimensions = imageDimensions(file.data, mimeType);
|
|
6833
6922
|
const sizes = await this.resizeImage(
|
|
@@ -6889,7 +6978,7 @@ var EasyCMS = class {
|
|
|
6889
6978
|
]);
|
|
6890
6979
|
const isPrivate = await this.folders.isPrivate(folder);
|
|
6891
6980
|
const storage = this.storageFor(isPrivate);
|
|
6892
|
-
const key = withPrivacy(storageKey(name, type,
|
|
6981
|
+
const key = withPrivacy(storageKey(name, type, randomBytes6(4).toString("hex")), isPrivate);
|
|
6893
6982
|
const upload = storage.uploadURL ? await storage.uploadURL(key, { contentType: type, size, expiresIn: UPLOAD_URL_SECONDS }) : null;
|
|
6894
6983
|
const ticket = this.sign("upload", {
|
|
6895
6984
|
key,
|
|
@@ -7173,7 +7262,7 @@ var EasyCMS = class {
|
|
|
7173
7262
|
const old = [String(doc.filename)];
|
|
7174
7263
|
const copy = async (source, target) => {
|
|
7175
7264
|
const file = await from.get(source);
|
|
7176
|
-
if (file) await to.put(target, file.body, { contentType });
|
|
7265
|
+
if (file) await to.put(target, file.body, { contentType: withCharset(contentType) });
|
|
7177
7266
|
};
|
|
7178
7267
|
await copy(String(doc.filename), filename);
|
|
7179
7268
|
const sizes = {};
|
|
@@ -9048,7 +9137,7 @@ function durationSeconds(value) {
|
|
|
9048
9137
|
}
|
|
9049
9138
|
|
|
9050
9139
|
// src/rest/admin-modules.ts
|
|
9051
|
-
import { createHash as
|
|
9140
|
+
import { createHash as createHash4 } from "crypto";
|
|
9052
9141
|
import { readFile as readFile3 } from "fs/promises";
|
|
9053
9142
|
import { createRequire } from "module";
|
|
9054
9143
|
import { join as join3 } from "path";
|
|
@@ -9089,11 +9178,11 @@ async function readAdminModule(cms, index) {
|
|
|
9089
9178
|
if (!file) return void 0;
|
|
9090
9179
|
const body = await readFile3(file, "utf8").catch(() => void 0);
|
|
9091
9180
|
if (body === void 0) return void 0;
|
|
9092
|
-
return { body, etag: `"${
|
|
9181
|
+
return { body, etag: `"${createHash4("sha1").update(body).digest("base64url")}"` };
|
|
9093
9182
|
}
|
|
9094
9183
|
|
|
9095
9184
|
// src/version.ts
|
|
9096
|
-
var VERSION = true ? "0.
|
|
9185
|
+
var VERSION = true ? "0.65.0" : "0.0.0-dev";
|
|
9097
9186
|
|
|
9098
9187
|
// src/rest/admin-deliveries.ts
|
|
9099
9188
|
var PER_PAGE = 50;
|
|
@@ -10029,7 +10118,8 @@ async function respond(cms, request, url, path, root, options, production) {
|
|
|
10029
10118
|
const headers = new Headers({
|
|
10030
10119
|
"content-type": "application/json; charset=utf-8",
|
|
10031
10120
|
"cache-control": "no-store",
|
|
10032
|
-
"x-content-type-options": "nosniff"
|
|
10121
|
+
"x-content-type-options": "nosniff",
|
|
10122
|
+
...production ? { "strict-transport-security": "max-age=31536000" } : {}
|
|
10033
10123
|
});
|
|
10034
10124
|
try {
|
|
10035
10125
|
if (path === null) throw new HttpError("Not found", 404);
|
|
@@ -10046,7 +10136,8 @@ async function respond(cms, request, url, path, root, options, production) {
|
|
|
10046
10136
|
context,
|
|
10047
10137
|
via: signedIn ? via : null,
|
|
10048
10138
|
token,
|
|
10049
|
-
headers
|
|
10139
|
+
headers,
|
|
10140
|
+
trustProxy: options.trustProxy === true
|
|
10050
10141
|
};
|
|
10051
10142
|
const method = request.method.toUpperCase();
|
|
10052
10143
|
if (method !== "GET" && method !== "HEAD") checkCsrf(cms, ctx);
|
|
@@ -10222,6 +10313,7 @@ async function route(cms, ctx, method, path, options) {
|
|
|
10222
10313
|
case "POST logout": {
|
|
10223
10314
|
if (ctx.token) await cms.auth.logout(ctx.token);
|
|
10224
10315
|
clearSessionCookies(ctx);
|
|
10316
|
+
ctx.headers.set("clear-site-data", '"cache"');
|
|
10225
10317
|
return { body: { message: "Logged out" } };
|
|
10226
10318
|
}
|
|
10227
10319
|
case "GET me": {
|
|
@@ -10916,23 +11008,17 @@ function readToken(request) {
|
|
|
10916
11008
|
return { token, via: token ? "cookie" : null };
|
|
10917
11009
|
}
|
|
10918
11010
|
function readCookie(request, name) {
|
|
10919
|
-
|
|
10920
|
-
if (!header) return void 0;
|
|
10921
|
-
for (const part of header.split(";")) {
|
|
10922
|
-
const eq = part.indexOf("=");
|
|
10923
|
-
if (eq > 0 && part.slice(0, eq).trim() === name)
|
|
10924
|
-
return decodeURIComponent(part.slice(eq + 1).trim());
|
|
10925
|
-
}
|
|
10926
|
-
return void 0;
|
|
11011
|
+
return cookieValue(request.headers.get("cookie"), name);
|
|
10927
11012
|
}
|
|
10928
|
-
function sameHost(origin, request) {
|
|
11013
|
+
function sameHost(origin, request, trustProxy) {
|
|
10929
11014
|
let host;
|
|
10930
11015
|
try {
|
|
10931
11016
|
host = new URL(origin).host;
|
|
10932
11017
|
} catch {
|
|
10933
11018
|
return false;
|
|
10934
11019
|
}
|
|
10935
|
-
const
|
|
11020
|
+
const trusted = trustProxy || !!process.env.VERCEL || !!process.env.NETLIFY;
|
|
11021
|
+
const forwarded = trusted ? request.headers.get("x-forwarded-host")?.split(",")[0]?.trim() : void 0;
|
|
10936
11022
|
return host === (forwarded || request.headers.get("host"));
|
|
10937
11023
|
}
|
|
10938
11024
|
function checkCsrf(cms, ctx) {
|
|
@@ -10940,7 +11026,7 @@ function checkCsrf(cms, ctx) {
|
|
|
10940
11026
|
const fetchSite = ctx.request.headers.get("sec-fetch-site");
|
|
10941
11027
|
const cookie2 = ctx.via === "cookie" && ctx.token !== void 0;
|
|
10942
11028
|
const { cors } = cms.config;
|
|
10943
|
-
const trusted = origin !== null && (origin === ctx.url.origin || sameHost(origin, ctx.request) || cms.config.auth.trustedOrigins.includes(origin) || !cookie2 && (cors === "*" || cors.includes(origin)));
|
|
11029
|
+
const trusted = origin !== null && (origin === ctx.url.origin || sameHost(origin, ctx.request, ctx.trustProxy) || cms.config.auth.trustedOrigins.includes(origin) || !cookie2 && (cors === "*" || cors.includes(origin)));
|
|
10944
11030
|
if (origin !== null && !trusted) throw new ForbiddenError("CSRF check failed: untrusted origin");
|
|
10945
11031
|
if (origin === null && fetchSite === "cross-site")
|
|
10946
11032
|
throw new ForbiddenError("CSRF check failed: cross-site request");
|
|
@@ -10962,7 +11048,7 @@ async function serveFile(cms, key, head) {
|
|
|
10962
11048
|
const file = await cms.storage.get(key);
|
|
10963
11049
|
if (!file) throw new HttpError("Not found", 404);
|
|
10964
11050
|
const extension = key.slice(key.lastIndexOf(".") + 1);
|
|
10965
|
-
const type = TYPE_BY_EXTENSION[extension] ?? "application/octet-stream";
|
|
11051
|
+
const type = withCharset(TYPE_BY_EXTENSION[extension] ?? "application/octet-stream");
|
|
10966
11052
|
return new Response(
|
|
10967
11053
|
head ? null : file.body,
|
|
10968
11054
|
{
|
|
@@ -11005,7 +11091,9 @@ async function servePrivateFile(cms, ctx, key, head) {
|
|
|
11005
11091
|
}
|
|
11006
11092
|
const file = await cms.privateStorage.get(key);
|
|
11007
11093
|
if (!file) throw new HttpError("Not found", 404);
|
|
11008
|
-
const type =
|
|
11094
|
+
const type = withCharset(
|
|
11095
|
+
TYPE_BY_EXTENSION[key.slice(key.lastIndexOf(".") + 1)] ?? "application/octet-stream"
|
|
11096
|
+
);
|
|
11009
11097
|
return new Response(
|
|
11010
11098
|
head ? null : file.body,
|
|
11011
11099
|
{
|
|
@@ -11116,9 +11204,14 @@ function isSecure(ctx) {
|
|
|
11116
11204
|
return ctx.url.protocol === "https:" || process.env.NODE_ENV === "production";
|
|
11117
11205
|
}
|
|
11118
11206
|
function cookie(ctx, name, value, opts) {
|
|
11119
|
-
const
|
|
11207
|
+
const secure = isSecure(ctx);
|
|
11208
|
+
const parts = [
|
|
11209
|
+
`${secure ? hostCookie(name) : name}=${encodeURIComponent(value)}`,
|
|
11210
|
+
"Path=/",
|
|
11211
|
+
"SameSite=Lax"
|
|
11212
|
+
];
|
|
11120
11213
|
if (opts.httpOnly) parts.push("HttpOnly");
|
|
11121
|
-
if (
|
|
11214
|
+
if (secure) parts.push("Secure");
|
|
11122
11215
|
if (opts.maxAge !== void 0) parts.push(`Max-Age=${opts.maxAge}`);
|
|
11123
11216
|
return parts.join("; ");
|
|
11124
11217
|
}
|
|
@@ -11197,6 +11290,9 @@ function setSessionCookies(cms, ctx, session) {
|
|
|
11197
11290
|
function clearSessionCookies(ctx) {
|
|
11198
11291
|
ctx.headers.append("set-cookie", cookie(ctx, SESSION_COOKIE, "", { httpOnly: true, maxAge: 0 }));
|
|
11199
11292
|
ctx.headers.append("set-cookie", cookie(ctx, CSRF_COOKIE, "", { httpOnly: false, maxAge: 0 }));
|
|
11293
|
+
if (isSecure(ctx))
|
|
11294
|
+
for (const name of [SESSION_COOKIE, CSRF_COOKIE])
|
|
11295
|
+
ctx.headers.append("set-cookie", `${name}=; Path=/; Max-Age=0; Secure; SameSite=Lax`);
|
|
11200
11296
|
}
|
|
11201
11297
|
function errorResponse(cms, error, headers, production) {
|
|
11202
11298
|
if (error instanceof EasyCMSError) {
|
|
@@ -11262,6 +11358,7 @@ function createApiHandler(config, options = {}) {
|
|
|
11262
11358
|
if (!handler) {
|
|
11263
11359
|
handler = createRestHandler(cms, {
|
|
11264
11360
|
getClientIp,
|
|
11361
|
+
trustProxy: options.trustProxy === true,
|
|
11265
11362
|
...options.basePath ? { basePath: options.basePath } : {}
|
|
11266
11363
|
});
|
|
11267
11364
|
handlers.set(cms, handler);
|
|
@@ -11356,6 +11453,7 @@ export {
|
|
|
11356
11453
|
configSignature,
|
|
11357
11454
|
copyDatabase,
|
|
11358
11455
|
diskStorage,
|
|
11456
|
+
decryptBackup,
|
|
11359
11457
|
writeBackupFile,
|
|
11360
11458
|
consoleLogger,
|
|
11361
11459
|
silentLogger,
|
|
@@ -271,6 +271,12 @@ function sizeKey(filename, size) {
|
|
|
271
271
|
return filename.replace(/\.([^.]+)$/, `-${size}.$1`);
|
|
272
272
|
}
|
|
273
273
|
var PRIVATE_KEY = /^[\p{L}\p{M}\p{N}-]+\.private(-[\p{L}\p{N}_-]+)?\.[a-z0-9]+$/u;
|
|
274
|
+
function withCharset(type) {
|
|
275
|
+
if (/;\s*charset=/i.test(type)) return type;
|
|
276
|
+
const base = type.split(";")[0]?.trim().toLowerCase() ?? "";
|
|
277
|
+
const text = base.startsWith("text/") || /^application\/(json|xml|javascript|ecmascript)$/.test(base) || /\+(json|xml)$/.test(base);
|
|
278
|
+
return text ? `${type}; charset=utf-8` : type;
|
|
279
|
+
}
|
|
274
280
|
|
|
275
281
|
// src/fields.ts
|
|
276
282
|
var uniqueWithinOf = (field) => field.uniqueWithin === void 0 ? [] : [field.uniqueWithin].flat();
|
|
@@ -534,6 +540,7 @@ export {
|
|
|
534
540
|
withPrivacy,
|
|
535
541
|
sizeKey,
|
|
536
542
|
PRIVATE_KEY,
|
|
543
|
+
withCharset,
|
|
537
544
|
uniqueWithinOf,
|
|
538
545
|
isHasMany,
|
|
539
546
|
mimeAllowedBy,
|
|
@@ -2148,7 +2148,11 @@ interface AuditVerification {
|
|
|
2148
2148
|
checked: number;
|
|
2149
2149
|
/** Entries whose signature does not match: changed after they were written. */
|
|
2150
2150
|
invalid: ID[];
|
|
2151
|
-
/**
|
|
2151
|
+
/**
|
|
2152
|
+
* Missing ids between entries: deleted entries, or (rarely) inserts that failed. Ids are never
|
|
2153
|
+
* reused, so the newest entries deleted show up here once anything is written after them (a
|
|
2154
|
+
* check writes an entry itself). The server's log has each check's last id, to compare with.
|
|
2155
|
+
*/
|
|
2152
2156
|
gaps: number;
|
|
2153
2157
|
at: string;
|
|
2154
2158
|
}
|
|
@@ -3102,6 +3106,13 @@ interface BackupsConfig {
|
|
|
3102
3106
|
readonly dir?: string;
|
|
3103
3107
|
/** Where backups are stored instead, e.g. `s3Storage()` with a private bucket. */
|
|
3104
3108
|
readonly storage?: StorageAdapter;
|
|
3109
|
+
/**
|
|
3110
|
+
* Encrypts the backup files (AES-256-GCM), so a leaked bucket or disk holds nothing readable.
|
|
3111
|
+
* At least 32 characters, e.g. from `process.env.EASY_CMS_BACKUP_KEY`; keep a copy somewhere
|
|
3112
|
+
* other than the backups: without it they can't be restored. Downloads from the admin are
|
|
3113
|
+
* decrypted; `easy-cms backup:decrypt <file>` decrypts one by hand.
|
|
3114
|
+
*/
|
|
3115
|
+
readonly encryptionKey?: string;
|
|
3105
3116
|
/**
|
|
3106
3117
|
* For Postgres: the SQLite adapter that writes the backup file, `sqlite` from
|
|
3107
3118
|
* `@easy-cms/db-sqlite`. SQLite databases back themselves up and don't need it.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { I as ID, ab as Config, au as EasyCMS, bd as LocalizationConfig, aD as Field, aH as FieldError, aS as FilterOptions, af as CreateEasyCMSOptions, bI as ResolvedConfig } from './config-
|
|
1
|
+
import { I as ID, ab as Config, au as EasyCMS, bd as LocalizationConfig, aD as Field, aH as FieldError, aS as FilterOptions, af as CreateEasyCMSOptions, bI as ResolvedConfig } from './config-DE8ez-a9.js';
|
|
2
2
|
|
|
3
|
+
/** Decrypts a file from `encryptBackup`; throws for another key or a changed file. */
|
|
4
|
+
declare function decryptBackup(data: Uint8Array, encryptionKey: string): Uint8Array;
|
|
3
5
|
type BackupState = 'pending' | 'running' | 'done' | 'failed';
|
|
4
6
|
/** One backup as the admin shows it (`GET <api>/admin/backups`). */
|
|
5
7
|
interface AdminBackup {
|
|
@@ -172,6 +174,11 @@ interface RestHandlerOptions {
|
|
|
172
174
|
readonly basePath?: string;
|
|
173
175
|
/** Client IP, used with the email to rate-limit logins. Adapters provide it. */
|
|
174
176
|
readonly getClientIp?: (request: Request) => string | undefined;
|
|
177
|
+
/**
|
|
178
|
+
* Behind a reverse proxy you control: its `X-Forwarded-Host` names the public host (for the
|
|
179
|
+
* CSRF check of a request's Origin). Vercel and Netlify are recognized without it.
|
|
180
|
+
*/
|
|
181
|
+
readonly trustProxy?: boolean;
|
|
175
182
|
}
|
|
176
183
|
type RestHandler = (request: Request) => Promise<Response>;
|
|
177
184
|
/** Creates the REST API as a Web-standard `(Request) => Response` handler. */
|
|
@@ -183,6 +190,7 @@ declare function createRestHandler<C extends Config>(instance: EasyCMS<C>, optio
|
|
|
183
190
|
* use it; the standalone server does.
|
|
184
191
|
*/
|
|
185
192
|
declare function createRootEndpointHandler<C extends Config>(instance: EasyCMS<C>, options?: Pick<RestHandlerOptions, 'getClientIp'>): (request: Request) => Promise<Response | undefined>;
|
|
193
|
+
/** A cookie of the request; the `__Host-` one (set over HTTPS) first. */
|
|
186
194
|
declare function readCookie(request: Request, name: string): string | undefined;
|
|
187
195
|
|
|
188
|
-
export { type AdminBackup as A, type BackupState as B, CONFIG_FILE_NAMES as C, DEFAULT_ADMIN_PATH as D,
|
|
196
|
+
export { type AdminBackup as A, type BackupState as B, CONFIG_FILE_NAMES as C, DEFAULT_ADMIN_PATH as D, readCookie as E, validateFields as F, writeBackupFile as G, type LoadConfigOptions as L, type RestHandler as R, type AdminBackups as a, type ApiHandlerOptions as b, type RestHandlerOptions as c, createApiHandler as d, createRestHandler as e, slugify as f, CSRF_COOKIE as g, CSRF_HEADER as h, DEFAULT_API_PATH as i, DEFAULT_MAX_FILE_SIZE as j, DEFAULT_TOKEN_EXPIRATION as k, loadConfig as l, applyDefaults as m, applyPlugins as n, createRootEndpointHandler as o, decryptBackup as p, fillMissing as q, resolveConfig as r, sharedEasyCMS as s, findConfigFile as t, forwardedClientIp as u, generateSlugs as v, importConfig as w, mergeForUpdate as x, parseId as y, platformClientIp as z };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { P as PaginatedDocs, I as ID, E as EmailSetting } from './config-
|
|
2
|
-
export { A as ADAPTER_API_VERSION, a as ADMIN_ICONS, b as Access, c as AccessArgs, d as AccessOptions, e as AdminBrand, f as AdminCommand, g as AdminComponent, h as AdminConfig, i as AdminFieldArgs, j as AdminIcon, k as AdminLocale, l as AdminPage, m as AdminRole, n as AdminRoleChange, o as AdminRoleField, p as AdminRoleTarget, q as AdminRoleView, r as AdminRoles, s as AdminSso, t as AdminSwitcher, u as AdminViewAccess, v as AfterChangeHook, w as AfterDeleteHook, x as AfterReadHook, y as ApiKeyContext, z as ApiKeyGlobalOperation, B as ApiKeyOperation, C as ApiKeyPermissions, D as ArrayField, F as AuditChange, G as AuditConfig, H as AuditEntry, J as AuditLog, K as AuditPage, L as AuditVerification, M as Auth, N as AuthConfig, O as AuthProvider, Q as AuthProviderProfile, R as AuthProviderRequest, S as AuthUser, T as BackupsConfig, U as BeforeChangeHook, V as BeforeDeleteHook, W as BeforeValidateHook, X as Block, Y as BlocksField, Z as BooleanField, _ as BuiltinApiKeysCollection, $ as BuiltinMediaCollection, a0 as BuiltinMediaFoldersCollection, a1 as BuiltinUsersCollection, a2 as BundleHints, a3 as CliCommand, a4 as CollectionAccess, a5 as CollectionAdmin, a6 as CollectionConfig, a7 as CollectionDocument, a8 as CollectionHooks, a9 as CollectionListAdmin, aa as CollectionSlug, ab as Config, ac as ConfigError, ad as ConfigIssue, ae as ContainerAdmin, af as CreateEasyCMSOptions, ag as CreateInput, ah as CreateMigrationResult, ai as CustomField, aj as CustomFieldTypes, ak as DashboardWidget, al as Database, am as DatabaseAdapter, an as DatabaseInitArgs, ao as DatabaseTransfer, ap as DateField, aq as DepthOptions, ar as DirectUpload, as as DiskStorageOptions, at as DocumentOf, au as EasyCMS, av as EasyCMSError, aw as EmailAdapter, ax as EmailField, ay as EmailMessage, az as Endpoint, aA as EndpointRequest, aB as ErrorCode, aC as FIELD_TYPES, aD as Field, aE as FieldAccess, aF as FieldAccessFn, aG as FieldAdmin, aH as FieldError, aI as FieldRule, aJ as FieldType, aK as FieldTypeBase, aL as FieldTypeDefinition, aM as FieldTypeValidateContext, aN as FieldValidate, aO as FieldValidateContext, aP as FieldValue, aQ as FieldsInput, aR as FieldsValue, aS as FilterOptions, aT as FilterOptionsArgs, aU as FindArgs, aV as FindOptions, aW as FolderLevel, aX as FolderPermissions, aY as ForbiddenError, aZ as GlobalAccess, a_ as GlobalConfig, a$ as GlobalDocument, b0 as GlobalHooks, b1 as GlobalInput, b2 as GlobalSlug, b3 as GroupField, b4 as ImageSize, b5 as IncrementOptions, b6 as InferCollection, b7 as InferGlobal, b8 as JobConfig, b9 as JsonField, ba as Label, bb as LayoutNode, bc as LivePreview, bd as LocalizationConfig, be as Logger, bf as LoginArgs, bg as MediaDocument, bh as MediaSize, bi as MembersConfig, bj as MembersSignup, bk as MigrationInfo, bl as NavGroup, bm as NotFoundError, bn as NumberField, bo as OnRequest, bp as OnRequestArgs, bq as Operation, br as PLUGIN_API_VERSION, bs as PasswordEmail, bt as PasswordEmailArgs, bu as PasswordEmailFn, bv as PasswordLinkOptions, bw as PayloadTooLargeError, bx as Plugin, by as PluginInfo, bz as PluginTypes, bA as PreviewTarget, bB as PreviewURL, bC as QueryError, bD as ROLE_OPERATIONS, bE as RawDocument, bF as ReadOptions, bG as RelationshipField, bH as RequestContext, bI as ResolvedConfig, bJ as RichTextDocument, bK as RichTextField, bL as Role, bM as RoleOperation, bN as RolePermissions, bO as Roles, bP as RoutesConfig, bQ as ScheduledPublish, bR as SchemaError, bS as SchemaMode, bT as SelectField, bU as SelectOption, bV as Session, bW as SidebarPanel, bX as SignupArgs, bY as SignupResult, bZ as SingleSignOn, b_ as SlugField, b$ as SlugOf, c0 as SsoOutcome, c1 as SsoProviderRef, c2 as StorageAdapter, c3 as StoredFile, c4 as TextField, c5 as TextareaField, c6 as TooManyRequestsError, c7 as TypedPlugin, c8 as UnauthorizedError, c9 as UpdateInput, ca as UpdateOptions, cb as UploadConfig, cc as UploadField, cd as UploadFromURLConfig, ce as UserIdentity, cf as ValidationError, cg as Version, ch as VersionSummary, ci as VersionsConfig, cj as WEBHOOK_EVENTS, ck as WebhookConfig, cl as WebhookEvent, cm as WebhookPayload, cn as Where, co as WhereOperators, cp as anyone, cq as consoleEmail, cr as consoleLogger, cs as createEasyCMS, ct as defineConfig, cu as defineFieldType, cv as definePlugin, cw as diskStorage, cx as hasRows, cy as isAdmin, cz as isHasMany, cA as isSignedIn, cB as isStaff, cC as isSystemAdmin, cD as mimeAllowedBy, cE as rowFields, cF as silentLogger, cG as uniqueWithinOf } from './config-
|
|
3
|
-
export { A as ADMIN_API_VERSION, a as AdminCollection, b as AdminComponentRef, c as AdminElementProps, d as AdminField, e as AdminGlobal, f as AdminLayoutNode, g as AdminNavGroup, h as AdminNavItem, i as AdminNavNode, j as AdminPageRef, k as AdminPageRoute, l as AdminSchema, m as AdminWidgetRef, S as SetFieldDetail, w as warnDeprecated } from './plugin-
|
|
4
|
-
export { A as AdminBackup, a as AdminBackups, b as ApiHandlerOptions, B as BackupState, L as LoadConfigOptions, R as RestHandler, c as RestHandlerOptions, d as createApiHandler, e as createRestHandler, l as loadConfig, r as resolveConfig, s as sharedEasyCMS, f as slugify } from './handler-
|
|
1
|
+
import { P as PaginatedDocs, I as ID, E as EmailSetting } from './config-DE8ez-a9.js';
|
|
2
|
+
export { A as ADAPTER_API_VERSION, a as ADMIN_ICONS, b as Access, c as AccessArgs, d as AccessOptions, e as AdminBrand, f as AdminCommand, g as AdminComponent, h as AdminConfig, i as AdminFieldArgs, j as AdminIcon, k as AdminLocale, l as AdminPage, m as AdminRole, n as AdminRoleChange, o as AdminRoleField, p as AdminRoleTarget, q as AdminRoleView, r as AdminRoles, s as AdminSso, t as AdminSwitcher, u as AdminViewAccess, v as AfterChangeHook, w as AfterDeleteHook, x as AfterReadHook, y as ApiKeyContext, z as ApiKeyGlobalOperation, B as ApiKeyOperation, C as ApiKeyPermissions, D as ArrayField, F as AuditChange, G as AuditConfig, H as AuditEntry, J as AuditLog, K as AuditPage, L as AuditVerification, M as Auth, N as AuthConfig, O as AuthProvider, Q as AuthProviderProfile, R as AuthProviderRequest, S as AuthUser, T as BackupsConfig, U as BeforeChangeHook, V as BeforeDeleteHook, W as BeforeValidateHook, X as Block, Y as BlocksField, Z as BooleanField, _ as BuiltinApiKeysCollection, $ as BuiltinMediaCollection, a0 as BuiltinMediaFoldersCollection, a1 as BuiltinUsersCollection, a2 as BundleHints, a3 as CliCommand, a4 as CollectionAccess, a5 as CollectionAdmin, a6 as CollectionConfig, a7 as CollectionDocument, a8 as CollectionHooks, a9 as CollectionListAdmin, aa as CollectionSlug, ab as Config, ac as ConfigError, ad as ConfigIssue, ae as ContainerAdmin, af as CreateEasyCMSOptions, ag as CreateInput, ah as CreateMigrationResult, ai as CustomField, aj as CustomFieldTypes, ak as DashboardWidget, al as Database, am as DatabaseAdapter, an as DatabaseInitArgs, ao as DatabaseTransfer, ap as DateField, aq as DepthOptions, ar as DirectUpload, as as DiskStorageOptions, at as DocumentOf, au as EasyCMS, av as EasyCMSError, aw as EmailAdapter, ax as EmailField, ay as EmailMessage, az as Endpoint, aA as EndpointRequest, aB as ErrorCode, aC as FIELD_TYPES, aD as Field, aE as FieldAccess, aF as FieldAccessFn, aG as FieldAdmin, aH as FieldError, aI as FieldRule, aJ as FieldType, aK as FieldTypeBase, aL as FieldTypeDefinition, aM as FieldTypeValidateContext, aN as FieldValidate, aO as FieldValidateContext, aP as FieldValue, aQ as FieldsInput, aR as FieldsValue, aS as FilterOptions, aT as FilterOptionsArgs, aU as FindArgs, aV as FindOptions, aW as FolderLevel, aX as FolderPermissions, aY as ForbiddenError, aZ as GlobalAccess, a_ as GlobalConfig, a$ as GlobalDocument, b0 as GlobalHooks, b1 as GlobalInput, b2 as GlobalSlug, b3 as GroupField, b4 as ImageSize, b5 as IncrementOptions, b6 as InferCollection, b7 as InferGlobal, b8 as JobConfig, b9 as JsonField, ba as Label, bb as LayoutNode, bc as LivePreview, bd as LocalizationConfig, be as Logger, bf as LoginArgs, bg as MediaDocument, bh as MediaSize, bi as MembersConfig, bj as MembersSignup, bk as MigrationInfo, bl as NavGroup, bm as NotFoundError, bn as NumberField, bo as OnRequest, bp as OnRequestArgs, bq as Operation, br as PLUGIN_API_VERSION, bs as PasswordEmail, bt as PasswordEmailArgs, bu as PasswordEmailFn, bv as PasswordLinkOptions, bw as PayloadTooLargeError, bx as Plugin, by as PluginInfo, bz as PluginTypes, bA as PreviewTarget, bB as PreviewURL, bC as QueryError, bD as ROLE_OPERATIONS, bE as RawDocument, bF as ReadOptions, bG as RelationshipField, bH as RequestContext, bI as ResolvedConfig, bJ as RichTextDocument, bK as RichTextField, bL as Role, bM as RoleOperation, bN as RolePermissions, bO as Roles, bP as RoutesConfig, bQ as ScheduledPublish, bR as SchemaError, bS as SchemaMode, bT as SelectField, bU as SelectOption, bV as Session, bW as SidebarPanel, bX as SignupArgs, bY as SignupResult, bZ as SingleSignOn, b_ as SlugField, b$ as SlugOf, c0 as SsoOutcome, c1 as SsoProviderRef, c2 as StorageAdapter, c3 as StoredFile, c4 as TextField, c5 as TextareaField, c6 as TooManyRequestsError, c7 as TypedPlugin, c8 as UnauthorizedError, c9 as UpdateInput, ca as UpdateOptions, cb as UploadConfig, cc as UploadField, cd as UploadFromURLConfig, ce as UserIdentity, cf as ValidationError, cg as Version, ch as VersionSummary, ci as VersionsConfig, cj as WEBHOOK_EVENTS, ck as WebhookConfig, cl as WebhookEvent, cm as WebhookPayload, cn as Where, co as WhereOperators, cp as anyone, cq as consoleEmail, cr as consoleLogger, cs as createEasyCMS, ct as defineConfig, cu as defineFieldType, cv as definePlugin, cw as diskStorage, cx as hasRows, cy as isAdmin, cz as isHasMany, cA as isSignedIn, cB as isStaff, cC as isSystemAdmin, cD as mimeAllowedBy, cE as rowFields, cF as silentLogger, cG as uniqueWithinOf } from './config-DE8ez-a9.js';
|
|
3
|
+
export { A as ADMIN_API_VERSION, a as AdminCollection, b as AdminComponentRef, c as AdminElementProps, d as AdminField, e as AdminGlobal, f as AdminLayoutNode, g as AdminNavGroup, h as AdminNavItem, i as AdminNavNode, j as AdminPageRef, k as AdminPageRoute, l as AdminSchema, m as AdminWidgetRef, S as SetFieldDetail, w as warnDeprecated } from './plugin-CX7E7RZZ.js';
|
|
4
|
+
export { A as AdminBackup, a as AdminBackups, b as ApiHandlerOptions, B as BackupState, L as LoadConfigOptions, R as RestHandler, c as RestHandlerOptions, d as createApiHandler, e as createRestHandler, l as loadConfig, r as resolveConfig, s as sharedEasyCMS, f as slugify } from './handler-Cm1ehtkm.js';
|
|
5
5
|
export { FieldCondition, matchesCondition } from './conditions.js';
|
|
6
6
|
|
|
7
7
|
type DeliveryKind = 'webhook' | 'email';
|
package/dist/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
sharedEasyCMS,
|
|
24
24
|
silentLogger,
|
|
25
25
|
slugify
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-BGGQ36LR.js";
|
|
27
27
|
import {
|
|
28
28
|
matchesCondition
|
|
29
29
|
} from "./chunk-U266LV4M.js";
|
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
defineFieldType,
|
|
49
49
|
definePlugin,
|
|
50
50
|
warnDeprecated
|
|
51
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-RTKWMVAC.js";
|
|
52
52
|
export {
|
|
53
53
|
ADAPTER_API_VERSION,
|
|
54
54
|
ADMIN_API_VERSION,
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ab as Config, al as Database, bl as NavGroup, a6 as CollectionConfig, I as ID, bE as RawDocument, aD as Field, bI as ResolvedConfig, ad as ConfigIssue } from './config-
|
|
2
|
-
export { cH as API_KEYS, cI as API_KEY_GLOBAL_OPERATIONS, cJ as API_KEY_OPERATIONS, cK as DEFAULT_MAX_VERSIONS, cL as DEFAULT_PASSWORD_EMAILS, cM as FIELD_TYPE_BASES, cN as FOLDER_LEVELS, cO as MediaFolders, cP as SSO_COOKIE, cQ as codeOfStatus, cx as hasRows, cz as isHasMany, cR as keyAllows, cD as mimeAllowedBy, cE as rowFields, cG as uniqueWithinOf } from './config-
|
|
3
|
-
export { C as CONFIG_FILE_NAMES, g as CSRF_COOKIE, h as CSRF_HEADER, D as DEFAULT_ADMIN_PATH, i as DEFAULT_API_PATH, j as DEFAULT_MAX_FILE_SIZE, k as DEFAULT_TOKEN_EXPIRATION, m as applyDefaults, n as applyPlugins, o as createRootEndpointHandler, p as
|
|
1
|
+
import { ab as Config, al as Database, bl as NavGroup, a6 as CollectionConfig, I as ID, bE as RawDocument, aD as Field, bI as ResolvedConfig, ad as ConfigIssue } from './config-DE8ez-a9.js';
|
|
2
|
+
export { cH as API_KEYS, cI as API_KEY_GLOBAL_OPERATIONS, cJ as API_KEY_OPERATIONS, cK as DEFAULT_MAX_VERSIONS, cL as DEFAULT_PASSWORD_EMAILS, cM as FIELD_TYPE_BASES, cN as FOLDER_LEVELS, cO as MediaFolders, cP as SSO_COOKIE, cQ as codeOfStatus, cx as hasRows, cz as isHasMany, cR as keyAllows, cD as mimeAllowedBy, cE as rowFields, cG as uniqueWithinOf } from './config-DE8ez-a9.js';
|
|
3
|
+
export { C as CONFIG_FILE_NAMES, g as CSRF_COOKIE, h as CSRF_HEADER, D as DEFAULT_ADMIN_PATH, i as DEFAULT_API_PATH, j as DEFAULT_MAX_FILE_SIZE, k as DEFAULT_TOKEN_EXPIRATION, m as applyDefaults, n as applyPlugins, o as createRootEndpointHandler, p as decryptBackup, q as fillMissing, t as findConfigFile, u as forwardedClientIp, v as generateSlugs, w as importConfig, x as mergeForUpdate, y as parseId, z as platformClientIp, E as readCookie, F as validateFields, G as writeBackupFile } from './handler-Cm1ehtkm.js';
|
|
4
4
|
export { conditionIssues } from './conditions.js';
|
|
5
5
|
|
|
6
6
|
/** The session cookie the REST API sets at login. */
|
package/dist/internal.js
CHANGED
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
configSignature,
|
|
32
32
|
copyDatabase,
|
|
33
33
|
createRootEndpointHandler,
|
|
34
|
+
decryptBackup,
|
|
34
35
|
fillMissing,
|
|
35
36
|
findConfigFile,
|
|
36
37
|
formToken,
|
|
@@ -52,7 +53,7 @@ import {
|
|
|
52
53
|
verifyPassword,
|
|
53
54
|
verifyTurnstile,
|
|
54
55
|
writeBackupFile
|
|
55
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-BGGQ36LR.js";
|
|
56
57
|
import {
|
|
57
58
|
conditionIssues
|
|
58
59
|
} from "./chunk-U266LV4M.js";
|
|
@@ -70,7 +71,7 @@ import {
|
|
|
70
71
|
rowFields,
|
|
71
72
|
sniffMimeType,
|
|
72
73
|
uniqueWithinOf
|
|
73
|
-
} from "./chunk-
|
|
74
|
+
} from "./chunk-RTKWMVAC.js";
|
|
74
75
|
|
|
75
76
|
// src/typegen.ts
|
|
76
77
|
var pascal = (name) => name.split(/[^A-Za-z0-9]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
@@ -275,6 +276,7 @@ export {
|
|
|
275
276
|
configSignature,
|
|
276
277
|
copyDatabase,
|
|
277
278
|
createRootEndpointHandler,
|
|
279
|
+
decryptBackup,
|
|
278
280
|
expandMimeTypes,
|
|
279
281
|
fillMissing,
|
|
280
282
|
findConfigFile,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ba as Label, aD as Field, I as ID, k as AdminLocale, t as AdminSwitcher, c1 as SsoProviderRef } from './config-
|
|
1
|
+
import { ba as Label, aD as Field, I as ID, k as AdminLocale, t as AdminSwitcher, c1 as SsoProviderRef } from './config-DE8ez-a9.js';
|
|
2
2
|
import { FieldCondition } from './conditions.js';
|
|
3
3
|
|
|
4
4
|
/** A Web Component from an admin module, with its `props` (plain JSON). */
|
package/dist/plugin.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { a as ADMIN_ICONS, g as AdminComponent, j as AdminIcon, S as AuthUser, a3 as CliCommand, a4 as CollectionAccess, a6 as CollectionConfig, ab as Config, ac as ConfigError, ak as DashboardWidget, av as EasyCMSError, az as Endpoint, aA as EndpointRequest, aB as ErrorCode, aD as Field, aE as FieldAccess, aH as FieldError, aK as FieldTypeBase, aL as FieldTypeDefinition, aM as FieldTypeValidateContext, aY as ForbiddenError, a_ as GlobalConfig, I as ID, b8 as JobConfig, ba as Label, bl as NavGroup, bm as NotFoundError, bo as OnRequest, bp as OnRequestArgs, br as PLUGIN_API_VERSION, bw as PayloadTooLargeError, bx as Plugin, by as PluginInfo, bz as PluginTypes, bC as QueryError, bH as RequestContext, c6 as TooManyRequestsError, c7 as TypedPlugin, c8 as UnauthorizedError, cf as ValidationError, cn as Where, cu as defineFieldType, cv as definePlugin } from './config-
|
|
2
|
-
export { A as ADMIN_API_VERSION, c as AdminElementProps, k as AdminPageRoute, S as SetFieldDetail, w as warnDeprecated } from './plugin-
|
|
1
|
+
export { a as ADMIN_ICONS, g as AdminComponent, j as AdminIcon, S as AuthUser, a3 as CliCommand, a4 as CollectionAccess, a6 as CollectionConfig, ab as Config, ac as ConfigError, ak as DashboardWidget, av as EasyCMSError, az as Endpoint, aA as EndpointRequest, aB as ErrorCode, aD as Field, aE as FieldAccess, aH as FieldError, aK as FieldTypeBase, aL as FieldTypeDefinition, aM as FieldTypeValidateContext, aY as ForbiddenError, a_ as GlobalConfig, I as ID, b8 as JobConfig, ba as Label, bl as NavGroup, bm as NotFoundError, bo as OnRequest, bp as OnRequestArgs, br as PLUGIN_API_VERSION, bw as PayloadTooLargeError, bx as Plugin, by as PluginInfo, bz as PluginTypes, bC as QueryError, bH as RequestContext, c6 as TooManyRequestsError, c7 as TypedPlugin, c8 as UnauthorizedError, cf as ValidationError, cn as Where, cu as defineFieldType, cv as definePlugin } from './config-DE8ez-a9.js';
|
|
2
|
+
export { A as ADMIN_API_VERSION, c as AdminElementProps, k as AdminPageRoute, S as SetFieldDetail, w as warnDeprecated } from './plugin-CX7E7RZZ.js';
|
|
3
3
|
import './conditions.js';
|
package/dist/plugin.js
CHANGED