1065 Commits

Author SHA1 Message Date
librelad
ed4bf41cba ux(updater): match the Backups page layout (sidebar, padding, click-to-open)
Restructure the updater page to mirror Backups exactly:
- updater-content.html: shared .sidebar/.category (with .category-icon SVGs)
  outside the page card, .config-section + .page-header + a padded
  .updater-page-body — fixes the missing content padding.
- updater.css: layout copied from .backup-* (flex, padding-bottom:48px,
  body padding:22px); dropped the custom sidebar/header styles (now using the
  shared chrome); kept the updater-specific content widgets.
- updater-page.js: delegate clicks on .updater-layout (sidebar is now a
  sibling of the card) so clicking a sidebar entry opens that tab; fill the
  shared page-header icon slot.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 07:34:32 +01:00
librelad
e7b299b9cc Merge claude/1 2026-05-30 07:13:52 +01:00
librelad
d39852aa3d refactor(webui): reorganize into components/ + core/ taxonomy
Final modularization layout (user-chosen): every page is a self-contained
folder under components/<id>/ (controllers + CSS + its html fragment), and all
shared/framework code folds into core/:
  core/kernel  (feature-registry, lifecycle, services, spa)
  core/boot    (auth, system-loader/orchestrator, setup, loaders)
  core/lib     (data-loader, router, helpers, the task kernel, shared modules)
  core/ui      (topbar, modal, notifications, … + topbar.html)
  core/css     (all shared stylesheets)
  core/icons
Top level is now just: components/, core/, themes/, index.html (+ runtime data/).

Every path reference rewritten (index.html, scripts arrays, fetch()/
loadFragment()/loadScript() literals, system-loader + config-manager controller
paths, kernel manifest URL, feature.json, backend FEATURES_DIR). The
/api/features/list endpoint NAME is unchanged (it now scans components/).
Deleted 3 dead files (app-content.html, apps-content.html, html-cache.js).
Verified: 0 stale prefixes, 0 double-rewrites, all JS/JSON valid.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 07:13:52 +01:00
librelad
8d193eda28 Merge claude/1 2026-05-30 03:13:26 +01:00
librelad
daa336449a feat(updater): backend — data generator + 'libreportal updater' CLI with DR
- scripts/webui/data/generators/updater/webui_updater_scan.sh (webuiUpdaterScan):
  writes frontend/data/updater/generated/{updates,cves,history}.json from the
  installed-apps DB (current image per app from compose). Available-version +
  CVE-scanner are clearly-marked pluggable hooks; always emits valid JSON.
- scripts/cli/commands/updater/{cli_updater_commands.sh,cli_updater_header.sh}:
  auto-dispatched as 'libreportal updater <sub>' (check/apply/apply-all/rollback).
  apply does disaster-recovery FIRST — snapshots the app via the backup engine,
  then pulls + recreates (real dockerComposeUp/compose-pull helpers), records
  history, and auto-rolls-back on failure. Standard LIBREPORTAL_TASK_EXEC
  enqueue/exec split so WebUI + CLI share locking + audit trail.

New .sh files: the array/function-manifest regen self-heals on deploy; the
check path also sources its generator on demand to cover the gap.

NOTE: host-side bash — written to the repo's conventions but not runnable in
this env; this is the surface to test (the WebUI feature is lp-shot-verified).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 03:13:26 +01:00
librelad
39a1b51f7f Merge claude/1 2026-05-30 02:55:31 +01:00
librelad
5e43f8ec79 feat(webui): wire updater actions through the task router
Add updater_check/apply/apply_all/rollback cases to task-router routeAction
and the matching task-actions methods, each running the locked-down
'libreportal updater …' CLI as a task (apply/rollback snapshot-before-update
host-side for disaster recovery). Verified: /updater + all tabs render, the
new Updates nav button shows globally, and the feature auto-discovers via
/api/features/list.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 02:55:31 +01:00
librelad
56f0111d5f Merge claude/1 2026-05-30 02:45:40 +01:00
librelad
e1794069cb feat(webui): add App Updater feature (versions, CVEs, disaster recovery)
New self-contained feature in features/updater/ (mirrors the backup feature):
- index.js + feature.json (auto-discovered; routes /updater + sub-tabs).
- updater-page.js: 5 tabs — Overview (update/CVE/recovery counts), Updates
  (per-app current->available + Update/Update-all), Security (CVEs by severity,
  links to NVD), Recovery (per-app rollback points; snapshot-before-update),
  History. Reads /data/updater/generated/{updates,cves,history}.json; falls
  back to the installed-apps list so it's useful before the first scan. All
  actions route through services.tasks (updater_check/apply/apply_all/rollback)
  — no new mutating API.
