4bnode 4.1.7 → 4.1.8
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "4bnode",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.8",
|
|
4
4
|
"description": "4bnode is a CLI-powered backend development platform with a built-in visual dashboard to generate, manage, and test Node.js/Express APIs faster.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -121,6 +121,7 @@ export function MailPage() {
|
|
|
121
121
|
async function save() {
|
|
122
122
|
let body;
|
|
123
123
|
if (provider === "resend") {
|
|
124
|
+
if (!form.resendFrom.trim()) { toast("From address is required", "error"); return; }
|
|
124
125
|
if (!form.apiKey.trim() && !(info.resend && info.resend.hasKey)) { toast("API key is required", "error"); return; }
|
|
125
126
|
body = { provider: "resend", apiKey: form.apiKey.trim(), from: form.resendFrom.trim(), domainPassword };
|
|
126
127
|
} else {
|
|
@@ -239,7 +240,7 @@ export function MailPage() {
|
|
|
239
240
|
<label className="form-label">Authorization password</label>
|
|
240
241
|
<input className="form-input" type="password" value=${domainPassword} onInput=${(e) => setDomainPassword(e.target.value)} placeholder="Required for 4brains.in" />
|
|
241
242
|
</div>`}
|
|
242
|
-
<button className="btn btn-primary" onClick=${save} disabled=${busy || (provider === "smtp" && !form.host.trim()) || (isOfficial && !domainPassword)}>
|
|
243
|
+
<button className="btn btn-primary" onClick=${save} disabled=${busy || (provider === "smtp" && !form.host.trim()) || (provider === "resend" && !form.resendFrom.trim()) || (isOfficial && !domainPassword)}>
|
|
243
244
|
${busy ? html`<span className="spinner" />` : html`<${LuCheck} size=${16} />`}
|
|
244
245
|
${busy ? "Saving..." : "Save & install"}
|
|
245
246
|
</button>
|
|
@@ -1372,11 +1372,11 @@ async function verifyMailConfig(envPath, provider) {
|
|
|
1372
1372
|
if (provider === 'resend') {
|
|
1373
1373
|
const key = getEnvValue(envPath, 'RESEND_API_KEY');
|
|
1374
1374
|
if (!key) throw new Error('Resend API key is missing.');
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
if (
|
|
1375
|
+
// Resend has no safe no-op verify — domains.list() needs a full-access key and
|
|
1376
|
+
// rejects normal send-only keys, which would fail valid configs. So validate the
|
|
1377
|
+
// key shape + From here, and let the "Send test" button confirm deliverability.
|
|
1378
|
+
if (!/^re_/.test(key)) throw new Error('That does not look like a Resend API key — it should start with "re_".');
|
|
1379
|
+
if (!getEnvValue(envPath, 'MAIL_FROM')) throw new Error('A From address is required for Postmaster.');
|
|
1380
1380
|
return;
|
|
1381
1381
|
}
|
|
1382
1382
|
const host = getEnvValue(envPath, 'SMTP_HOST');
|
|
@@ -1456,6 +1456,7 @@ router.post('/api/mail/setup', async (req, res) => {
|
|
|
1456
1456
|
if (from !== undefined) updateEnvFile(envPath, 'MAIL_FROM', String(from || '').trim());
|
|
1457
1457
|
if (apiKey) updateEnvFile(envPath, 'RESEND_API_KEY', String(apiKey).trim());
|
|
1458
1458
|
if (!getEnvValue(envPath, 'RESEND_API_KEY')) return res.status(400).json({ error: 'A Resend API key is required.' });
|
|
1459
|
+
if (!getEnvValue(envPath, 'MAIL_FROM')) return res.status(400).json({ error: 'A From address is required.' });
|
|
1459
1460
|
await installDeps('resend');
|
|
1460
1461
|
} else {
|
|
1461
1462
|
const { host, port, secure, user, pass, from } = req.body || {};
|