@actuate-media/cms-core 0.38.0 → 0.39.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/__tests__/api/ai-status.test.d.ts +2 -0
- package/dist/__tests__/api/ai-status.test.d.ts.map +1 -0
- package/dist/__tests__/api/ai-status.test.js +110 -0
- package/dist/__tests__/api/ai-status.test.js.map +1 -0
- package/dist/__tests__/api/users-invite.test.d.ts +2 -0
- package/dist/__tests__/api/users-invite.test.d.ts.map +1 -0
- package/dist/__tests__/api/users-invite.test.js +167 -0
- package/dist/__tests__/api/users-invite.test.js.map +1 -0
- package/dist/__tests__/auth/invite.test.d.ts +2 -0
- package/dist/__tests__/auth/invite.test.d.ts.map +1 -0
- package/dist/__tests__/auth/invite.test.js +151 -0
- package/dist/__tests__/auth/invite.test.js.map +1 -0
- package/dist/__tests__/auth/reset.test.js +52 -1
- package/dist/__tests__/auth/reset.test.js.map +1 -1
- package/dist/__tests__/email/sender.test.d.ts +2 -0
- package/dist/__tests__/email/sender.test.d.ts.map +1 -0
- package/dist/__tests__/email/sender.test.js +33 -0
- package/dist/__tests__/email/sender.test.js.map +1 -0
- package/dist/__tests__/middleware.test.js +30 -0
- package/dist/__tests__/middleware.test.js.map +1 -1
- package/dist/__tests__/setup/index.test.js +37 -2
- package/dist/__tests__/setup/index.test.js.map +1 -1
- package/dist/api/handlers.d.ts.map +1 -1
- package/dist/api/handlers.js +102 -1
- package/dist/api/handlers.js.map +1 -1
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +1 -0
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/invite-email.d.ts +12 -0
- package/dist/auth/invite-email.d.ts.map +1 -0
- package/dist/auth/invite-email.js +57 -0
- package/dist/auth/invite-email.js.map +1 -0
- package/dist/auth/invite.d.ts +37 -0
- package/dist/auth/invite.d.ts.map +1 -0
- package/dist/auth/invite.js +104 -0
- package/dist/auth/invite.js.map +1 -0
- package/dist/auth/reset.d.ts +6 -0
- package/dist/auth/reset.d.ts.map +1 -1
- package/dist/auth/reset.js +11 -3
- package/dist/auth/reset.js.map +1 -1
- package/dist/email/sender.d.ts +35 -0
- package/dist/email/sender.d.ts.map +1 -0
- package/dist/email/sender.js +25 -0
- package/dist/email/sender.js.map +1 -0
- package/dist/forms/notify.d.ts.map +1 -1
- package/dist/forms/notify.js +3 -2
- package/dist/forms/notify.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +60 -1
- package/dist/middleware.js.map +1 -1
- package/dist/setup/index.d.ts +14 -0
- package/dist/setup/index.d.ts.map +1 -1
- package/dist/setup/index.js +29 -3
- package/dist/setup/index.js.map +1 -1
- package/package.json +6 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach, vi } from 'vitest';
|
|
2
|
+
import { setEmailSender, getRegisteredEmailSender, resolveEmailSender } from '../../email/sender.js';
|
|
3
|
+
import { setActuateConfig } from '../../config/runtime.js';
|
|
4
|
+
afterEach(() => {
|
|
5
|
+
setEmailSender(null);
|
|
6
|
+
setActuateConfig(undefined);
|
|
7
|
+
});
|
|
8
|
+
describe('email sender registry', () => {
|
|
9
|
+
it('round-trips a registered sender', () => {
|
|
10
|
+
expect(getRegisteredEmailSender()).toBeNull();
|
|
11
|
+
const sender = { send: vi.fn(async () => { }) };
|
|
12
|
+
setEmailSender(sender);
|
|
13
|
+
expect(getRegisteredEmailSender()).toBe(sender);
|
|
14
|
+
setEmailSender(null);
|
|
15
|
+
expect(getRegisteredEmailSender()).toBeNull();
|
|
16
|
+
});
|
|
17
|
+
it('resolves null when nothing is configured', () => {
|
|
18
|
+
expect(resolveEmailSender()).toBeNull();
|
|
19
|
+
});
|
|
20
|
+
it('resolves the registered sender when no platform adapter is set', () => {
|
|
21
|
+
const sender = { send: vi.fn(async () => { }) };
|
|
22
|
+
setEmailSender(sender);
|
|
23
|
+
expect(resolveEmailSender()).toBe(sender);
|
|
24
|
+
});
|
|
25
|
+
it('prefers the platform adapter over a registered sender', () => {
|
|
26
|
+
const registered = { send: vi.fn(async () => { }) };
|
|
27
|
+
const platform = { send: vi.fn(async () => { }) };
|
|
28
|
+
setEmailSender(registered);
|
|
29
|
+
setActuateConfig({ platform: { email: platform } });
|
|
30
|
+
expect(resolveEmailSender()).toBe(platform);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=sender.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sender.test.js","sourceRoot":"","sources":["../../../src/__tests__/email/sender.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC5D,OAAO,EAAE,cAAc,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AACpG,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAE1D,SAAS,CAAC,GAAG,EAAE;IACb,cAAc,CAAC,IAAI,CAAC,CAAA;IACpB,gBAAgB,CAAC,SAAkB,CAAC,CAAA;AACtC,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,CAAC,wBAAwB,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAA;QAC7C,MAAM,MAAM,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,GAAE,CAAC,CAAC,EAAE,CAAA;QAC9C,cAAc,CAAC,MAAM,CAAC,CAAA;QACtB,MAAM,CAAC,wBAAwB,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC/C,cAAc,CAAC,IAAI,CAAC,CAAA;QACpB,MAAM,CAAC,wBAAwB,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAA;IAC/C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,kBAAkB,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAA;IACzC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACxE,MAAM,MAAM,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,GAAE,CAAC,CAAC,EAAE,CAAA;QAC9C,cAAc,CAAC,MAAM,CAAC,CAAA;QACtB,MAAM,CAAC,kBAAkB,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IAC3C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,UAAU,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,GAAE,CAAC,CAAC,EAAE,CAAA;QAClD,MAAM,QAAQ,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,GAAE,CAAC,CAAC,EAAE,CAAA;QAChD,cAAc,CAAC,UAAU,CAAC,CAAA;QAC1B,gBAAgB,CAAC,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAW,CAAC,CAAA;QAC5D,MAAM,CAAC,kBAAkB,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IAC7C,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -47,4 +47,34 @@ describe('createCMSMiddleware route boundary', () => {
|
|
|
47
47
|
expect(res?.status).toBe(403);
|
|
48
48
|
});
|
|
49
49
|
});
|
|
50
|
+
describe('createCMSMiddleware missing CMS_SECRET', () => {
|
|
51
|
+
const origSecret = process.env.CMS_SECRET;
|
|
52
|
+
beforeEach(() => {
|
|
53
|
+
process.env.CMS_SECRET = '';
|
|
54
|
+
});
|
|
55
|
+
afterEach(() => {
|
|
56
|
+
process.env.CMS_SECRET = origSecret;
|
|
57
|
+
});
|
|
58
|
+
function req(path) {
|
|
59
|
+
return new Request(`https://example.com${path}`);
|
|
60
|
+
}
|
|
61
|
+
it('returns an actionable 503 HTML page for the admin UI', async () => {
|
|
62
|
+
const mw = createCMSMiddleware({ admin: { path: '/admin' } });
|
|
63
|
+
const res = await mw(req('/admin'));
|
|
64
|
+
expect(res?.status).toBe(503);
|
|
65
|
+
expect(res?.headers.get('Content-Type')).toContain('text/html');
|
|
66
|
+
const body = await res.text();
|
|
67
|
+
expect(body).toContain('CMS_SECRET');
|
|
68
|
+
// Must not leak any secret value — only the variable name + remediation.
|
|
69
|
+
expect(body).toContain('randomBytes');
|
|
70
|
+
});
|
|
71
|
+
it('still lets the setup API through so the wizard can run before a secret exists', async () => {
|
|
72
|
+
const mw = createCMSMiddleware({ admin: { path: '/admin' } });
|
|
73
|
+
expect(await mw(req('/api/cms/setup/status'))).toBeNull();
|
|
74
|
+
});
|
|
75
|
+
it('does not block public routes when the secret is missing', async () => {
|
|
76
|
+
const mw = createCMSMiddleware({ admin: { path: '/admin' } });
|
|
77
|
+
expect(await mw(req('/about'))).toBeNull();
|
|
78
|
+
});
|
|
79
|
+
});
|
|
50
80
|
//# sourceMappingURL=middleware.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.test.js","sourceRoot":"","sources":["../../src/__tests__/middleware.test.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"middleware.test.js","sourceRoot":"","sources":["../../src/__tests__/middleware.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import { checkSetupRequired, createInitialAdmin, isDatabaseUnavailableError, } from '../../setup/index.js';
|
|
2
|
+
import { checkSetupRequired, createInitialAdmin, isDatabaseUnavailableError, isSchemaMissingError, } from '../../setup/index.js';
|
|
3
3
|
function connectionError(code) {
|
|
4
4
|
const err = new Error(`Can't reach database server`);
|
|
5
5
|
err.code = code;
|
|
6
6
|
return err;
|
|
7
7
|
}
|
|
8
|
+
function schemaMissingError() {
|
|
9
|
+
const err = new Error('The table `public.actuate_users` does not exist in the current database.');
|
|
10
|
+
err.code = 'P2021';
|
|
11
|
+
return err;
|
|
12
|
+
}
|
|
8
13
|
describe('isDatabaseUnavailableError', () => {
|
|
9
14
|
it('detects Prisma connection codes', () => {
|
|
10
15
|
expect(isDatabaseUnavailableError(connectionError('P1001'))).toBe(true);
|
|
@@ -37,10 +42,34 @@ describe('checkSetupRequired', () => {
|
|
|
37
42
|
const db = { user: { count: vi.fn().mockRejectedValue(connectionError('ECONNREFUSED')) } };
|
|
38
43
|
await expect(checkSetupRequired(db)).rejects.toThrow(/reach database/i);
|
|
39
44
|
});
|
|
40
|
-
it('still treats non-connection errors as setup-required', async () => {
|
|
45
|
+
it('still treats unknown non-connection errors as setup-required', async () => {
|
|
41
46
|
const db = { user: { count: vi.fn().mockRejectedValue(new Error('table missing')) } };
|
|
42
47
|
await expect(checkSetupRequired(db)).resolves.toEqual({ setupRequired: true, userCount: 0 });
|
|
43
48
|
});
|
|
49
|
+
it('reports migrationsRequired when the tables do not exist yet', async () => {
|
|
50
|
+
const db = { user: { count: vi.fn().mockRejectedValue(schemaMissingError()) } };
|
|
51
|
+
await expect(checkSetupRequired(db)).resolves.toEqual({
|
|
52
|
+
setupRequired: false,
|
|
53
|
+
userCount: 0,
|
|
54
|
+
migrationsRequired: true,
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
describe('isSchemaMissingError', () => {
|
|
59
|
+
it('detects Prisma P2021/P2022 codes', () => {
|
|
60
|
+
expect(isSchemaMissingError(schemaMissingError())).toBe(true);
|
|
61
|
+
const colErr = new Error('column missing');
|
|
62
|
+
colErr.code = 'P2022';
|
|
63
|
+
expect(isSchemaMissingError(colErr)).toBe(true);
|
|
64
|
+
});
|
|
65
|
+
it('detects table-missing errors by message', () => {
|
|
66
|
+
expect(isSchemaMissingError(new Error('relation "actuate_users" does not exist'))).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
it('does not flag connection errors or ordinary errors', () => {
|
|
69
|
+
expect(isSchemaMissingError(connectionError('P1001'))).toBe(false);
|
|
70
|
+
expect(isSchemaMissingError(new Error('something else'))).toBe(false);
|
|
71
|
+
expect(isSchemaMissingError(null)).toBe(false);
|
|
72
|
+
});
|
|
44
73
|
});
|
|
45
74
|
describe('createInitialAdmin', () => {
|
|
46
75
|
const validInput = {
|
|
@@ -59,6 +88,12 @@ describe('createInitialAdmin', () => {
|
|
|
59
88
|
};
|
|
60
89
|
await expect(createInitialAdmin(db, validInput)).rejects.toThrow(/reach database/i);
|
|
61
90
|
});
|
|
91
|
+
it('rethrows schema-missing errors so the route can prompt for migrations', async () => {
|
|
92
|
+
const db = {
|
|
93
|
+
$transaction: vi.fn().mockRejectedValue(schemaMissingError()),
|
|
94
|
+
};
|
|
95
|
+
await expect(createInitialAdmin(db, validInput)).rejects.toThrow(/does not exist/i);
|
|
96
|
+
});
|
|
62
97
|
it('returns a friendly error when setup was already completed', async () => {
|
|
63
98
|
const db = {
|
|
64
99
|
$transaction: vi.fn(async (fn) => fn({ user: { count: vi.fn().mockResolvedValue(1) } })),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../../src/__tests__/setup/index.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AACjD,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,0BAA0B,
|
|
1
|
+
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../../src/__tests__/setup/index.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/api/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/api/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAggC5C,iFAAiF;AACjF,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AA8JD,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAS9E;AAoTD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA+lVzD"}
|
package/dist/api/handlers.js
CHANGED
|
@@ -3,7 +3,8 @@ import { populateRelations, parsePopulateParam } from '../fields/relations.js';
|
|
|
3
3
|
import { verifyPassword, hashPassword, needsRehash, compareToDummyHash } from '../auth/password.js';
|
|
4
4
|
import { createSession, verifySession, revokeSession } from '../auth/session.js';
|
|
5
5
|
import { createPasswordReset, executePasswordReset } from '../auth/reset.js';
|
|
6
|
-
import {
|
|
6
|
+
import { createUserInvite } from '../auth/invite.js';
|
|
7
|
+
import { checkSetupRequired, createInitialAdmin, isSchemaMissingError } from '../setup/index.js';
|
|
7
8
|
import { getDB } from '../db.js';
|
|
8
9
|
import { generateCodeVerifier, generateCodeChallenge, generateState, generateOAuthNonce, getAuthorizationUrl, handleOAuthCallback, } from '../auth/oauth.js';
|
|
9
10
|
import { createMfaPendingToken, verifyMfaPendingToken, computeRequestFingerprint, } from '../auth/mfa-pending.js';
|
|
@@ -1076,6 +1077,9 @@ function requireRole(role, allowedRoles) {
|
|
|
1076
1077
|
return null;
|
|
1077
1078
|
}
|
|
1078
1079
|
const loginLimiter = createRateLimiter({ maxRequests: 5, windowMs: 15 * 60 * 1000 });
|
|
1080
|
+
// Admin-gated, but bounds invitation-email volume (and accidental loops) per
|
|
1081
|
+
// admin: enough to onboard a team, low enough to deter abuse.
|
|
1082
|
+
const inviteLimiter = createRateLimiter({ maxRequests: 20, windowMs: 60 * 60 * 1000 });
|
|
1079
1083
|
const totpLimiter = createRateLimiter({ maxRequests: 10, windowMs: 15 * 60 * 1000 });
|
|
1080
1084
|
const formLimiterGlobal = createRateLimiter({ maxRequests: 10, windowMs: 60_000 });
|
|
1081
1085
|
const aiGenerateLimiter = createRateLimiter({ maxRequests: 20, windowMs: 60 * 60 * 1000 });
|
|
@@ -1687,6 +1691,7 @@ export function registerCMSRoutes(router) {
|
|
|
1687
1691
|
const cmsConfig = getActuateConfig();
|
|
1688
1692
|
await createPasswordReset(d, email.toLowerCase().trim(), {
|
|
1689
1693
|
siteUrl,
|
|
1694
|
+
adminPath: cmsConfig?.admin?.path ?? '/admin',
|
|
1690
1695
|
platform: cmsConfig?.platform,
|
|
1691
1696
|
});
|
|
1692
1697
|
await logEvent({
|
|
@@ -3471,6 +3476,9 @@ export function registerCMSRoutes(router) {
|
|
|
3471
3476
|
return json({ data: result }, 201);
|
|
3472
3477
|
}
|
|
3473
3478
|
catch (err) {
|
|
3479
|
+
if (isSchemaMissingError(err)) {
|
|
3480
|
+
return errorResponse('Database not migrated: the Actuate CMS tables do not exist yet. Run `prisma migrate deploy` against your database, then try again.', 503);
|
|
3481
|
+
}
|
|
3474
3482
|
return internalError(err);
|
|
3475
3483
|
}
|
|
3476
3484
|
});
|
|
@@ -4327,6 +4335,61 @@ export function registerCMSRoutes(router) {
|
|
|
4327
4335
|
return internalError(err, 'users search');
|
|
4328
4336
|
}
|
|
4329
4337
|
});
|
|
4338
|
+
router.post('/users/invite', async (request) => {
|
|
4339
|
+
try {
|
|
4340
|
+
const auth = await requireAuth(request);
|
|
4341
|
+
if (auth.error)
|
|
4342
|
+
return auth.error;
|
|
4343
|
+
const roleErr = requireRole(auth.session.role, ADMIN_ROLES);
|
|
4344
|
+
if (roleErr)
|
|
4345
|
+
return roleErr;
|
|
4346
|
+
if (!(await checkRateLimitAsync(inviteLimiter, `invite:${auth.session.userId}`))) {
|
|
4347
|
+
return errorResponse('Too many invitations. Please try again later.', 429);
|
|
4348
|
+
}
|
|
4349
|
+
const d = db();
|
|
4350
|
+
if (!hasModel(d, 'user'))
|
|
4351
|
+
return modelNotAvailable('user');
|
|
4352
|
+
const body = (await request.json());
|
|
4353
|
+
if (!body.email)
|
|
4354
|
+
return errorResponse('Email is required', 400);
|
|
4355
|
+
const cmsConfig = getActuateConfig();
|
|
4356
|
+
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL ?? new URL(request.url).origin;
|
|
4357
|
+
const inviter = await d.user.findUnique({
|
|
4358
|
+
where: { id: auth.session.userId },
|
|
4359
|
+
select: { name: true },
|
|
4360
|
+
});
|
|
4361
|
+
const result = await createUserInvite(d, { email: body.email, name: body.name, role: body.role ?? 'AUTHOR' }, {
|
|
4362
|
+
siteUrl,
|
|
4363
|
+
adminPath: cmsConfig?.admin?.path ?? '/admin',
|
|
4364
|
+
invitedByName: inviter?.name ?? undefined,
|
|
4365
|
+
siteName: cmsConfig?.admin?.branding?.name ??
|
|
4366
|
+
undefined,
|
|
4367
|
+
});
|
|
4368
|
+
if (!result.success) {
|
|
4369
|
+
return errorResponse(result.error ?? 'Could not create invitation', 400);
|
|
4370
|
+
}
|
|
4371
|
+
await logEvent({
|
|
4372
|
+
event: 'user_invited',
|
|
4373
|
+
userId: auth.session.userId,
|
|
4374
|
+
details: {
|
|
4375
|
+
targetEmail: result.user?.email,
|
|
4376
|
+
role: result.user?.role,
|
|
4377
|
+
emailed: result.emailed,
|
|
4378
|
+
},
|
|
4379
|
+
});
|
|
4380
|
+
return json({
|
|
4381
|
+
data: {
|
|
4382
|
+
success: true,
|
|
4383
|
+
user: result.user,
|
|
4384
|
+
emailed: result.emailed,
|
|
4385
|
+
inviteUrl: result.inviteUrl,
|
|
4386
|
+
},
|
|
4387
|
+
});
|
|
4388
|
+
}
|
|
4389
|
+
catch (err) {
|
|
4390
|
+
return internalError(err, 'user invite');
|
|
4391
|
+
}
|
|
4392
|
+
});
|
|
4330
4393
|
router.delete('/users/:id', async (request, params) => {
|
|
4331
4394
|
try {
|
|
4332
4395
|
const auth = await requireAuth(request);
|
|
@@ -9053,6 +9116,44 @@ export function registerCMSRoutes(router) {
|
|
|
9053
9116
|
return internalError(err, 'security GET');
|
|
9054
9117
|
}
|
|
9055
9118
|
});
|
|
9119
|
+
// Read-only AI status. The dashboard cannot configure AI — it is wired up
|
|
9120
|
+
// server-side via `aiPlugin({ provider, apiKey })` reading environment
|
|
9121
|
+
// variables. This endpoint just reports whether the plugin is installed and
|
|
9122
|
+
// a provider is configured so the admin UI can show an honest status instead
|
|
9123
|
+
// of a decorative key field. Never returns secret values.
|
|
9124
|
+
router.get('/ai/status', async (request) => {
|
|
9125
|
+
try {
|
|
9126
|
+
const auth = await requireAuth(request);
|
|
9127
|
+
if (auth.error)
|
|
9128
|
+
return auth.error;
|
|
9129
|
+
const adminErr = requireAdminScope(auth.session);
|
|
9130
|
+
if (adminErr)
|
|
9131
|
+
return adminErr;
|
|
9132
|
+
let installed = false;
|
|
9133
|
+
let configured = false;
|
|
9134
|
+
try {
|
|
9135
|
+
const aiMod = await importAIPlugin();
|
|
9136
|
+
installed = true;
|
|
9137
|
+
if (typeof aiMod?.getAIProvider === 'function') {
|
|
9138
|
+
configured = aiMod.getAIProvider() != null;
|
|
9139
|
+
}
|
|
9140
|
+
}
|
|
9141
|
+
catch {
|
|
9142
|
+
installed = false;
|
|
9143
|
+
}
|
|
9144
|
+
// Best-effort provider hint from the environment. We only expose which
|
|
9145
|
+
// provider env var is present, never the key value itself.
|
|
9146
|
+
const provider = process.env.ANTHROPIC_API_KEY
|
|
9147
|
+
? 'anthropic'
|
|
9148
|
+
: process.env.OPENAI_API_KEY
|
|
9149
|
+
? 'openai'
|
|
9150
|
+
: null;
|
|
9151
|
+
return json({ data: { installed, configured, provider } });
|
|
9152
|
+
}
|
|
9153
|
+
catch (err) {
|
|
9154
|
+
return internalError(err, 'ai status');
|
|
9155
|
+
}
|
|
9156
|
+
});
|
|
9056
9157
|
router.put('/security', async (request) => {
|
|
9057
9158
|
try {
|
|
9058
9159
|
const auth = await requireAuth(request);
|