[glm-grade=B] fix(notifications): repair UI/API contract drift — SMTP auth, event gate, test button (DC-092)
Three user-facing notification features were silently dead from contract
drift between the settings UI and the backend:
1. SMTP auth never applied: the UI sent email.user/email.pass while the
manager read username/password. Route now normalizes aliases; legacy
config files canonicalize at load.
2. Event toggles were cosmetic: UI sent camelCase keys (containerDown),
the send() gate read kebab-case ('container-down'). EVENT_ALIASES now
folds every known spelling (manager gate, route store, legacy files);
UI sends and reads canonical keys.
3. Deploy/auto-restart notifications always dropped: deploy-success/
deploy-failed/auto-restart were missing from DEFAULT events, and
send('test') was itself gated -> the Test button was a no-op. Defaults
added; 'test' bypasses the gate.
Also: strict boolean contract (string 'false' for secure/enabled rejected
— previously coerced truthy, silently forcing TLS), SMTP port bounds,
non-destructive credential merge (blank password no longer clobbers the
stored one), GET /config returns port/secure/to/username + hasPassword
(password never returned), full form prefill + keep-hint placeholder.
Verified live pre-fix: send('test')/'deploymentSuccess'/'auto-restart'
all returned 'not enabled'. Post-fix: +20 tests (route + manager),
full suite 2641/2641 (112 suites).
Judge: GLM-5.3 cold read via delegate_task (deleg_ad765d63), grade B,
ship, zero blockers; judge independently ran the DC-092 suites (38/38).
Polish #1 (canonical event for provider titles) folded in. Remaining
emitters with the same gate-miss class (recipeRemoved, workflow,
ssl-cert-expiry, dns-propagation, drift-detected, dependency-restart-*)
noted for a follow-up tick.
This commit is contained in:
@@ -240,9 +240,23 @@
|
||||
document.getElementById('ntfy-server').value = config.providers.ntfy.serverUrl;
|
||||
}
|
||||
|
||||
// email fields
|
||||
// email fields — DC-092: prefill the FULL form so a save doesn't
|
||||
// silently wipe fields the GET response previously omitted. Password
|
||||
// is never returned; when one is stored the field shows a keep-hint
|
||||
// and an empty submit preserves the stored credential server-side.
|
||||
if (config.providers?.email?.host) document.getElementById('email-host').value = config.providers.email.host;
|
||||
if (config.providers?.email?.from) document.getElementById('email-from').value = config.providers.email.from;
|
||||
if (config.providers?.email?.to) document.getElementById('email-to').value = config.providers.email.to;
|
||||
if (config.providers?.email?.port) document.getElementById('email-port').value = config.providers.email.port;
|
||||
if (config.providers?.email?.secure !== undefined) document.getElementById('email-secure').checked = config.providers.email.secure === true;
|
||||
if (config.providers?.email?.username) document.getElementById('email-user').value = config.providers.email.username;
|
||||
const emailPassEl = document.getElementById('email-pass');
|
||||
if (config.providers?.email?.hasPassword) {
|
||||
emailPassEl.value = '';
|
||||
emailPassEl.placeholder = 'saved — leave blank to keep';
|
||||
} else {
|
||||
emailPassEl.placeholder = 'app password';
|
||||
}
|
||||
|
||||
// Health check
|
||||
document.getElementById('health-check-enabled').checked = config.healthCheck?.enabled || false;
|
||||
@@ -254,12 +268,14 @@
|
||||
`Last check: ${new Date(config.healthCheck.lastCheck).toLocaleString()}`;
|
||||
}
|
||||
|
||||
// Events
|
||||
document.getElementById('event-container-down').checked = config.events?.containerDown !== false;
|
||||
document.getElementById('event-container-up').checked = config.events?.containerUp !== false;
|
||||
document.getElementById('event-deploy-success').checked = config.events?.deploymentSuccess !== false;
|
||||
document.getElementById('event-deploy-failed').checked = config.events?.deploymentFailed !== false;
|
||||
document.getElementById('event-resource-alert').checked = config.events?.resourceAlert !== false;
|
||||
// Events — canonical kebab-case keys, matching the backend store
|
||||
// (DC-092: previously read camelCase keys that never existed, so
|
||||
// every toggle re-rendered as 'checked' regardless of stored state).
|
||||
document.getElementById('event-container-down').checked = config.events?.['container-down'] !== false;
|
||||
document.getElementById('event-container-up').checked = config.events?.['container-up'] === true;
|
||||
document.getElementById('event-deploy-success').checked = config.events?.['deploy-success'] !== false;
|
||||
document.getElementById('event-deploy-failed').checked = config.events?.['deploy-failed'] !== false;
|
||||
document.getElementById('event-resource-alert').checked = config.events?.['alert'] !== false;
|
||||
}
|
||||
} catch (error) {
|
||||
errorHandler.logError('[Notifications] Load Config', error, { function: 'loadConfig' });
|
||||
@@ -325,18 +341,18 @@
|
||||
host: document.getElementById('email-host').value.trim(),
|
||||
port: parseInt(document.getElementById('email-port').value) || 587,
|
||||
secure: document.getElementById('email-secure').checked,
|
||||
user: document.getElementById('email-user').value.trim(),
|
||||
pass: document.getElementById('email-pass').value.trim(),
|
||||
username: document.getElementById('email-user').value.trim(),
|
||||
password: document.getElementById('email-pass').value.trim(),
|
||||
from: document.getElementById('email-from').value.trim(),
|
||||
to: document.getElementById('email-to').value.trim()
|
||||
}
|
||||
},
|
||||
events: {
|
||||
containerDown: document.getElementById('event-container-down').checked,
|
||||
containerUp: document.getElementById('event-container-up').checked,
|
||||
deploymentSuccess: document.getElementById('event-deploy-success').checked,
|
||||
deploymentFailed: document.getElementById('event-deploy-failed').checked,
|
||||
resourceAlert: document.getElementById('event-resource-alert').checked
|
||||
'container-down': document.getElementById('event-container-down').checked,
|
||||
'container-up': document.getElementById('event-container-up').checked,
|
||||
'deploy-success': document.getElementById('event-deploy-success').checked,
|
||||
'deploy-failed': document.getElementById('event-deploy-failed').checked,
|
||||
'alert': document.getElementById('event-resource-alert').checked
|
||||
},
|
||||
healthCheck: {
|
||||
enabled: document.getElementById('health-check-enabled').checked,
|
||||
|
||||
Reference in New Issue
Block a user