- updater.css (self-contained, teal --page-updater hue) + updater-content.html.
- New topbar 'Updates' nav button (nav-updater) + active-highlighting in
  topbar.js + spa.js. Kernel: setupRoutesFromManifest now allows module-only
  features (no legacy handler) — this is the first such feature.

Backend generator + 'libreportal updater' task land next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 02:45:40 +01:00
librelad
55eecd6dfe Merge claude/1 2026-05-30 02:10:09 +01:00
librelad
eaafd1bb38 refactor(webui): relocate admin area into features/admin/ + shared extractions
- features/admin/: the 10 admin-owned config controllers, the 5 admin pages
  (overview/system/charts/metric/storage), ssh-page.js, peers-page.js, plus
  admin.css/ip-whitelist.css/ssh.css (eager). config-manager.js kept last in
  the load order (it news the sub-managers).
- shared/js/: config-shared.js + config-options.js (ConfigShared/ConfigOptions
  globals consumed cross-feature by backup/apps/tasks).
- shared/css/: forms.css + config.css (generic form + config-form primitives
  borrowed by apps/backup/admin).
- Updated all path strings in system-loader.js (config component) and
  config-manager.js (lazyLoad of admin/ssh/peers controllers); index.html CSS
  hrefs. No /js/components/{config,admin,ssh,peers}/ refs remain.

js/components/ now holds only shared UI (topbar, notifications, eo-modal,
update-notifier, mobile-menu, confirmation-dialog).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 02:10:09 +01:00
librelad
a82d4b75dd Merge claude/1 2026-05-30 02:06:31 +01:00
librelad
ee44a4eb80 refactor(webui): relocate backup into features/backup/, app-card to shared/
- features/backup/: backup-schema.js, backup-page.js, backup.css (eager).
- shared/js/backup-app-card.js: cross-feature (used by backup AND the
  app-detail Backups tab), so it goes to shared/, not buried in backup.
- Updated paths: feature scripts array, spa.js handleBackup fallback,
  system-loader apps-manager component (app-card), index.html backup.css href.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 02:06:31 +01:00
librelad
402c1af861 Merge claude/1 2026-05-30 02:03:57 +01:00
librelad
f15cfe3043 refactor(webui): relocate apps + app-detail controllers and CSS into features/apps/
Move the 6 app controllers (apps-manager, app-tabbed-manager, port-manager,
routing-manager, services-manager, tools-manager) and their 7 stylesheets
(apps, apps-layout, tools, services, service-buttons, routing, port-manager)
into features/apps/. JS paths only existed in system-loader.js (the
apps-manager + app-tabbed-manager components); CSS hrefs in index.html (kept
eager). The cross-feature task-manager (/shared/task/) + backup-app-card
entries in the apps-manager component are untouched here. Globals unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 02:03:56 +01:00
librelad
7924fcc42d Merge claude/1 2026-05-30 02:00:59 +01:00
librelad
b4649cd713 refactor(webui): relocate tasks page + shared task kernel
- features/tasks/: tasks-manager.js (the /tasks page controller) + tasks.css.
- shared/task/: the 6 cross-cutting task-kernel files (event-bus, commands,
  actions, router, global-functions, manager) + task-refresh-coordinator.js —
  used by tasks AND apps/app-detail/backup, so they go to shared/, not a
  feature. task-parameter-preserve.js stays at js/ (shared root).
