@actuate-media/cms-core 0.2.3 → 0.3.1
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/LICENSE +21 -21
- package/dist/__tests__/auth/session.test.js +1 -1
- package/dist/__tests__/auth/session.test.js.map +1 -1
- package/dist/api/handler-factory.d.ts.map +1 -1
- package/dist/api/handler-factory.js +85 -68
- package/dist/api/handler-factory.js.map +1 -1
- package/dist/api/handlers.d.ts +1 -1
- package/dist/api/handlers.d.ts.map +1 -1
- package/dist/api/handlers.js +212 -31
- package/dist/api/handlers.js.map +1 -1
- package/dist/api/index.d.ts +2 -19
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +1 -54
- package/dist/api/index.js.map +1 -1
- package/dist/api/router.d.ts +20 -0
- package/dist/api/router.d.ts.map +1 -0
- package/dist/api/router.js +55 -0
- package/dist/api/router.js.map +1 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +2 -0
- package/dist/config/index.js.map +1 -1
- package/dist/i18n/index.js +8 -8
- package/dist/media/optimize.d.ts +2 -3
- package/dist/media/optimize.d.ts.map +1 -1
- package/dist/media/optimize.js +10 -1
- package/dist/media/optimize.js.map +1 -1
- package/dist/next.d.ts +3 -3
- package/dist/next.d.ts.map +1 -1
- package/dist/next.js +8 -8
- package/dist/next.js.map +1 -1
- package/dist/search/index.js +22 -22
- package/dist/security/anomaly-detection.d.ts +1 -1
- package/dist/security/anomaly-detection.d.ts.map +1 -1
- package/dist/security/anomaly-detection.js +27 -5
- package/dist/security/anomaly-detection.js.map +1 -1
- package/dist/security/reauth.d.ts +1 -1
- package/dist/security/reauth.d.ts.map +1 -1
- package/dist/security/reauth.js +12 -4
- package/dist/security/reauth.js.map +1 -1
- package/dist/security/sanitize.d.ts +1 -1
- package/dist/security/sanitize.d.ts.map +1 -1
- package/dist/security/sanitize.js +9 -11
- package/dist/security/sanitize.js.map +1 -1
- package/dist/security/webhook.d.ts +1 -1
- package/dist/security/webhook.d.ts.map +1 -1
- package/dist/security/webhook.js +24 -3
- package/dist/security/webhook.js.map +1 -1
- package/package.json +3 -2
- package/prisma/cms-schema.prisma +237 -237
- package/prisma/migrations/0001_init/migration.sql +384 -384
- package/prisma/migrations/0002_folders/migration.sql +39 -39
- package/prisma/migrations/0003_search_and_webhooks/migration.sql +50 -50
- package/prisma/migrations/migration_lock.toml +3 -3
- package/prisma/schema.prisma +485 -485
- package/prisma/seed.ts +82 -82
- package/generated/browser.ts +0 -109
- package/generated/client.ts +0 -133
- package/generated/commonInputTypes.ts +0 -709
- package/generated/enums.ts +0 -125
- package/generated/internal/class.ts +0 -376
- package/generated/internal/prismaNamespace.ts +0 -2617
- package/generated/internal/prismaNamespaceBrowser.ts +0 -611
- package/generated/models/ApiKey.ts +0 -1550
- package/generated/models/AuditLog.ts +0 -1206
- package/generated/models/BackupRecord.ts +0 -1250
- package/generated/models/ContentLock.ts +0 -1472
- package/generated/models/ContentTemplate.ts +0 -1416
- package/generated/models/Document.ts +0 -3005
- package/generated/models/Folder.ts +0 -1904
- package/generated/models/FormSubmission.ts +0 -1200
- package/generated/models/InAppNotification.ts +0 -1457
- package/generated/models/Media.ts +0 -2340
- package/generated/models/MediaUsage.ts +0 -1472
- package/generated/models/OAuthAccount.ts +0 -1463
- package/generated/models/Redirect.ts +0 -1284
- package/generated/models/Session.ts +0 -1492
- package/generated/models/Site.ts +0 -1206
- package/generated/models/User.ts +0 -3513
- package/generated/models/Version.ts +0 -1511
- package/generated/models/WorkflowState.ts +0 -1514
- package/generated/models.ts +0 -29
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Actuate Media
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Actuate Media
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -51,7 +51,7 @@ describe('refreshSession', () => {
|
|
|
51
51
|
it('returns a new valid token with the same payload', async () => {
|
|
52
52
|
const original = await createSession(TEST_PAYLOAD, {
|
|
53
53
|
secret: TEST_SECRET,
|
|
54
|
-
maxAge:
|
|
54
|
+
maxAge: 10,
|
|
55
55
|
});
|
|
56
56
|
await new Promise((resolve) => setTimeout(resolve, 1100));
|
|
57
57
|
const refreshed = await refreshSession(original, { secret: TEST_SECRET });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.test.js","sourceRoot":"","sources":["../../../src/__tests__/auth/session.test.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"session.test.js","sourceRoot":"","sources":["../../../src/__tests__/auth/session.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler-factory.d.ts","sourceRoot":"","sources":["../../src/api/handler-factory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"handler-factory.d.ts","sourceRoot":"","sources":["../../src/api/handler-factory.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7C;AAID,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,uBAA4B,IA4DtC,SAAS,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CA0GnE"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { createApiRouter } from './router.js';
|
|
2
3
|
import { registerCMSRoutes, parseCookieHeader } from './handlers.js';
|
|
3
4
|
import { initDB, isDBInitialized } from '../db.js';
|
|
4
5
|
import { validateCsrfToken } from '../security/index.js';
|
|
5
6
|
import { createRateLimiter } from '../security/rate-limit.js';
|
|
6
7
|
import { autoSeedAdmin } from '../setup/index.js';
|
|
7
|
-
|
|
8
|
+
const _require = createRequire(import.meta.url);
|
|
9
|
+
const { version: CMS_CORE_VERSION } = _require('../../package.json');
|
|
8
10
|
let cachedGraphQL = null;
|
|
9
|
-
const CMS_CORE_VERSION = '0.1.0';
|
|
10
11
|
export function handleActuateAPI(options = {}) {
|
|
11
12
|
globalThis.__actuateCoreVersion = CMS_CORE_VERSION;
|
|
12
13
|
if (options.config) {
|
|
@@ -57,83 +58,99 @@ export function handleActuateAPI(options = {}) {
|
|
|
57
58
|
registerCMSRoutes(router);
|
|
58
59
|
let dbChecked = false;
|
|
59
60
|
return async function handler(request) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
initDB(client);
|
|
64
|
-
autoSeedAdmin(client).catch(() => { });
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
if (!dbChecked) {
|
|
68
|
-
dbChecked = true;
|
|
69
|
-
try {
|
|
70
|
-
const client = options.prismaClient;
|
|
61
|
+
try {
|
|
62
|
+
if (!isDBInitialized()) {
|
|
63
|
+
const client = options.prismaClient ?? (options.prismaClientGetter ? await options.prismaClientGetter() : null);
|
|
71
64
|
if (client) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (missing.length > 0) {
|
|
75
|
-
console.warn(`[Actuate CMS] Prisma client is missing models: ${missing.join(', ')}. `
|
|
76
|
-
+ 'CMS features that depend on these models will return empty results. '
|
|
77
|
-
+ 'Add the CMS schema to your project: see @actuate-media/cms-core/prisma/schema');
|
|
78
|
-
}
|
|
65
|
+
initDB(client);
|
|
66
|
+
autoSeedAdmin(client).catch(() => { });
|
|
79
67
|
}
|
|
80
68
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return new Response(JSON.stringify({ error: 'Too many requests' }), {
|
|
97
|
-
status: 429,
|
|
98
|
-
headers: {
|
|
99
|
-
'Content-Type': 'application/json',
|
|
100
|
-
'Retry-After': rateLimitResult.retryAfter?.toString() ?? '60',
|
|
101
|
-
},
|
|
102
|
-
});
|
|
69
|
+
if (!dbChecked) {
|
|
70
|
+
dbChecked = true;
|
|
71
|
+
try {
|
|
72
|
+
const client = options.prismaClient;
|
|
73
|
+
if (client) {
|
|
74
|
+
const required = ['document', 'user', 'session', 'media'];
|
|
75
|
+
const missing = required.filter(m => !(m in client));
|
|
76
|
+
if (missing.length > 0) {
|
|
77
|
+
console.warn(`[Actuate CMS] Prisma client is missing models: ${missing.join(', ')}. `
|
|
78
|
+
+ 'CMS features that depend on these models will return empty results. '
|
|
79
|
+
+ 'Add the CMS schema to your project: see @actuate-media/cms-core/prisma/schema');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch { }
|
|
103
84
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
85
|
+
const url = new URL(request.url);
|
|
86
|
+
const originalPath = url.pathname;
|
|
87
|
+
if (originalPath === '/api/cms/graphql' || originalPath.startsWith('/api/cms/graphql')) {
|
|
88
|
+
const secret = process.env.CMS_SECRET;
|
|
89
|
+
if (!secret || secret.length < 32) {
|
|
90
|
+
return new Response(JSON.stringify({ error: 'Server misconfiguration' }), {
|
|
91
|
+
status: 500,
|
|
110
92
|
headers: { 'Content-Type': 'application/json' },
|
|
111
93
|
});
|
|
112
94
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
95
|
+
const clientIp = request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ?? 'unknown';
|
|
96
|
+
const rateLimitResult = await rateLimiter.check(clientIp);
|
|
97
|
+
if (!rateLimitResult.allowed) {
|
|
98
|
+
return new Response(JSON.stringify({ error: 'Too many requests' }), {
|
|
99
|
+
status: 429,
|
|
100
|
+
headers: {
|
|
101
|
+
'Content-Type': 'application/json',
|
|
102
|
+
'Retry-After': rateLimitResult.retryAfter?.toString() ?? '60',
|
|
103
|
+
},
|
|
104
|
+
});
|
|
118
105
|
}
|
|
119
|
-
|
|
106
|
+
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(request.method)) {
|
|
107
|
+
const csrfToken = request.headers.get('x-csrf-token');
|
|
108
|
+
const csrfCookie = parseCookieHeader(request.headers.get('cookie') ?? '')['actuate_csrf'];
|
|
109
|
+
if (!csrfToken || !csrfCookie || !validateCsrfToken(csrfToken, csrfCookie)) {
|
|
110
|
+
return new Response(JSON.stringify({ error: 'Invalid CSRF token' }), {
|
|
111
|
+
status: 403,
|
|
112
|
+
headers: { 'Content-Type': 'application/json' },
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const config = globalThis.__actuateConfig;
|
|
117
|
+
if (config) {
|
|
118
|
+
if (!cachedGraphQL) {
|
|
119
|
+
const { createGraphQLHandler } = await import('../graphql/index.js');
|
|
120
|
+
cachedGraphQL = createGraphQLHandler({ config, secret });
|
|
121
|
+
}
|
|
122
|
+
return cachedGraphQL.handle(request);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const cmsPrefix = '/api/cms';
|
|
126
|
+
if (originalPath.startsWith(cmsPrefix)) {
|
|
127
|
+
const strippedPath = originalPath.slice(cmsPrefix.length) || '/';
|
|
128
|
+
const rewrittenUrl = new URL(strippedPath, url.origin);
|
|
129
|
+
rewrittenUrl.search = url.search;
|
|
130
|
+
const rewrittenRequest = new Request(rewrittenUrl.toString(), {
|
|
131
|
+
method: request.method,
|
|
132
|
+
headers: request.headers,
|
|
133
|
+
body: request.body,
|
|
134
|
+
// @ts-ignore -- duplex is needed for streaming bodies
|
|
135
|
+
duplex: 'half',
|
|
136
|
+
});
|
|
137
|
+
return router.handle(rewrittenRequest);
|
|
120
138
|
}
|
|
139
|
+
return router.handle(request);
|
|
121
140
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
141
|
+
catch (err) {
|
|
142
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
143
|
+
const stack = err instanceof Error ? err.stack : undefined;
|
|
144
|
+
console.error(`[Actuate CMS] Unhandled error in API handler: ${message}`, stack);
|
|
145
|
+
const isDev = process.env.NODE_ENV !== 'production';
|
|
146
|
+
return new Response(JSON.stringify({
|
|
147
|
+
error: 'Internal CMS error',
|
|
148
|
+
...(isDev && { detail: message, stack }),
|
|
149
|
+
}), {
|
|
150
|
+
status: 500,
|
|
151
|
+
headers: { 'Content-Type': 'application/json' },
|
|
133
152
|
});
|
|
134
|
-
return router.handle(rewrittenRequest);
|
|
135
153
|
}
|
|
136
|
-
return router.handle(request);
|
|
137
154
|
};
|
|
138
155
|
}
|
|
139
156
|
//# sourceMappingURL=handler-factory.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler-factory.js","sourceRoot":"","sources":["../../src/api/handler-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"handler-factory.js","sourceRoot":"","sources":["../../src/api/handler-factory.ts"],"names":[],"mappings":"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"}
|
package/dist/api/handlers.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ApiRouter } from './
|
|
1
|
+
import type { ApiRouter } from './router.js';
|
|
2
2
|
export declare function parseCookieHeader(cookieHeader: string): Record<string, string>;
|
|
3
3
|
export declare function registerCMSRoutes(router: ApiRouter): void;
|
|
4
4
|
//# sourceMappingURL=handlers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/api/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/api/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAkN7C,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAS9E;AAiED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAk8EzD"}
|
package/dist/api/handlers.js
CHANGED
|
@@ -64,9 +64,18 @@ function modelNotAvailable(name) {
|
|
|
64
64
|
}
|
|
65
65
|
async function safeCount(model, where) {
|
|
66
66
|
try {
|
|
67
|
-
if (!model
|
|
67
|
+
if (!model || typeof model !== 'object')
|
|
68
68
|
return 0;
|
|
69
|
-
|
|
69
|
+
let countFn;
|
|
70
|
+
try {
|
|
71
|
+
countFn = model.count;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
if (typeof countFn !== 'function')
|
|
77
|
+
return 0;
|
|
78
|
+
return await countFn.call(model, where ? { where } : undefined);
|
|
70
79
|
}
|
|
71
80
|
catch {
|
|
72
81
|
return 0;
|
|
@@ -74,9 +83,18 @@ async function safeCount(model, where) {
|
|
|
74
83
|
}
|
|
75
84
|
async function safeFindMany(model, args) {
|
|
76
85
|
try {
|
|
77
|
-
if (!model
|
|
86
|
+
if (!model || typeof model !== 'object')
|
|
87
|
+
return [];
|
|
88
|
+
let findManyFn;
|
|
89
|
+
try {
|
|
90
|
+
findManyFn = model.findMany;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
if (typeof findManyFn !== 'function')
|
|
78
96
|
return [];
|
|
79
|
-
return await
|
|
97
|
+
return await findManyFn.call(model, args);
|
|
80
98
|
}
|
|
81
99
|
catch {
|
|
82
100
|
return [];
|
|
@@ -107,22 +125,44 @@ function isAllowedStorageUrl(url) {
|
|
|
107
125
|
const ALLOWED_SORT_FIELDS = new Set([
|
|
108
126
|
'createdAt', 'updatedAt', 'publishedAt', 'status', 'collection',
|
|
109
127
|
]);
|
|
128
|
+
let _secretMissing = false;
|
|
129
|
+
let _secretWarningLogged = false;
|
|
110
130
|
function getSessionSecret() {
|
|
111
131
|
const secret = process.env.CMS_SECRET
|
|
112
132
|
?? process.env.CMS_SESSION_SECRET
|
|
113
133
|
?? globalThis.__actuateConfig?.secret;
|
|
114
134
|
if (!secret) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
135
|
+
_secretMissing = true;
|
|
136
|
+
if (!_secretWarningLogged) {
|
|
137
|
+
_secretWarningLogged = true;
|
|
138
|
+
console.error('[Actuate CMS] Missing CMS secret. Set the CMS_SECRET environment variable (min 32 characters) '
|
|
139
|
+
+ 'or pass `secret` in your actuate.config.ts. '
|
|
140
|
+
+ 'Generate one with: node -e "console.log(require(\'crypto\').randomBytes(32).toString(\'hex\'))" '
|
|
141
|
+
+ '-- All authenticated API routes will return 503 until this is configured.');
|
|
142
|
+
}
|
|
143
|
+
throw new Error('CMS secret not configured');
|
|
118
144
|
}
|
|
119
145
|
if (secret.length < 32) {
|
|
120
146
|
throw new Error('[Actuate CMS] CMS secret must be at least 32 characters (got ' + secret.length + '). '
|
|
121
147
|
+ 'Generate a secure value with: node -e "console.log(require(\'crypto\').randomBytes(32).toString(\'hex\'))"');
|
|
122
148
|
}
|
|
149
|
+
_secretMissing = false;
|
|
123
150
|
return secret;
|
|
124
151
|
}
|
|
152
|
+
function isSecretMissing() {
|
|
153
|
+
if (_secretMissing)
|
|
154
|
+
return true;
|
|
155
|
+
try {
|
|
156
|
+
getSessionSecret();
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
125
163
|
async function extractSession(request) {
|
|
164
|
+
if (isSecretMissing())
|
|
165
|
+
return null;
|
|
126
166
|
let token;
|
|
127
167
|
const authHeader = request.headers.get('Authorization');
|
|
128
168
|
if (authHeader?.startsWith('Bearer ')) {
|
|
@@ -163,6 +203,14 @@ export function parseCookieHeader(cookieHeader) {
|
|
|
163
203
|
return cookies;
|
|
164
204
|
}
|
|
165
205
|
async function requireAuth(request) {
|
|
206
|
+
if (isSecretMissing()) {
|
|
207
|
+
return {
|
|
208
|
+
error: json({
|
|
209
|
+
error: 'CMS secret not configured. Set CMS_SECRET or CMS_SESSION_SECRET environment variable (min 32 characters).',
|
|
210
|
+
code: 'MISSING_SECRET',
|
|
211
|
+
}, 503),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
166
214
|
const session = await extractSession(request);
|
|
167
215
|
if (!session) {
|
|
168
216
|
return { error: errorResponse('Unauthorized', 401) };
|
|
@@ -206,16 +254,39 @@ class ModelNotAvailableError extends Error {
|
|
|
206
254
|
export function registerCMSRoutes(router) {
|
|
207
255
|
const rawDb = () => getDB();
|
|
208
256
|
const db = () => {
|
|
209
|
-
|
|
257
|
+
let d;
|
|
258
|
+
try {
|
|
259
|
+
d = rawDb();
|
|
260
|
+
}
|
|
261
|
+
catch (err) {
|
|
262
|
+
console.error('[actuate][db] Failed to obtain database client:', err instanceof Error ? err.message : err);
|
|
263
|
+
return new Proxy({}, {
|
|
264
|
+
get(_target, prop) {
|
|
265
|
+
if (typeof prop !== 'string' || prop.startsWith('$') || prop === 'then')
|
|
266
|
+
return undefined;
|
|
267
|
+
return new Proxy({}, {
|
|
268
|
+
get() { throw new ModelNotAvailableError(String(prop)); },
|
|
269
|
+
});
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
}
|
|
210
273
|
return new Proxy(d, {
|
|
211
274
|
get(target, prop) {
|
|
212
|
-
|
|
213
|
-
|
|
275
|
+
if (typeof prop !== 'string')
|
|
276
|
+
return Reflect.get(target, prop);
|
|
277
|
+
if (prop.startsWith('$') || prop === 'then')
|
|
278
|
+
return Reflect.get(target, prop);
|
|
279
|
+
let val;
|
|
280
|
+
try {
|
|
281
|
+
val = target[prop];
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
val = undefined;
|
|
285
|
+
}
|
|
286
|
+
if (val !== undefined && val !== null)
|
|
214
287
|
return val;
|
|
215
288
|
return new Proxy({}, {
|
|
216
|
-
get() {
|
|
217
|
-
throw new ModelNotAvailableError(String(prop));
|
|
218
|
-
},
|
|
289
|
+
get() { throw new ModelNotAvailableError(String(prop)); },
|
|
219
290
|
});
|
|
220
291
|
},
|
|
221
292
|
});
|
|
@@ -253,17 +324,17 @@ export function registerCMSRoutes(router) {
|
|
|
253
324
|
});
|
|
254
325
|
});
|
|
255
326
|
router.get('/docs', async () => {
|
|
256
|
-
const html = `<!DOCTYPE html>
|
|
257
|
-
<html>
|
|
258
|
-
<head>
|
|
259
|
-
<title>Actuate CMS API Reference</title>
|
|
260
|
-
<meta charset="utf-8" />
|
|
261
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
262
|
-
</head>
|
|
263
|
-
<body>
|
|
264
|
-
<script id="api-reference" data-url="/api/cms/openapi.json"></script>
|
|
265
|
-
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
|
|
266
|
-
</body>
|
|
327
|
+
const html = `<!DOCTYPE html>
|
|
328
|
+
<html>
|
|
329
|
+
<head>
|
|
330
|
+
<title>Actuate CMS API Reference</title>
|
|
331
|
+
<meta charset="utf-8" />
|
|
332
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
333
|
+
</head>
|
|
334
|
+
<body>
|
|
335
|
+
<script id="api-reference" data-url="/api/cms/openapi.json"></script>
|
|
336
|
+
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
|
|
337
|
+
</body>
|
|
267
338
|
</html>`;
|
|
268
339
|
return new Response(html, {
|
|
269
340
|
status: 200,
|
|
@@ -1205,13 +1276,117 @@ export function registerCMSRoutes(router) {
|
|
|
1205
1276
|
// ---------------------------------------------------------------------------
|
|
1206
1277
|
// Stats route
|
|
1207
1278
|
// ---------------------------------------------------------------------------
|
|
1279
|
+
// ---------------------------------------------------------------------------
|
|
1280
|
+
// Health endpoint -- reports available models and CMS version
|
|
1281
|
+
// ---------------------------------------------------------------------------
|
|
1282
|
+
const CMS_EXPECTED_MODELS = [
|
|
1283
|
+
'document', 'media', 'user', 'session', 'version',
|
|
1284
|
+
'folder', 'redirect', 'formSubmission', 'auditLog',
|
|
1285
|
+
'webhookEndpoint', 'webhookDeliveryLog',
|
|
1286
|
+
];
|
|
1287
|
+
router.get('/health', async () => {
|
|
1288
|
+
const cmsVersion = globalThis.__actuateCoreVersion ?? '0.0.0';
|
|
1289
|
+
const models = {};
|
|
1290
|
+
let d;
|
|
1291
|
+
try {
|
|
1292
|
+
d = db();
|
|
1293
|
+
}
|
|
1294
|
+
catch {
|
|
1295
|
+
for (const m of CMS_EXPECTED_MODELS)
|
|
1296
|
+
models[m] = false;
|
|
1297
|
+
return json({
|
|
1298
|
+
data: {
|
|
1299
|
+
status: 'degraded',
|
|
1300
|
+
version: cmsVersion,
|
|
1301
|
+
secretConfigured: !isSecretMissing(),
|
|
1302
|
+
models,
|
|
1303
|
+
databaseConnected: false,
|
|
1304
|
+
},
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
|
+
let dbConnected = true;
|
|
1308
|
+
for (const modelName of CMS_EXPECTED_MODELS) {
|
|
1309
|
+
try {
|
|
1310
|
+
const model = d[modelName];
|
|
1311
|
+
if (model && typeof model.count === 'function') {
|
|
1312
|
+
await model.count({ take: 0 });
|
|
1313
|
+
models[modelName] = true;
|
|
1314
|
+
}
|
|
1315
|
+
else {
|
|
1316
|
+
models[modelName] = false;
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
catch {
|
|
1320
|
+
models[modelName] = false;
|
|
1321
|
+
if (modelName === CMS_EXPECTED_MODELS[0]) {
|
|
1322
|
+
dbConnected = false;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
const allAvailable = Object.values(models).every(Boolean);
|
|
1327
|
+
return json({
|
|
1328
|
+
data: {
|
|
1329
|
+
status: allAvailable ? 'healthy' : 'degraded',
|
|
1330
|
+
version: cmsVersion,
|
|
1331
|
+
secretConfigured: !isSecretMissing(),
|
|
1332
|
+
models,
|
|
1333
|
+
databaseConnected: dbConnected,
|
|
1334
|
+
},
|
|
1335
|
+
});
|
|
1336
|
+
});
|
|
1337
|
+
// ---------------------------------------------------------------------------
|
|
1338
|
+
// Stats route
|
|
1339
|
+
// ---------------------------------------------------------------------------
|
|
1340
|
+
const EMPTY_STATS = {
|
|
1341
|
+
totalDocuments: 0,
|
|
1342
|
+
totalMedia: 0,
|
|
1343
|
+
totalUsers: 0,
|
|
1344
|
+
collectionCounts: {},
|
|
1345
|
+
recentDocuments: [],
|
|
1346
|
+
};
|
|
1347
|
+
async function safeGroupBy(model) {
|
|
1348
|
+
try {
|
|
1349
|
+
if (!model || typeof model !== 'object')
|
|
1350
|
+
return {};
|
|
1351
|
+
let groupByFn;
|
|
1352
|
+
try {
|
|
1353
|
+
groupByFn = model.groupBy;
|
|
1354
|
+
}
|
|
1355
|
+
catch {
|
|
1356
|
+
return {};
|
|
1357
|
+
}
|
|
1358
|
+
if (typeof groupByFn !== 'function')
|
|
1359
|
+
return {};
|
|
1360
|
+
const groups = await groupByFn.call(model, {
|
|
1361
|
+
by: ['collection'],
|
|
1362
|
+
where: { deletedAt: null },
|
|
1363
|
+
_count: { _all: true },
|
|
1364
|
+
});
|
|
1365
|
+
const result = {};
|
|
1366
|
+
for (const g of groups) {
|
|
1367
|
+
result[g.collection] = g._count._all;
|
|
1368
|
+
}
|
|
1369
|
+
return result;
|
|
1370
|
+
}
|
|
1371
|
+
catch {
|
|
1372
|
+
return {};
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1208
1375
|
router.get('/stats', async (request) => {
|
|
1209
1376
|
try {
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1377
|
+
if (!isSecretMissing()) {
|
|
1378
|
+
const auth = await requireAuth(request);
|
|
1379
|
+
if (auth.error)
|
|
1380
|
+
return auth.error;
|
|
1381
|
+
}
|
|
1382
|
+
let d;
|
|
1383
|
+
try {
|
|
1384
|
+
d = db();
|
|
1385
|
+
}
|
|
1386
|
+
catch {
|
|
1387
|
+
return json({ data: EMPTY_STATS });
|
|
1388
|
+
}
|
|
1389
|
+
const [docResult, mediaResult, userResult, recentResult, groupResult] = await Promise.allSettled([
|
|
1215
1390
|
safeCount(d.document, { deletedAt: null }),
|
|
1216
1391
|
safeCount(d.media),
|
|
1217
1392
|
safeCount(d.user),
|
|
@@ -1219,19 +1394,25 @@ export function registerCMSRoutes(router) {
|
|
|
1219
1394
|
where: { deletedAt: null },
|
|
1220
1395
|
orderBy: { updatedAt: 'desc' },
|
|
1221
1396
|
take: 10,
|
|
1397
|
+
select: {
|
|
1398
|
+
id: true, title: true, status: true, collection: true,
|
|
1399
|
+
updatedAt: true, createdById: true,
|
|
1400
|
+
},
|
|
1222
1401
|
}),
|
|
1402
|
+
safeGroupBy(d.document),
|
|
1223
1403
|
]);
|
|
1224
1404
|
return json({
|
|
1225
1405
|
data: {
|
|
1226
1406
|
totalDocuments: docResult.status === 'fulfilled' ? docResult.value : 0,
|
|
1227
1407
|
totalMedia: mediaResult.status === 'fulfilled' ? mediaResult.value : 0,
|
|
1228
1408
|
totalUsers: userResult.status === 'fulfilled' ? userResult.value : 0,
|
|
1409
|
+
collectionCounts: groupResult.status === 'fulfilled' ? groupResult.value : {},
|
|
1229
1410
|
recentDocuments: recentResult.status === 'fulfilled' ? recentResult.value : [],
|
|
1230
1411
|
},
|
|
1231
1412
|
});
|
|
1232
1413
|
}
|
|
1233
|
-
catch
|
|
1234
|
-
return json({ data:
|
|
1414
|
+
catch {
|
|
1415
|
+
return json({ data: EMPTY_STATS });
|
|
1235
1416
|
}
|
|
1236
1417
|
});
|
|
1237
1418
|
// ---------------------------------------------------------------------------
|