mirror of
https://github.com/UNITRONIX/BetterDesk.git
synced 2026-09-10 01:27:11 +00:00
fix(settings): use Utils.api for SMTP settings to fix HTTP 415 (Fixes #240)
Settings Email save/test/load now send Content-Type application/json required by panel API middleware. Adds regression tests for POST without Content-Type.
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
## [3.3.109] — 2026-07-05
|
||||
|
||||
### Fixed
|
||||
- **Email/SMTP settings (Fixes #240):** Settings → Email “Test connection” no longer returns HTTP 415 — SMTP save/test/load now use `Utils.api()` so requests include `Content-Type: application/json` required by panel API middleware.
|
||||
- **Panel tabs redirect to dashboard (401):** RustDesk client API routes (`GET /api/devices`, `/api/strategies`) no longer shadow panel session routes — browser requests without Bearer token fall through to panel handlers; `users.js` uses `/api/panel/strategies`; `Utils.api` no longer redirects logged-in users to `/login` (which bounced to dashboard) on incidental 401.
|
||||
- **Devices/Users 429 rate limit:** extended panel poll whitelist (`/api/folders`, `/api/tags`, `/api/device-groups`, `/api/bd/notifications`, `/api/panel/*`); dedicated limiter for `POST /api/desktop/layout`; staggered Devices page API loads; desktop widget layout saves gated when desktop mode is inactive.
|
||||
|
||||
|
||||
@@ -4935,13 +4935,7 @@
|
||||
if (!confirmed) return;
|
||||
}
|
||||
try {
|
||||
const res = await fetch('/api/settings/email/smtp', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': window.BetterDesk?.csrfToken || '' },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.error || 'Save failed');
|
||||
await Utils.api('/api/settings/email/smtp', { method: 'PUT', body });
|
||||
Notifications.success(_('settings.email.smtp_saved'));
|
||||
document.getElementById('email-smtp-pass').value = '';
|
||||
await loadEmailSmtpConfig();
|
||||
@@ -4952,11 +4946,7 @@
|
||||
|
||||
testBtn.addEventListener('click', async () => {
|
||||
try {
|
||||
const res = await fetch('/api/settings/email/smtp/test', {
|
||||
method: 'POST',
|
||||
headers: { 'X-CSRF-Token': window.BetterDesk?.csrfToken || '' },
|
||||
});
|
||||
const data = await res.json();
|
||||
const data = await Utils.api('/api/settings/email/smtp/test', { method: 'POST', body: {} });
|
||||
if (data.success) {
|
||||
Notifications.success(_('settings.email.smtp_test_success'));
|
||||
} else {
|
||||
@@ -4970,8 +4960,7 @@
|
||||
|
||||
async function loadEmailSmtpConfig() {
|
||||
try {
|
||||
const res = await fetch('/api/settings/email/smtp');
|
||||
const config = await res.json();
|
||||
const config = await Utils.api('/api/settings/email/smtp');
|
||||
if (!config.configured) {
|
||||
_smtpWasConfigured = false;
|
||||
return;
|
||||
|
||||
@@ -91,3 +91,52 @@ describe('SMTP settings handlers', () => {
|
||||
expect(res.body.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
/** Same logic as routes/index.js requireJsonContentType */
|
||||
function requireJsonContentType(req, res, next) {
|
||||
if (['GET', 'DELETE', 'OPTIONS', 'HEAD'].includes(req.method)) {
|
||||
return next();
|
||||
}
|
||||
if (!req.path.startsWith('/api/')) {
|
||||
return next();
|
||||
}
|
||||
if (req.path.includes('/upload') || req.path.includes('/import')) {
|
||||
return next();
|
||||
}
|
||||
if (!req.is('application/json')) {
|
||||
return res.status(415).json({
|
||||
success: false,
|
||||
error: 'Content-Type must be application/json',
|
||||
});
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
describe('SMTP test route JSON Content-Type enforcement', () => {
|
||||
let app;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
app = createTestApp();
|
||||
withAuth(app, { id: 1, username: 'admin', role: 'server_admin' });
|
||||
app.use(requireJsonContentType);
|
||||
app.post('/api/settings/email/smtp/test', testSmtpSettings);
|
||||
});
|
||||
|
||||
it('returns 415 when POST has no Content-Type header', async () => {
|
||||
const res = await request(app).post('/api/settings/email/smtp/test');
|
||||
expect(res.status).toBe(415);
|
||||
expect(res.body.error).toBe('Content-Type must be application/json');
|
||||
expect(emailService.testConnection).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('allows POST with application/json Content-Type', async () => {
|
||||
const res = await request(app)
|
||||
.post('/api/settings/email/smtp/test')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send({});
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body.success).toBe(true);
|
||||
expect(emailService.testConnection).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user