@actuate-media/cms-admin 0.43.0 → 0.44.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/CHANGELOG.md +9 -0
- package/dist/__tests__/views/email-settings.render.test.d.ts +2 -0
- package/dist/__tests__/views/email-settings.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/email-settings.render.test.js +184 -0
- package/dist/__tests__/views/email-settings.render.test.js.map +1 -0
- package/dist/actuate-admin.css +1 -1
- package/dist/views/Settings.d.ts.map +1 -1
- package/dist/views/Settings.js +5 -3
- package/dist/views/Settings.js.map +1 -1
- package/dist/views/settings/EmailSettingsTab.d.ts +4 -0
- package/dist/views/settings/EmailSettingsTab.d.ts.map +1 -0
- package/dist/views/settings/EmailSettingsTab.js +75 -0
- package/dist/views/settings/EmailSettingsTab.js.map +1 -0
- package/dist/views/settings/useEmailSettings.d.ts +58 -0
- package/dist/views/settings/useEmailSettings.d.ts.map +1 -0
- package/dist/views/settings/useEmailSettings.js +209 -0
- package/dist/views/settings/useEmailSettings.js.map +1 -0
- package/package.json +2 -2
- package/src/__tests__/views/email-settings.render.test.tsx +225 -0
- package/src/views/Settings.tsx +13 -0
- package/src/views/settings/EmailSettingsTab.tsx +436 -0
- package/src/views/settings/useEmailSettings.ts +296 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# @actuate-media/cms-admin
|
|
2
2
|
|
|
3
|
+
## 0.44.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- d6b21e7: Add Settings > Email: configure the transactional email provider from the admin UI.
|
|
8
|
+
- **cms-admin**: new Email tab in Settings with a provider picker (Resend, SMTP.com, Amazon SES, generic SMTP), per-provider credential fields, masked stored secrets (last-4 only, write-only inputs), a "Send test email" button, and notices for environment/platform-managed configuration.
|
|
9
|
+
- **cms-core**: new `__email_config__` store with API keys and SMTP passwords encrypted at rest (`CMS_ENCRYPTION_KEY`), `GET/PUT /email/settings` + `POST /email/settings/test` admin routes (test sends only to the signed-in admin's own address, rate-limited), and a DB-aware `resolveEmailSenderAsync()` used by invites, password resets, and form notifications. Resolution order: platform adapter → admin-configured settings → environment variables.
|
|
10
|
+
- **plugin-email**: registers a provider factory with cms-core so admin-configured settings build real Resend/SMTP.com/SES/SMTP senders without env vars.
|
|
11
|
+
|
|
3
12
|
## 0.43.0
|
|
4
13
|
|
|
5
14
|
### Minor Changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-settings.render.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/views/email-settings.render.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// @vitest-environment happy-dom
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
5
|
+
const EMPTY_RESPONSE = {
|
|
6
|
+
settings: { provider: null, hasApiKey: false, hasSmtpPass: false },
|
|
7
|
+
envProvider: null,
|
|
8
|
+
factoryAvailable: true,
|
|
9
|
+
envSenderActive: false,
|
|
10
|
+
platformAdapter: false,
|
|
11
|
+
};
|
|
12
|
+
let getResponse = EMPTY_RESPONSE;
|
|
13
|
+
let putResponse = null;
|
|
14
|
+
let testResponse = {
|
|
15
|
+
data: { success: true, sentTo: 'admin@example.com' },
|
|
16
|
+
status: 200,
|
|
17
|
+
};
|
|
18
|
+
const cmsApi = vi.fn(async (endpoint, options) => {
|
|
19
|
+
const method = options?.method ?? 'GET';
|
|
20
|
+
if (endpoint === '/email/settings' && method === 'GET') {
|
|
21
|
+
return { data: getResponse, status: 200 };
|
|
22
|
+
}
|
|
23
|
+
if (endpoint === '/email/settings' && method === 'PUT') {
|
|
24
|
+
return putResponse
|
|
25
|
+
? { data: putResponse, status: 200 }
|
|
26
|
+
: { error: 'unexpected PUT', status: 500 };
|
|
27
|
+
}
|
|
28
|
+
if (endpoint === '/email/settings/test' && method === 'POST') {
|
|
29
|
+
return testResponse;
|
|
30
|
+
}
|
|
31
|
+
return { data: {}, status: 200 };
|
|
32
|
+
});
|
|
33
|
+
vi.mock('../../lib/api.js', () => ({
|
|
34
|
+
cmsApi: (e, o) => cmsApi(e, o),
|
|
35
|
+
ensureCsrfToken: vi.fn(async () => { }),
|
|
36
|
+
}));
|
|
37
|
+
const { EmailSettingsTab } = await import('../../views/settings/EmailSettingsTab.js');
|
|
38
|
+
function deepClone(v) {
|
|
39
|
+
return JSON.parse(JSON.stringify(v));
|
|
40
|
+
}
|
|
41
|
+
beforeEach(() => {
|
|
42
|
+
cmsApi.mockClear();
|
|
43
|
+
getResponse = deepClone(EMPTY_RESPONSE);
|
|
44
|
+
putResponse = null;
|
|
45
|
+
testResponse = { data: { success: true, sentTo: 'admin@example.com' }, status: 200 };
|
|
46
|
+
});
|
|
47
|
+
describe('EmailSettingsTab', () => {
|
|
48
|
+
it('renders the provider picker with the unconfigured state', async () => {
|
|
49
|
+
render(_jsx(EmailSettingsTab, { role: "ADMIN" }));
|
|
50
|
+
expect(await screen.findByRole('radio', { name: 'None' })).toBeTruthy();
|
|
51
|
+
expect(screen.getByRole('radio', { name: 'Resend' })).toBeTruthy();
|
|
52
|
+
expect(screen.getByRole('radio', { name: 'SMTP.com' })).toBeTruthy();
|
|
53
|
+
expect(screen.getByRole('radio', { name: 'Amazon SES' })).toBeTruthy();
|
|
54
|
+
expect(screen.getByRole('radio', { name: 'SMTP server' })).toBeTruthy();
|
|
55
|
+
expect(screen.getByText(/No email provider configured/)).toBeTruthy();
|
|
56
|
+
});
|
|
57
|
+
it('saves an SMTP.com configuration with the entered key (PUT payload contract)', async () => {
|
|
58
|
+
putResponse = {
|
|
59
|
+
settings: {
|
|
60
|
+
provider: 'smtpcom',
|
|
61
|
+
from: 'noreply@example.com',
|
|
62
|
+
channel: 'transactional',
|
|
63
|
+
hasApiKey: true,
|
|
64
|
+
apiKeyLast4: '-key',
|
|
65
|
+
hasSmtpPass: false,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
render(_jsx(EmailSettingsTab, { role: "ADMIN" }));
|
|
69
|
+
fireEvent.click(await screen.findByRole('radio', { name: 'SMTP.com' }));
|
|
70
|
+
fireEvent.change(screen.getByLabelText('Sender address'), {
|
|
71
|
+
target: { value: 'noreply@example.com' },
|
|
72
|
+
});
|
|
73
|
+
fireEvent.change(screen.getByLabelText('API key'), {
|
|
74
|
+
target: { value: 'smtpcom-secret-key' },
|
|
75
|
+
});
|
|
76
|
+
fireEvent.change(screen.getByLabelText('Sender channel'), {
|
|
77
|
+
target: { value: 'transactional' },
|
|
78
|
+
});
|
|
79
|
+
fireEvent.click(screen.getByRole('button', { name: 'Save Changes' }));
|
|
80
|
+
await waitFor(() => {
|
|
81
|
+
const put = cmsApi.mock.calls.find(([e, o]) => e === '/email/settings' && o?.method === 'PUT');
|
|
82
|
+
expect(put).toBeTruthy();
|
|
83
|
+
expect(JSON.parse(put[1].body)).toEqual({
|
|
84
|
+
provider: 'smtpcom',
|
|
85
|
+
from: 'noreply@example.com',
|
|
86
|
+
apiKey: 'smtpcom-secret-key',
|
|
87
|
+
channel: 'transactional',
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
// After save the key input is cleared and the masked hint appears.
|
|
91
|
+
expect(screen.getByLabelText('API key').value).toBe('');
|
|
92
|
+
expect(await screen.findByText(/ends in -key/)).toBeTruthy();
|
|
93
|
+
});
|
|
94
|
+
it('omits the apiKey from the payload when a stored key is kept', async () => {
|
|
95
|
+
getResponse = {
|
|
96
|
+
...deepClone(EMPTY_RESPONSE),
|
|
97
|
+
settings: {
|
|
98
|
+
provider: 'resend',
|
|
99
|
+
from: 'old@example.com',
|
|
100
|
+
hasApiKey: true,
|
|
101
|
+
apiKeyLast4: 'k3y9',
|
|
102
|
+
hasSmtpPass: false,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
putResponse = {
|
|
106
|
+
settings: {
|
|
107
|
+
provider: 'resend',
|
|
108
|
+
from: 'new@example.com',
|
|
109
|
+
hasApiKey: true,
|
|
110
|
+
apiKeyLast4: 'k3y9',
|
|
111
|
+
hasSmtpPass: false,
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
render(_jsx(EmailSettingsTab, { role: "ADMIN" }));
|
|
115
|
+
const from = await screen.findByLabelText('Sender address');
|
|
116
|
+
expect(await screen.findByText(/ends in k3y9/)).toBeTruthy();
|
|
117
|
+
fireEvent.change(from, { target: { value: 'new@example.com' } });
|
|
118
|
+
fireEvent.click(screen.getByRole('button', { name: 'Save Changes' }));
|
|
119
|
+
await waitFor(() => {
|
|
120
|
+
const put = cmsApi.mock.calls.find(([e, o]) => e === '/email/settings' && o?.method === 'PUT');
|
|
121
|
+
expect(put).toBeTruthy();
|
|
122
|
+
const body = JSON.parse(put[1].body);
|
|
123
|
+
expect(body).toEqual({ provider: 'resend', from: 'new@example.com' });
|
|
124
|
+
expect(body).not.toHaveProperty('apiKey');
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
it('sends a test email and shows the result', async () => {
|
|
128
|
+
getResponse = {
|
|
129
|
+
...deepClone(EMPTY_RESPONSE),
|
|
130
|
+
settings: {
|
|
131
|
+
provider: 'resend',
|
|
132
|
+
from: 'a@b.co',
|
|
133
|
+
hasApiKey: true,
|
|
134
|
+
apiKeyLast4: 'k3y9',
|
|
135
|
+
hasSmtpPass: false,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
render(_jsx(EmailSettingsTab, { role: "ADMIN" }));
|
|
139
|
+
fireEvent.click(await screen.findByRole('button', { name: /Send test email/ }));
|
|
140
|
+
await waitFor(() => expect(cmsApi).toHaveBeenCalledWith('/email/settings/test', expect.objectContaining({ method: 'POST' })));
|
|
141
|
+
expect(await screen.findByText(/Test email sent to admin@example.com/)).toBeTruthy();
|
|
142
|
+
});
|
|
143
|
+
it('surfaces a failed test send', async () => {
|
|
144
|
+
getResponse = {
|
|
145
|
+
...deepClone(EMPTY_RESPONSE),
|
|
146
|
+
settings: {
|
|
147
|
+
provider: 'resend',
|
|
148
|
+
from: 'a@b.co',
|
|
149
|
+
hasApiKey: true,
|
|
150
|
+
hasSmtpPass: false,
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
testResponse = { error: 'SMTP.com API error (401): bad key', status: 502 };
|
|
154
|
+
render(_jsx(EmailSettingsTab, { role: "ADMIN" }));
|
|
155
|
+
fireEvent.click(await screen.findByRole('button', { name: /Send test email/ }));
|
|
156
|
+
expect(await screen.findByText(/SMTP\.com API error \(401\)/)).toBeTruthy();
|
|
157
|
+
});
|
|
158
|
+
it('shows the env-managed notice and disables editing for non-admins', async () => {
|
|
159
|
+
getResponse = {
|
|
160
|
+
...deepClone(EMPTY_RESPONSE),
|
|
161
|
+
envProvider: 'resend',
|
|
162
|
+
envSenderActive: true,
|
|
163
|
+
};
|
|
164
|
+
render(_jsx(EmailSettingsTab, { role: "EDITOR" }));
|
|
165
|
+
expect(await screen.findByText(/read-only access/)).toBeTruthy();
|
|
166
|
+
expect(screen.getByText(/Environment variables currently configure/)).toBeTruthy();
|
|
167
|
+
expect(screen.getByRole('radio', { name: 'Resend' }).disabled).toBe(true);
|
|
168
|
+
});
|
|
169
|
+
it('warns when the email plugin is missing for a configured provider', async () => {
|
|
170
|
+
getResponse = {
|
|
171
|
+
...deepClone(EMPTY_RESPONSE),
|
|
172
|
+
factoryAvailable: false,
|
|
173
|
+
settings: {
|
|
174
|
+
provider: 'resend',
|
|
175
|
+
from: 'a@b.co',
|
|
176
|
+
hasApiKey: true,
|
|
177
|
+
hasSmtpPass: false,
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
render(_jsx(EmailSettingsTab, { role: "ADMIN" }));
|
|
181
|
+
expect(await screen.findByText(/requires the email plugin/)).toBeTruthy();
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
//# sourceMappingURL=email-settings.render.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-settings.render.test.js","sourceRoot":"","sources":["../../../src/__tests__/views/email-settings.render.test.tsx"],"names":[],"mappings":"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"}
|