From 51d090ee2c78cb6b6d5e1bad1e80ae0e768ac682 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 25 Jul 2026 02:05:35 +0000 Subject: [PATCH] Modernize the Extensions tab: batched saves, collapsed sections, inline edit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tab consolidation fixed what the page showed but not how it read. With two extensions and nothing else configured it rendered ~1900px tall: six cards all expanded, three paragraphs of prose before the first control, eight Save buttons with no indication of which rows had been touched, browser prompt() dialogs for renames, and a nine-column table with no scroll container. Interaction: - Name and Category are edited in place and feed one batched save. Edited rows get a highlight and a left rail; a sticky bar reports the count and Save changes commits only those rows, routing each field to the endpoint it needs (rename/category through the container, tier/numbers/messaging through the permissions file — or the messaging-only endpoint with no trunk). Discard reverts; leaving with edits pending warns. Delete keeps its own per-row control since it is destructive. - Categories, Rooms, Groups, Caps and Personal numbers became
sections with item counts, so the tab opens on the extensions table. Explanations moved behind "what this means" disclosures. - Add-extension is behind a button; the one-time device password gets a persistent dismissible callout rather than a line that scrolls away. Everything else reports through toasts, replacing six inline message divs. Presentation: a token-based stylesheet (spacing/radius/colour scale), sticky translucent header, sticky table headers, status as a colour-coded pill, focus-visible outlines, primary/secondary/danger button hierarchy, and a .table-wrap that owns horizontal overflow. Security Log and CrowdSec cards picked up the same card-body padding and scroll wrappers. Verified in Chromium against a fixture Asterisk config at 1280px and 390px: full layout opens at ~700px tall with five collapsed sections, editing two rows marks both and shows "2 extensions edited", saving persists and clears the dirty state, per-extension failures are reported individually rather than swallowed, the bare no-container/no-trunk layout still collapses to Ext/Name/Messaging with two cards and saves via the messaging-only endpoint, and at 390px the page does not scroll horizontally while the table does. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01NAddJGE1G6eGaPzmScG5Vh --- services/security-dashboard.sh | 961 +++++++++++++++++++++++---------- 1 file changed, 668 insertions(+), 293 deletions(-) diff --git a/services/security-dashboard.sh b/services/security-dashboard.sh index a524027..ccd18b9 100644 --- a/services/security-dashboard.sh +++ b/services/security-dashboard.sh @@ -228,9 +228,24 @@ which tab a given extension's settings live on. device list. Columns: Ext, Name, then Category/Status/Transport if that container is present, then Tier/Approved-numbers if a PSTN trunk dialplan is installed, then Messaging (always — internal SIP texting has no PSTN - dependency at all: no cost, no carrier, no DID) and a per-row Save. Save - writes tier + approved numbers + messaging together when there's a trunk, - and messaging alone when there isn't. + dependency at all: no cost, no carrier, no DID). + + Name and Category are edited **in place**; every cell feeds one batched + save. Rows you've touched get a highlight and a left rail, a sticky bar + reports how many are edited, and **Save changes** commits just those rows — + routing each change to the endpoint it needs (rename/category through the + container; tier + approved numbers + messaging through the permissions + file, or the messaging-only endpoint when there's no trunk). **Discard** + reverts to what's on disk, and closing the page with edits pending warns + first. Delete keeps its own control per row, since it's destructive and + must not ride along with a batch. + + Everything below the table — Categories, Rooms, Groups, Concurrent-call + caps, Personal numbers — is a collapsed section with an item count in its + header, so the tab opens on the extensions table rather than on six + expanded cards. Long explanations sit behind "what this means" + disclosures for the same reason. The table scrolls horizontally inside + its own card, so the page never scrolls sideways on a phone. - **Extensions** — add/rename/delete a SIP extension, reassign its category; live registered/unregistered status per device. This is a native reimplementation of Easy Asterisk's own vendored web admin @@ -2242,31 +2257,233 @@ INDEX_HTML = """ Security Dashboard @@ -2290,179 +2513,234 @@ INDEX_HTML = """
-

Recent Asterisk SIP security events, newest first. Errors/warnings are real auth failures; informational lines are normal registration traffic.

- - - - - - -
TimeEventAccountRemoteSeverity
+
+

Recent Asterisk SIP security events, newest first. Errors/warnings are real auth failures; informational lines are normal registration traffic.

+
+ + + + + + +
TimeEventAccountRemoteSeverity
+
+
+ +
+ Rooms (ring groups) +
+

A shared extension that rings (or pages) every member device at once.

+
+ + + + + +
+
+ + + + + + + +
ExtNameMembersTimeoutType
+
-
- - - - -
GroupMembers
-
- -
-

Concurrent-call caps

-

A call over either cap gets a busy signal (and an ntfy alert, if enabled) — existing calls are never affected. Changes are usually live on the next call; if a call doesn't reflect a recent change, use "Commit Changes" at the top.

-
- - - +
+ +
+ Groups +
+
+ What groups do +

Named sets of extensions for bulk actions — e.g. enable messaging for everyone in "Sales" at once. A management convenience only: applying an action writes the same per-extension setting each member's own Messaging checkbox above would, one time. It isn't a runtime concept the dialplan knows about, and membership changes never retroactively affect anything already applied. A group can also own a personal number below, which is evaluated live against current membership on every call.

+
+
+ + +
+
+
+ + + + +
GroupMembers
+
-
- -
-

Personal numbers

-

- Multiple DIDs can share this one trunk. Assigning a DID to an extension routes inbound calls to that DID straight to its owner (still gated by the owner's own tier/approved-numbers above — no ring-group fallback), and makes that extension's outbound calls show this DID as Caller-ID instead of the shared trunk DID. You can also assign a DID to a group instead of a single extension — every current member whose own tier/approved-numbers authorize the caller rings, checked fresh against the group's current membership on every call; a group has no single extension to hang the outbound Caller-ID override on, so that part only applies to single-extension assignments. The shared DID/ring-group keeps working regardless. Reassigning a DID's owner has been confirmed to sometimes need "Commit Changes" (at the top) before Asterisk actually uses the new owner. -

-
- - - +
+ +
+ Concurrent-call caps +
+

A call over either cap gets a busy signal (and an ntfy alert, if enabled) — existing calls are never affected. Usually live on the next call; if a call doesn't reflect a recent change, use "Commit changes" at the top.

+
+ + + +
- - - - -
DIDOwner
-
- +
+ +
+ Personal numbers +
+
+ How personal numbers route +

Multiple DIDs can share this one trunk. Assigning a DID to an extension routes inbound calls to that DID straight to its owner (still gated by the owner's own tier/approved-numbers above — no ring-group fallback), and makes that extension's outbound calls show this DID as Caller-ID instead of the shared trunk DID.

+

You can also assign a DID to a group instead of a single extension — every current member whose own tier/approved-numbers authorize the caller rings, checked fresh against the group's current membership on every call. A group has no single extension to hang the outbound Caller-ID override on, so that part only applies to single-extension assignments. The shared DID/ring-group keeps working regardless.

+

Reassigning a DID's owner has been confirmed to sometimes need "Commit changes" (at the top) before Asterisk actually uses the new owner.

+
+
+ + + +
+
+ + + + +
DIDOwner
+
+
+
+