26 Commits

Author SHA1 Message Date
librelad
16eda07b3d fix(webui): make SSH Access page full-width like config/admin pages
The SSH Access page was boxed to max-width 860px and centered, unlike the
Overview and System admin pages (.admin-page) which span the full content
width. Drop the cap and match .admin-page padding so /admin/tools/ssh-access
looks like the rest of the Admin area.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 22:12:50 +01:00
librelad
62f7a84126 feat(webui): Admin System page with gauges, trend charts & per-app stats
New 'System' admin page (sidebar Tools group) rendering the metrics the
collector now produces:
- live ring gauges for CPU, memory, disk and load
- SVG trend charts (CPU/mem/disk/network) with 1h/6h/24h range toggle
- host info + swap + docker summary strips
- per-app table: CPU/mem bars, network, status, CPU sparkline

Charts are hand-rolled SVG in charts.js (LPCharts) — no third-party libs or
CDN calls — themed entirely from the active theme's CSS variables. The
Overview System card now links here.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-24 16:47:20 +01:00
librelad
23a15345fb refactor(admin): sidebar Config/Tools groups, per-group breadcrumbs, SSH matches config layout
- Sidebar now groups items: Overview at top, a 'Config' heading over the config
  categories, and the existing 'Tools' heading over SSH Access.
- Breadcrumb reflects the group: config pages read 'Config' (was 'Admin'), SSH
  reads 'Tools', Overview stays 'Admin'.
- SSH Access page restyled to the config page's section layout
  (.config-category/.domains-wrapper sections) instead of backup-style cards, so
  it matches the other Admin config pages.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-23 18:19:25 +01:00
librelad
b5107e30cc feat(admin): Admin Overview landing + unified Admin page headers
Add an Admin Overview as the Admin landing (default when you open Admin): an
ops/health board distinct from the user Dashboard. Four cards built from data
we already generate — Updates (update_status.json, with one-click update),
Backups (backup dashboard.json), SSH & Security (access.json), System
(disk/memory/system_info) — each with a Manage link into the right section.
Styled like the backup dashboard (tiles/status dots).