- Updated all path strings: system-loader.js task-system + apps-manager
  components, apps-manager loadTaskSystem(), index.html (refresh-coordinator +
  tasks.css). Globals (taskEventBus/tasksManager/TaskManager/...) unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 02:00:59 +01:00
librelad
61b48fa7ae Merge claude/1 2026-05-30 01:56:12 +01:00
librelad
3abc45985a refactor(webui): relocate dashboard into features/dashboard/
Move dashboard.js + dashboard.css into the feature folder (kept eager-linked
from the new paths in index.html — dashboard.js stays eager because
system-loader calls its bare globals at boot). No reference sites outside
index.html (the feature's index.js uses globals, not the controller path).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 01:56:12 +01:00
librelad
c6ab276c1e Merge claude/1 2026-05-30 01:52:43 +01:00
librelad
a98a241d5e chore(webui): remove dead controllers (app-manager.js, config-router.js)
Both are orphans with zero inbound references (verified by grep across the
whole frontend): app-manager.js only self-assigns window.appManager and is
in no loader/scripts array; config-router.js only self-defines ConfigRouter
and is referenced nowhere (config-manager owns rendering directly). First,
safest step of the feature reorganization.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 01:52:43 +01:00
librelad
66a48ea8b8 Merge claude/1 2026-05-30 01:14:46 +01:00
librelad
eeb1baf563 refactor(webui): begin backup god-file decomposition + sequential feature scripts
- kernel/lifecycle.js: ctx.loadScripts now loads in array order (sequential),
  so a feature can list a base file before files that augment it. Strictly
  safer than the previous parallel load.
- Extract the module-level schema/retention data (the BACKUP_* maps + the
  retention-preset detector, 83 lines) out of backup-page.js into a new
  backup-schema.js, loaded first. Verbatim move — no logic change. First slice
  of the backup decomposition (god-file: 2553 -> 2470 lines).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 01:14:46 +01:00
librelad
86fc41fe77 Merge claude/1 2026-05-30 01:04:59 +01:00
librelad
301174e750 docs: record implemented state of the feature-module architecture
Add an Implementation status section: kernel + manifest routing, all pages
migrated to feature modules, folder auto-discovery via /api/features/list
(supersedes the §3 shell-regen generator), DI seam, and the shared token
layer are shipped + verified. God-file decomposition and the base.css/CSS
split remain as the large internal refactors still to do.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 01:04:59 +01:00
librelad
3f1cb67d02 Merge claude/1 2026-05-30 00:46:57 +01:00
librelad
98d950ba44 feat(webui): phase 2 — DI service container (ctx.services)
Introduces kernel/services.js (window.LP.services): an additive, typed,
LAZY view onto the existing cross-cutting singletons — tasks{bus,refresh,
route}, live, auth, data, notify, theme, modal, router. It constructs
nothing (pure getters onto the live globals), so there's no double-init and
the globals stay authoritative. MountContext now injects it as ctx.services.

Slot names/globals were verified against the real code (workflow map): the
design doc's §4 list was wrong in several places — no window.taskManager
(client slot dropped), tasks.route lives on tasksManager.router, auth has no
status(), DataLoader isn't a window prop (lexical fallback), modal/router are
split surfaces (grouped/bound objects).

Migrated the 4 cross-cutting refs in the feature modules onto ctx.services
(admin: router.adminCategoryFromPath + tasks.refresh; backup: tasks.refresh;
app-detail: router.appPath). Page-owned controllers stay feature-globals.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 00:46:57 +01:00
librelad
cd34a7671a Merge claude/1 2026-05-30 00:18:20 +01:00
librelad
31b73f9670 feat(webui): auto-discover features from folders, mirroring the theme system
Themes are already modular via folder discovery (GET /api/themes/list scans
themes/<name>/). This brings the SAME model to pages:
- backend/routes/features.js: public GET /api/features/list scans
  frontend/features/<id>/feature.json and returns the page manifest. The
  Node process reads its own bind-mounted /app/frontend — no runFileOp /
  regen / source-array plumbing needed (sidesteps the shell-generator gotchas).
- features/<id>/feature.json: each page now self-describes (id, routes,
  module, handler, navId, nav, order). 6 real features + 3 redirect-only
  (config/peers/ssh) so behaviour is preserved exactly.
- kernel loadManifest() prefers /api/features/list, falls back to the static
  features/manifest.dev.json when the endpoint isn't up yet.

Result: dropping a features/<id>/ folder registers a page; deleting it
removes it — zero central edits, exactly like dropping a theme folder.
(Backend route needs a Node restart to activate; the static-manifest
fallback keeps everything working until then.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 00:18:20 +01:00
librelad
3dd2444bc2 Merge claude/1 2026-05-29 23:52:51 +01:00
librelad
0724ed785a feat(webui): load feature modules from the manifest (drop index.html script list)
The kernel now loads each feature's self-registering index.js from the
manifest (new 'module' field) before building routes, so index.html no
longer hardcodes a per-feature <script> list — one of the four registries
the modularization targets is now gone. Adding a page = drop a
features/<id>/ folder + a manifest entry; no index.html edit.

loadScript is idempotent and non-fatal: a module that 404s or fails to
register leaves its route on the legacy handler. Manifest-load failure
still falls back to the built-in route table.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 23:52:51 +01:00
librelad
c2dab953af Merge claude/1 2026-05-29 23:46:04 +01:00
librelad
d7ac865b98 fix(webui): guard app-detail listener binds against per-navigation leak
AppTabbedManager.initialize() re-runs on every /app navigation (its
'initialized' flag is never set true), and setupURLMonitoring() /
setupTaskEventListeners() add window listeners (popstate, taskCreated/
Completed/Updated) unguarded — so each app-detail visit stacked another
listener set. Bind them once via a _listenersWired flag (mirrors the
existing _watchdogStarted guard). Pre-existing leak surfaced by the
feature-migration review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 23:46:04 +01:00
librelad
ac9f2bf767 Merge claude/1 2026-05-29 23:37:51 +01:00
librelad
b4105d8cff feat(webui): migrate the Admin area to one feature module
features/admin/index.js owns all /admin* sub-routes (overview, config/<cat>,
system[/sub], tools/ssh-access, tools/peers). mount() parses the category and
delegates to the system-loader configManager singleton's renderConfig();
unmount() stops AdminSystem's live SSE sub + 30s interval, drops the
admin-overview task-refresh registration, and nulls the per-visit
sub-controllers (adminOverview/adminSystem/sshPage/peersPage) while leaving
configManager intact. Legacy /config /ssh /peers redirect handlers unchanged.

With this, every WebUI page now routes through the feature-module kernel;
the legacy handleX() methods remain only as fallbacks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 23:37:51 +01:00
librelad
ff79249fdd Merge claude/1 2026-05-29 23:35:14 +01:00
librelad
247310f370 feat(webui): migrate App Center + app-detail to feature modules
- features/apps/index.js (/apps*) and features/app-detail/index.js (/app*)
  as two features; /apps* registered first so wildcard precedence holds.
  Both drive the system-loader-pre-initialized singletons (appsManager /
  appTabbedManager) via .initialize(), mirroring the legacy handlers exactly
  (incl. app-detail's legacy ?app=/?=name parsing + ?tab=/?config= rewrite).
- kernel/lifecycle.js: ctx.nav(path, addToHistory) so app-detail's empty-name
  redirect matches navigate('/apps', false) exactly.

unmount is a no-op for both (shared singletons); the app-tabbed-manager
listener-rebind leak is pre-existing and handled in a follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 23:35:14 +01:00
librelad
935faa4c58 Merge claude/1 2026-05-29 23:29:31 +01:00
librelad
e6e796311a feat(webui): migrate Dashboard + Tasks to feature modules
Two more pages on the feature-module contract (specs produced + adversarially
verified by workflow):
- features/dashboard/index.js: landing page; mount() folds in the data-reload
  that used to be a navigate() special-case (now deleted from spa.js, so it
  fires exactly once). No controller class — uses the eager dashboard.js globals.
- features/tasks/index.js: re-inits the system-loader tasksManager singleton;
  unmount() clears the 30s auto-refresh interval + open log streams WITHOUT
  stopping the shared SSE bus or nulling the singleton.

Verifier fixes applied: deleted the duplicate dashboard reload in navigate();
dropped a dead detachDashboardLive() call; fixed an invalid try{}while(false)
in the tasks unmount.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 23:29:31 +01:00
librelad
4c368e43de Merge claude/1 2026-05-29 23:02:24 +01:00
librelad
182be8c33d feat(webui): phase 3 (first feature) — migrate Backup to a feature module
Introduces the kernel lifecycle and migrates the first real page to the
feature-module contract:
- kernel/lifecycle.js: MountContext (loadScripts/loadFragment/setContent
  + an AbortController/unsub teardown ledger so mounts can't leak
  listeners or live streams).
- features/backup/index.js: Backup Center as a self-contained module
  (LP.features.register with mount/unmount); heavy backup-page.js stays
  lazy-loaded on first mount.
- spa.js: routes whose feature has a registered mount() are driven
  through the kernel; everything else still uses its legacy handleX().
  navigate() unmounts the current feature first. Both fall back to the
  legacy handler if a module is missing or mount throws.

Strangler step: /backup now flows manifest -> registry -> mount/unmount.
The other pages are untouched. handleBackup remains as the fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 23:02:24 +01:00
librelad
cab04108d3 Merge claude/1 2026-05-29 22:49:39 +01:00
librelad
57c17647e3 feat(webui): phase 1a — shared base token layer (tokens.css)
First slice of the per-module CSS strategy: introduce
shared/css/tokens.css as the always-present, theme-agnostic base token
layer, loaded before all other stylesheets.

- Defines --font-mono (ui-monospace stack). Several feature sheets used
  var(--font-mono, monospace) with no definition, falling back to bare
  monospace; this unifies them with the richer stack used elsewhere.
- Hoists the --page-* identity hues out of css/admin.css so they belong
  to the base layer rather than the admin stylesheet. Values unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 22:49:39 +01:00
librelad
6ae85d8dd2 Merge claude/1 2026-05-29 22:32:42 +01:00
librelad
7e051be196 feat(webui): phase 0b — route from the feature manifest
LibrePortalSPAClean now builds its route table from window.LP.features
(features/manifest.dev.json) instead of the hardcoded setupRoutes() Map.
Manifest order is preserved so findRouteHandler()'s wildcard precedence
(/apps* before /app*) is unchanged. All-or-nothing fallback to the
built-in table if the manifest is missing/empty or names an unknown
handler, so routing is never left half-wired.

Rendering is unchanged — handlers still do the work; only the routing
source moved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 22:32:42 +01:00
librelad
76138ddcdd Merge claude/1 2026-05-29 22:28:19 +01:00
librelad
2eaa5857a1 feat(webui): phase 0a — feature-module kernel scaffold (passive)
Adds the foundation of the feature-module architecture
(docs/frontend-modularization.md) as inert, additive code:
- kernel/feature-registry.js: window.LP.features — runtime register(),
  manifest loader, route-table + nav builders.
- features/manifest.dev.json: hand-committed manifest mirroring spa.js
  setupRoutes() exactly (route -> handler + navId).
- index.html loads the kernel before spa.js.

Zero behaviour change: nothing consults the registry yet. Phase 0b flips
routing to be registry-driven with the spa.js Map as fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 22:28:19 +01:00
librelad
8312f2222f Merge claude/1 2026-05-29 22:10:36 +01:00
librelad
22aafe3a55 docs: frontend feature-module modularization design
Synthesized architecture for turning the no-build vanilla-JS WebUI into a
scan-and-manifest feature system mirroring the backend container scan:
self-contained features/<id>/ folders, a navigation kernel, uniform
mount/unmount lifecycle, DI service context replacing ~80 window globals,
per-feature CSS, god-file decomposition, and a strangler migration roadmap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 22:10:36 +01:00
librelad
116e48699e Merge claude/1 2026-05-29 16:09:51 +01:00