emergency: 911 dialplan, notifications, test button, settings, warnings

Dialplan: sp-emergency-dial (911, 9911) first in sp-from-ext;
sp-emergency has no class, channel-limit or DND check, dials every
enabled trunk in name order with the E911 caller ID, and raises
UserEvent SpEmergency. Static sp-emergency-alert (_X! so "0" matches)
for the ring-out.

api: SpEmergency -> emergency_events row, live emergency.call (admins
and notify users only), email via nodemailer (SMTP from settings) and
an Originate to every notify extension, each outcome recorded. Test
button runs the same with TEST in front, records and dials nothing.
GET/PATCH /api/settings (smtp password write-only, references
checked, read-only system keys), SMTP test, /api/system/warnings.
Fix: clearing a nullable setting stored SQL NULL (now JSON null).

UI: Settings page (Email, Emergency with the 06 §6 notice), user
Emergency tab, dashboard warnings banner, red 911 banner.

Verified: generator/emergency.test.ts; trunk-test.ts dials 911 from a
caller with no class to the fake provider (E911 caller ID in RPID,
SpEmergency on AMI, alert plays on baresip 101); scripts/e2e/m3-t4.mjs
(SMTP sink receives the test email, 101 rings, banner shows).
nodemailer pinned to 10.0.11 (newest release over two weeks old).

Task: M3-T4
This commit is contained in:
iamdoubz committed 2026-10-17 20:52:50 -05:00
1 parent 4496f6b89a
commit 409724bc55
45 files changed
+1781 -139

No files matched your search

+3 -1
View File
@@ -344,8 +344,10 @@ Reports, history, system:
| GET | `/api/reports/summary?from&to` | totals per day, per direction, missed count, average talk time |
| GET | `/api/reports/queues?from&to` | per queue: offered, answered, abandoned, avg wait, % within SLA |
| GET | `/api/audit-log` | |
| GET/PATCH | `/api/settings` | whole settings object; PATCH merges known keys only |
| GET/PATCH | `/api/settings` | whole settings object; PATCH merges known keys only. `smtp` comes back as `{host, port, secure, username, from, hasPassword}` and is written with an optional `password` (empty keeps the stored one). `system.*`, `sms.webhookToken` and `setup.completed` are read-only. References (DIDs, extensions, destinations) are checked, and a transport a trunk uses can't be turned off |
| POST | `/api/settings/smtp/test` | `{to}` sends a test email |
| POST | `/api/settings/emergency/test` | runs 04 §5.3 steps 2–4 with `TEST` in front (email, ring-out, banner); records nothing and dials nothing (06 §6.5). Returns `{notified}` |
| GET | `/api/system/warnings` | `{items: [{code, message, link}]}` for the dashboard banner: `restart_required`, `e911_cid_missing` (a trunk exists, no default E911 caller ID), `e911_address_unconfirmed` (that DID has no `e911_address`), `smtp_missing`. "Trunk down" comes from live state in the browser |
| POST | `/api/settings/network/detect-public-ip` | |
| GET | `/api/system/status` | `{ db: ok, ami: connected, asteriskVersion, uptime, trunks: [...], disk: {free, total}, restartRequired, lastGeneratorRun }` |
| GET | `/api/system/generated-files` | names + contents of generated files, **passwords masked** (debug view) |