Wire-up: 'Overview' is the top sidebar item and the default category
(handleConfig + sidebar), rendered by AdminOverview into #config-section via a
renderConfig('overview') special case. Every Admin page now shows the same
'Admin' breadcrumb header (Overview, SSH Access, and the config categories) for
a consistent Admin → Section feel. User Dashboard gets an 'Admin overview →'
link.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-23 17:57:21 +01:00
librelad
4fd043a852 refactor(webui): fold SSH Access into an Admin area
Rename the Config top-nav to 'Admin' and move SSH Access into its sidebar
under a 'Tools' group, instead of a separate top-level nav item. SSH Access is
rendered by SshPage into the config main pane via a renderConfig('ssh-access')
special case; the sidebar item (config-sidebar.js) routes there. SshPage now
mounts into any container (defaults to #config-section). /ssh redirects to
/config?=ssh-access for old links; the standalone ssh-content.html is removed.

Declutters the top bar and gives system/admin features one home that scales
(updates, users, Connect settings can become sidebar entries later).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-23 17:31:26 +01:00
librelad
e75f10618d feat(ssh): WebUI SSH Access page
New /ssh page (topbar nav + SPA route + SshPage controller + ssh-content.html
+ ssh.css). Reads data/ssh/access.json and lets the admin: paste a public key
to authorize a machine, remove keys, and toggle key-only login — all via
'libreportal ssh ...' tasks through the backend's lockout guards. Reuses the
backup key-card styles for a consistent look. This is the inbound counterpart
to the backup location key card (outbound): same paste-a-key model, opposite
direction.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-23 16:52:47 +01:00
librelad
d3faa2514f feat(backup): SSH key card in the sftp location editor
When a location uses SSH key auth, show a key card: paste an existing private
key, or 'Generate keypair', then the card displays the public key to copy into
the remote server's authorized_keys (with Copy/Delete). Wires to the
ssh-key-set/generate/delete CLI; key mutations refresh locations.json so the
card reflects state immediately. applySshAuthVisibility toggles the card vs the
password field by auth mode. Private key only ever flows in (base64); only the
public key is ever shown.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-23 16:17:34 +01:00
librelad
459609a35b style(backup): polish location tabs — drop stray descriptions, pad panels, round corners
- Remove the per-tab 'How LibrePortal connects…' description lines; the tab
  labels already say what each panel is, and the paragraphs read as misplaced
  titles.
- Give the tab panels even, comfortable padding (tabs-content padding zeroed so
  the panel owns it) instead of the cramped 2px sides.
- Round the tab strip's top corners (.tabs-list) so the strip + content read as
  one card — .tabs-content already rounds the bottom, leaving the top square.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-23 15:07:19 +01:00
librelad
02e4f7d6ab style(backup): match location editor tabs to the app-detail tab design
Reuse the shared .tabs-wrapper/.tab-button/.tab-panel components (same as an
app's Config/Tasks tabs) for the location editor instead of bespoke tab CSS:
emoji + label buttons, equal-width strip, accent active state. Panels toggle
via the .active class like the rest of the UI; only the panel padding is
trimmed so it nests inside the backup row.

Also drop the now-dead 'No advanced options' empty state — every type has at
least Engine + append-only in the Advanced tab.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-23 14:46:03 +01:00
librelad
6da8f80477 feat(backup): tabbed location editor (Connection / Retention / Advanced)
The expanded location row was one long form. Split it into tabs so it opens
showing only the Connection fields. Retention moves from a stacked section
into its own tab, and the advanced overrides (URI/SSH port/append-only) get
their own tab instead of the inline disclosure from the previous pass.

Field grouping is metadata-driven: locFieldGroups partitions a type's fields
into Connection vs Advanced via the configs.json "advanced" flag (with
LOC_ADVANCED_SUFFIXES as the legacy fallback). Type changes rebuild both the
Connection and Advanced panels since advanced fields are type-dependent too.
Save still reads every field across all panels (hidden tabs stay in the DOM).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-23 14:31:36 +01:00
librelad
c5ecc520aa feat(backup): system-driven location fields with an Advanced reveal
The Locations editor now renders field metadata from configs.json
(window.configData) instead of relying on the hardcoded BACKUP_LOC_FIELD_DEFS,
which drops to a fallback. Fields flagged advanced (URI override, SSH port,
append-only) move out of the main grid into a full-width "Advanced"
disclosure that's collapsed by default, so the common case stays simple.

Also load the unified config once on the backup page into window.configData
(metadata) + a flat window.systemConfigs (values). Previously systemConfigs
was only populated after a save — and with the full nested JSON, while the
code reads it as a flat map — so default-engine lookups and save-time change
detection silently misbehaved on first load. Both are now correct.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-23 13:44:41 +01:00
librelad
61ed8aa7f2 style(config): match toggle box height to input fields
The config-grid toggle box used the input's 12px vertical padding, but its
24px pill made it render 48px tall vs the inputs' 44px, so it sat too tall
to read as inline. Trim vertical padding to 10px so the box is 44px.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-23 13:21:34 +01:00
librelad
06a0e9de3c style(config): soften section divider; align toggle box with input fields
Divider: .domains-divider was a bold 2px accent bar under every section header,
which read as a stray line. Drop it to a subtle 1px low-opacity neutral rule so
it separates without shouting.

Toggle: the boxed config toggle (.checkbox-label) used a different radius (10px),
fill (0.04) and border (0.10) than the .form-control inputs beside it (8px /
0.05 / 0.20), so it looked off and out of line. Match it to the input field box
exactly so toggles and inputs read as the same surface. The app-config
borderless toggle override is unaffected.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-23 01:17:48 +01:00
librelad
bab89df191 style(backup): add bottom margin to location action row
Give the per-location Save changes / Delete location row some breathing room
from the bottom of the expanded card.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-23 01:03:30 +01:00
librelad
25027da86e style(backup): add icons to location buttons; move nebula CSS into theme folder
Buttons: the per-location Save changes / Delete location buttons had no icons,
unlike the apps-config action buttons. Add a save (floppy) icon and a trash
icon so they match the reference; colour comes from the nebula button groups
they already belong to.

Theme refactor: move the theme-specific [data-theme="nebula"] button/topbar/CTA
rules out of the shared css/themes.css and into themes/nebula/theme.css, where
the README says theme overrides belong. css/themes.css keeps only the generic,
non-theme-scoped defaults (solid status/accent buttons, danger-zone,
warning-banner) shared by dark-blue/light. No behaviour change: the nebula file
loads after css/themes.css so the moved rules still win.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-23 01:01:01 +01:00
librelad
d7d5260605 style(backup): use nebula translucent buttons; left-align location actions
Two follow-ups to the button restyle:

- On the nebula theme, primary/danger CTAs are translucent (rgba accent/danger
  fill + white text + border), not the solid generic .btn-primary. The earlier
  change only added the backup classes to the generic groups, so on nebula the
  Add location / Save changes / Delete buttons fell back to a solid fill with
  dark text. Add .backup-primary-btn and .backup-danger-btn to the
  [data-theme="nebula"] groups too, so they match the config-page buttons.

- The per-location action row used justify-content: space-between, throwing the
  two buttons to opposite edges. Switch to flex-start with a gap (like
  .config-actions) and put Save changes (primary) before Delete location.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-23 00:47:43 +01:00
librelad
0f6e15e8f9 style(backup): match primary/danger buttons to nebula config-page style
The backup pages' primary buttons (Add location, Save changes) and Delete
location button used a local gradient + glow in backup.css, so they didn't
match the flat solid-accent buttons on the config page. Add .backup-primary-btn
and .backup-danger-btn to the shared nebula button groups in themes.css
(.btn-primary / .btn-uninstall) so they get the same solid accent/danger fill
and hover with !important across themes, and drop the local gradient/shadow/lift
from backup.css. Top-right and bottom-of-page backup buttons now match config.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-23 00:31:37 +01:00
librelad
4568ec51ef feat(backup): Export dropdown in Configuration header; warning is dismiss-only
Drop the Export button from the config-backup warning banner — it's now just the alert + dismiss (x). On the Configuration tab the top-right primary action becomes an 'Export' dropdown (first item: Repository Passwords, reusing the existing export-passwords action) so more export types can be added later. Other tabs keep Backup all apps / Add location. Menu opens from the trigger and closes on outside click, item click, or tab switch.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-23 00:14:06 +01:00
librelad
14ba3b03c7 feat(backup): make config-backup warning stand out and dismissible
Add a large amber alert-triangle icon to the 'keep your config backed up offline' banner and a close (x) button in its top-right. Dismissal is stored in localStorage (libreportal:backup-config-warning-dismissed) — a per-browser UI nudge, not server config — and hides both the banner and its divider until cleared.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-23 00:07:38 +01:00
librelad
d75024b22c fix(webui): portal custom-select popup to body so cards' hover-transform can't break it
Location config dropdowns (Type, Path, etc.) live inside .task-item cards,
whose :hover applies transform: translateY(-2px). A transformed element
becomes the containing block for position:fixed descendants, so the popup —
previously a child of the card — was positioned with viewport coords against
the card instead of the viewport (wrong placement) and perturbed layout
(content shifted left).

Portal the popup to <body> on open and detach on close, so position:fixed is
always relative to the viewport regardless of any transformed/overflow
ancestor. flip-up styling moves onto the popup element and the topbar's wider
popup is carried via a class, since the popup no longer nests in the wrapper.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-22 14:57:59 +01:00
librelad
3b13a67ca7 feat(backup): tidy location editor — section dividers, style tooltip, row enable toggle
Make the expanded location editor read like /config: Connection and Retention now use the section header + .domains-divider layout, and Connection gets a description. Move the retention 'Backup style' guidance into a tooltip and drop the always-visible hint line below it. Move the Enabled toggle out of the Connection fields into the collapsed location row header so a location can be enabled/disabled without expanding it; setLocationEnabled persists the change via the same config_update routing as saveSection.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-22 14:09:32 +01:00
librelad
1fc7ff95a2 style(config): divider below features Danger Zone, drop config-actions top padding
Add a config-divider after the header-only Danger Zone banner on the features page so a line separates it from the feature fields. Drop the now-redundant 24px top padding on .config-actions since the divider above the Save/Reset buttons already provides that spacing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-22 13:32:16 +01:00
librelad
2361f23607 feat(config): add divider above the advanced container
Emit a config-divider before the Danger Zone / "Show Advanced Options" container in the shared renderer, so a line separates the regular fields from the advanced toggle — mirroring the dividers above Save/Reset and inside #advanced-sections. Applies to every config category.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-22 13:26:21 +01:00
librelad
afaa43de36 feat(config): add section dividers to the config form
Add a thin divider above the Save/Reset buttons, and one at the top of
#advanced-sections so a line appears between the "Show Advanced Options"
toggle and the advanced fields only when they're revealed. Shared config
renderer, so it applies to every config category (backup included).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-22 11:41:53 +01:00
librelad
d5fe1bc56b feat(webui): out-of-date detection + one-click update
Surface when LibrePortal is behind upstream and let users update from the
WebUI, reusing the proven git-update path instead of reinventing it.

Detection (host): webuiSystemUpdateCheck writes
frontend/data/system/update_status.json from a throttled git fetch +
behind-count + VERSION compare, off the existing per-minute
`webui generate system` cron. A new /VERSION file is the canonical version.

Display (frontend): update-notifier.js/.css render a global topbar badge
(every page) and a dashboard banner (prominent when behind, subtle "up to
date" with a manual check otherwise), plus a details panel.

Actions go through the task pipeline:
- `libreportal update apply` -> webuiRunUpdate (non-interactive: guards,
  forced check, gitPerformUpdate, then dockerInstallApp libreportal)
- `libreportal update check` -> forced recheck

gitFolderResetAndBackup's body is extracted into gitPerformUpdate (no exit)
so the WebUI path can reuse it; the interactive CLI flow is unchanged.

Detection JSON verified against the repo (up-to-date and behind cases).
webuiRunUpdate's re-clone + redeploy still needs validation on a live host.

The latest-version source is git for now and is the single swap point for
get.libreportal.org later — the JSON contract and frontend stay unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-21 23:33:43 +01:00
librelad
875a60f90f LibrePortal v0.1.0 — initial release
A free, open, self-hosted app platform (GNU AGPLv3): one-click app deploys,
Traefik reverse proxy with automatic SSL, rootless Docker support, gluetun
VPN routing, and a web dashboard to manage it all.

Free & open forever to self-host; optional paid hosted services fund it.
See PROMISE.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-21 20:37:54 +01:00