@easy-cms/core 1.6.0 → 1.7.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-GBEWSAZF.js → chunk-AVUGFJ4Q.js} +75 -6
- package/dist/index.js +1 -1
- package/dist/internal.d.ts +10 -2
- package/dist/internal.js +3 -1
- package/package.json +1 -1
|
@@ -2789,6 +2789,42 @@ function configSignature(config) {
|
|
|
2789
2789
|
}
|
|
2790
2790
|
var signatures = /* @__PURE__ */ new WeakMap();
|
|
2791
2791
|
|
|
2792
|
+
// src/health.ts
|
|
2793
|
+
var HEADERS = { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" };
|
|
2794
|
+
var migrated = /* @__PURE__ */ new WeakSet();
|
|
2795
|
+
async function pendingMigrations(cms) {
|
|
2796
|
+
if (migrated.has(cms)) return 0;
|
|
2797
|
+
const pending = (await cms.db.migrationStatus()).filter((m) => !m.applied).length;
|
|
2798
|
+
if (pending === 0) migrated.add(cms);
|
|
2799
|
+
return pending;
|
|
2800
|
+
}
|
|
2801
|
+
async function healthResponse(check3, instance) {
|
|
2802
|
+
const cms = instance;
|
|
2803
|
+
if (check3 === "healthz") return new Response("ok", { headers: { "cache-control": "no-store" } });
|
|
2804
|
+
if (!cms) {
|
|
2805
|
+
return Response.json(
|
|
2806
|
+
{ status: "error", checks: { startup: "error" } },
|
|
2807
|
+
{ status: 503, headers: HEADERS }
|
|
2808
|
+
);
|
|
2809
|
+
}
|
|
2810
|
+
const checks = {};
|
|
2811
|
+
try {
|
|
2812
|
+
await cms.db.count({ collection: USERS });
|
|
2813
|
+
checks.database = "ok";
|
|
2814
|
+
const pending = await pendingMigrations(cms);
|
|
2815
|
+
checks.migrations = pending === 0 ? "ok" : `${pending} pending`;
|
|
2816
|
+
} catch (error) {
|
|
2817
|
+
if (checks.database) checks.migrations = "error";
|
|
2818
|
+
else Object.assign(checks, { database: "error", migrations: "not checked" });
|
|
2819
|
+
cms.logger.error(`Readiness check failed: ${error.message}`, { error });
|
|
2820
|
+
}
|
|
2821
|
+
const ok = Object.values(checks).every((value) => value === "ok");
|
|
2822
|
+
return Response.json(
|
|
2823
|
+
{ status: ok ? "ok" : "error", checks },
|
|
2824
|
+
{ status: ok ? 200 : 503, headers: HEADERS }
|
|
2825
|
+
);
|
|
2826
|
+
}
|
|
2827
|
+
|
|
2792
2828
|
// src/copy.ts
|
|
2793
2829
|
var BATCH = 200;
|
|
2794
2830
|
async function copyDatabase(source, target, options = {}) {
|
|
@@ -4825,7 +4861,9 @@ var RESERVED_SLUGS = /* @__PURE__ */ new Set([
|
|
|
4825
4861
|
"jobs",
|
|
4826
4862
|
"auth",
|
|
4827
4863
|
"migrations",
|
|
4828
|
-
"access"
|
|
4864
|
+
"access",
|
|
4865
|
+
"healthz",
|
|
4866
|
+
"readyz"
|
|
4829
4867
|
]);
|
|
4830
4868
|
var SYSTEM_FIELD_NAMES = /* @__PURE__ */ new Set(["id", "createdAt", "updatedAt"]);
|
|
4831
4869
|
var SLUG_PATTERN = /^[a-z][a-z0-9_-]*$/;
|
|
@@ -5999,6 +6037,8 @@ var RESERVED_ENDPOINT_ROOTS = /* @__PURE__ */ new Set([
|
|
|
5999
6037
|
"media",
|
|
6000
6038
|
"api-keys",
|
|
6001
6039
|
"auth",
|
|
6040
|
+
"healthz",
|
|
6041
|
+
"readyz",
|
|
6002
6042
|
// Kept free for later versions.
|
|
6003
6043
|
"system",
|
|
6004
6044
|
"health",
|
|
@@ -6122,12 +6162,13 @@ function validateEndpoints(config, collectionSlugs, add) {
|
|
|
6122
6162
|
const taken = [
|
|
6123
6163
|
config.routes?.api ?? "/api/cms",
|
|
6124
6164
|
`/${(config.admin?.path ?? "/admin").replace(/^\/+|\/+$/g, "")}`,
|
|
6125
|
-
"/healthz"
|
|
6165
|
+
"/healthz",
|
|
6166
|
+
"/readyz"
|
|
6126
6167
|
].map((p) => p.replace(/\/+$/, "")).find((p) => route2 === p || route2.startsWith(`${p}/`) || p.startsWith(`${route2}/`));
|
|
6127
6168
|
if (taken)
|
|
6128
6169
|
add(
|
|
6129
6170
|
`${path}.path`,
|
|
6130
|
-
`"${route2}" is served by ${taken === "/healthz" ? "the standalone server" : `"${taken}"`}`
|
|
6171
|
+
`"${route2}" is served by ${taken === "/healthz" || taken === "/readyz" ? "the standalone server" : `"${taken}"`}`
|
|
6131
6172
|
);
|
|
6132
6173
|
} else if (RESERVED_ENDPOINT_ROOTS.has(first) || collectionSlugs.has(first)) {
|
|
6133
6174
|
add(
|
|
@@ -9765,7 +9806,7 @@ async function readAdminModule(cms, index) {
|
|
|
9765
9806
|
}
|
|
9766
9807
|
|
|
9767
9808
|
// src/version.ts
|
|
9768
|
-
var VERSION = true ? "1.
|
|
9809
|
+
var VERSION = true ? "1.7.0" : "0.0.0-dev";
|
|
9769
9810
|
|
|
9770
9811
|
// src/rest/handler.ts
|
|
9771
9812
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
@@ -10715,6 +10756,10 @@ function withCors(cms, handle) {
|
|
|
10715
10756
|
return withHeaders(await handle(request), cors);
|
|
10716
10757
|
};
|
|
10717
10758
|
}
|
|
10759
|
+
var HEALTH_CHECKS = /* @__PURE__ */ new Map([
|
|
10760
|
+
["healthz", "healthz"],
|
|
10761
|
+
["readyz", "readyz"]
|
|
10762
|
+
]);
|
|
10718
10763
|
var REQUEST_ID = /^[\w.-]{1,128}$/;
|
|
10719
10764
|
async function respond(cms, request, url, path, root, options, production) {
|
|
10720
10765
|
const started = performance.now();
|
|
@@ -10732,7 +10777,8 @@ async function respond(cms, request, url, path, root, options, production) {
|
|
|
10732
10777
|
} catch {
|
|
10733
10778
|
tagged = withHeaders(response, new Headers({ "x-request-id": requestId }));
|
|
10734
10779
|
}
|
|
10735
|
-
|
|
10780
|
+
const probe = !root && (path === "/healthz" || path === "/readyz");
|
|
10781
|
+
if (!probe && cms.config.logs?.requests !== false && isJsonLogger(cms.logger)) {
|
|
10736
10782
|
auditContext.run(
|
|
10737
10783
|
{ requestId },
|
|
10738
10784
|
() => cms.logger.info(`${request.method} ${url.pathname} ${tagged.status}`, {
|
|
@@ -10756,6 +10802,15 @@ async function respondNow(cms, request, url, path, root, options, production, re
|
|
|
10756
10802
|
try {
|
|
10757
10803
|
if (path === null) throw new HttpError("Not found", 404);
|
|
10758
10804
|
const segments = pathSegments(path);
|
|
10805
|
+
const probe = !root && segments.length === 1 ? HEALTH_CHECKS.get(segments[0]) : void 0;
|
|
10806
|
+
if (probe) {
|
|
10807
|
+
const method2 = request.method.toUpperCase();
|
|
10808
|
+
if (method2 !== "GET" && method2 !== "HEAD") {
|
|
10809
|
+
headers.set("allow", "GET, HEAD");
|
|
10810
|
+
throw new HttpError("Method not allowed", 405);
|
|
10811
|
+
}
|
|
10812
|
+
return await healthResponse(probe, cms);
|
|
10813
|
+
}
|
|
10759
10814
|
const { token, via } = readToken(request);
|
|
10760
10815
|
const signedIn = token ? await cms.auth.verify(token) : null;
|
|
10761
10816
|
if (!signedIn && via === "bearer" && token?.startsWith("ecms_"))
|
|
@@ -12012,8 +12067,21 @@ function platformClientIp(headers) {
|
|
|
12012
12067
|
function createApiHandler(config, options = {}) {
|
|
12013
12068
|
const handlers = /* @__PURE__ */ new WeakMap();
|
|
12014
12069
|
const getClientIp = options.getClientIp ?? (options.trustProxy ? (r) => forwardedClientIp(r.headers.get("x-forwarded-for")) : (r) => platformClientIp(r.headers));
|
|
12070
|
+
const base = (options.basePath ?? config.routes?.api ?? "/api/cms").replace(/\/+$/, "");
|
|
12015
12071
|
return async (request) => {
|
|
12016
|
-
|
|
12072
|
+
let cms;
|
|
12073
|
+
try {
|
|
12074
|
+
cms = await sharedEasyCMS(config);
|
|
12075
|
+
} catch (error) {
|
|
12076
|
+
const { pathname } = new URL(request.url);
|
|
12077
|
+
const probe = pathname === `${base}/healthz` ? "healthz" : pathname === `${base}/readyz` ? "readyz" : null;
|
|
12078
|
+
if (!probe) throw error;
|
|
12079
|
+
if (probe === "readyz") {
|
|
12080
|
+
const logger = logFormat(config.logs) === "json" ? jsonLogger() : consoleLogger;
|
|
12081
|
+
logger.error(`Readiness check failed: ${error.message}`, { error });
|
|
12082
|
+
}
|
|
12083
|
+
return healthResponse(probe, null);
|
|
12084
|
+
}
|
|
12017
12085
|
let handler = handlers.get(cms);
|
|
12018
12086
|
if (!handler) {
|
|
12019
12087
|
handler = createRestHandler(cms, {
|
|
@@ -12111,6 +12179,7 @@ export {
|
|
|
12111
12179
|
validateFields,
|
|
12112
12180
|
consoleEmail,
|
|
12113
12181
|
configSignature,
|
|
12182
|
+
healthResponse,
|
|
12114
12183
|
copyDatabase,
|
|
12115
12184
|
diskStorage,
|
|
12116
12185
|
decryptBackup,
|
package/dist/index.js
CHANGED
package/dist/internal.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ab as Config, al as Database, bo as NavGroup, a6 as CollectionConfig, I as ID, bH as RawDocument, aD as Field, bL as ResolvedConfig, ad as ConfigIssue } from './config-CzW6kBeb.js';
|
|
1
|
+
import { ab as Config, al as Database, au as EasyCMS, bo as NavGroup, a6 as CollectionConfig, I as ID, bH as RawDocument, aD as Field, bL as ResolvedConfig, ad as ConfigIssue } from './config-CzW6kBeb.js';
|
|
2
2
|
export { cL as API_KEYS, cM as API_KEY_GLOBAL_OPERATIONS, cN as API_KEY_OPERATIONS, cO as DEFAULT_MAX_VERSIONS, cP as DEFAULT_PASSWORD_EMAILS, cQ as FIELD_TYPE_BASES, cR as FOLDER_LEVELS, cS as MediaFolders, cT as SSO_COOKIE, cU as codeOfStatus, cA as hasRows, cC as isHasMany, cV as isJsonLogger, cW as keyAllows, cX as logFormat, cH as mimeAllowedBy, cI as rowFields, cK as uniqueWithinOf } from './config-CzW6kBeb.js';
|
|
3
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-BsBQ-2qf.js';
|
|
4
4
|
export { conditionIssues } from './conditions.js';
|
|
@@ -51,6 +51,14 @@ declare function copyDatabase(source: Database, target: Database, options?: {
|
|
|
51
51
|
onTable?: (progress: CopyProgress) => void;
|
|
52
52
|
}): Promise<CopyResult>;
|
|
53
53
|
|
|
54
|
+
type HealthCheck = 'healthz' | 'readyz';
|
|
55
|
+
/**
|
|
56
|
+
* `healthz`: the process answers (liveness), without touching the database. `readyz`: the CMS
|
|
57
|
+
* started, its database answers and no migration is pending (readiness); `503` otherwise, naming
|
|
58
|
+
* the check that failed, with the error in the log only. `cms` is `null` when it failed to start.
|
|
59
|
+
*/
|
|
60
|
+
declare function healthResponse<C extends Config>(check: HealthCheck, instance: EasyCMS<C> | null): Promise<Response>;
|
|
61
|
+
|
|
54
62
|
/** File types Easy CMS recognizes from their contents (FR-UPL-03). */
|
|
55
63
|
declare const EXTENSIONS: Readonly<Record<string, string>>;
|
|
56
64
|
/**
|
|
@@ -133,4 +141,4 @@ declare const BUILTIN_COLLECTIONS: readonly ["users", "media"];
|
|
|
133
141
|
/** Checks a config and returns every problem found. An empty array means it is valid. */
|
|
134
142
|
declare function validateConfig(config: Config): ConfigIssue[];
|
|
135
143
|
|
|
136
|
-
export { BUILTIN_COLLECTIONS, BUILTIN_NAV, type CopyProgress, type CopyResult, DEFAULT_DEPTH, EXTENSIONS, INTERNAL_COLLECTIONS, MAX_DEPTH, MEDIA, MEDIA_FOLDERS, MIME_GROUPS, MIN_PASSWORD_LENGTH, MIN_SECRET_LENGTH, SESSION_COOKIE, checkFormToken, checkRenamedOptions, configSignature, copyDatabase, expandMimeTypes, formToken, generateTypes, hashPassword, honeypotName, imageDimensions, mimeAllowed, populate, rateKeys, resolveAdminModule, singularize, sniffMimeType, validateConfig, verifyPassword, verifyTurnstile };
|
|
144
|
+
export { BUILTIN_COLLECTIONS, BUILTIN_NAV, type CopyProgress, type CopyResult, DEFAULT_DEPTH, EXTENSIONS, type HealthCheck, INTERNAL_COLLECTIONS, MAX_DEPTH, MEDIA, MEDIA_FOLDERS, MIME_GROUPS, MIN_PASSWORD_LENGTH, MIN_SECRET_LENGTH, SESSION_COOKIE, checkFormToken, checkRenamedOptions, configSignature, copyDatabase, expandMimeTypes, formToken, generateTypes, hashPassword, healthResponse, honeypotName, imageDimensions, mimeAllowed, populate, rateKeys, resolveAdminModule, singularize, sniffMimeType, validateConfig, verifyPassword, verifyTurnstile };
|
package/dist/internal.js
CHANGED
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
forwardedClientIp,
|
|
39
39
|
generateSlugs,
|
|
40
40
|
hashPassword,
|
|
41
|
+
healthResponse,
|
|
41
42
|
honeypotName,
|
|
42
43
|
importConfig,
|
|
43
44
|
isJsonLogger,
|
|
@@ -55,7 +56,7 @@ import {
|
|
|
55
56
|
verifyPassword,
|
|
56
57
|
verifyTurnstile,
|
|
57
58
|
writeBackupFile
|
|
58
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-AVUGFJ4Q.js";
|
|
59
60
|
import {
|
|
60
61
|
conditionIssues
|
|
61
62
|
} from "./chunk-U266LV4M.js";
|
|
@@ -288,6 +289,7 @@ export {
|
|
|
288
289
|
generateTypes,
|
|
289
290
|
hasRows,
|
|
290
291
|
hashPassword,
|
|
292
|
+
healthResponse,
|
|
291
293
|
honeypotName,
|
|
292
294
|
imageDimensions,
|
|
293
295
|
importConfig,
|