Removing Focalboard from the catalogue left its icon still being served:
the sync only ever ADDS, so every app ever dropped leaves a file behind
that the portal keeps offering for something that is gone. Same shape as
the task queue that only ever appended.
webuiPruneAppIcons runs at the end of the sync and removes only icons it
can match to a missing template — anything else in the directory is left
alone.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
'local result=$(cmd)' resets $? to 0 (the local builtin's own exit), so the
following checkSuccess always saw success regardless of cmd's real exit — the
mechanism that masked the de-sudo write failures. Split declaration from
assignment ('local result; result=$(cmd)') across all 235 active-code sites
(84 files) so the command's exit reaches checkSuccess. No behaviour change
beyond $? now being accurate (no set -e in runtime code; multi-line
assignments transform safely).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
The frontend modularization moved icons to frontend/core/icons/ and updated the
frontend JS, but the host-side generators were never updated — they wrote the
apps.json/categories 'icon' field as /icons/apps/<app>.svg and /icons/categories/
<cat>.svg, and webui_app_icons.sh / webui_config.sh synced icon files into the
non-existent frontend/icons/apps. Those served paths 404 (text/html catch-all),
so every app card fell back to default.svg (the generic box) instead of its real
logo.
Repointed to /core/icons/... (where the SVGs actually live and serve as
image/svg+xml):
- webui_config.sh: icon dir + emitted apps.json icon path
- webui_app_icons.sh: icon sync dir + comment
- webui_container_setup.sh: comment
- webui_create_app_categories.sh: 11 category icon paths
Source fix only — the live apps.json refreshes on the next host-side regen
(lpRegen). NOT touched: scripts/backup/engines/*.json '/icons/config/backup.svg'
(that SVG lives at the oddly-nested components/admin/config/icons/config/ and
serves at neither path — needs a placement decision, flagged separately).
Signed-off-by: librelad <librelad@digitalangels.vip>
The uninstall branch of webuiUpdateAppLog removed the per-app WebUI log
with a bare `rm`. The log lives in the container data plane and is owned
by the container user, often without a write bit. A bare rm (run as root
via `sudo init.sh uninstall`) prompts interactively for write-protected
files — which hangs an otherwise-unattended deploy: the uninstall phase
of a `full` redeploy stopped dead at "rm: remove write-protected regular
file '.../frontend/logs/apps/<app>.log'?".
Route it through runFileOp rm -f (as the container-data owner, force) to
match the neighbouring uninstall_app.sh and the install branch's
owner-aware createTouch/runFileWrite helpers. No prompt, correct owner.
Signed-off-by: librelad <librelad@digitalangels.vip>
Introduce scripts/source/paths.sh as the canonical path resolver for three
independently-relocatable roots:
LP_SYSTEM_DIR manager-owned control plane (configs/logs/install/db/ssl/ssh/migrate)
LP_CONTAINERS_DIR container-user-owned live app data
LP_BACKUPS_DIR container-user-owned backup repos (own mount-able)
Roots come from the environment when set (install bakes them; CLI/app inherit
from init.sh), else default to /libreportal-*. A transitional compat default
keeps EXISTING installs (legacy single /docker tree, by config marker) on /docker
until a deliberate reinstall, so deploying this never strands a running box.
- init.sh derives the same vars inline (self-contained for the bare /root/init.sh
reinstall case); paths.sh mirrors it for the standalone task/check processors,
which now self-locate their scripts dir and source it.
- Replace functional /docker literals with the derived vars across runtime,
install, backup, crontab, crowdsec/restic, headscale, and reinstall paths;
clean the inert '== /docker/containers/*' guard fallbacks to the variable form.
- backend: CONTAINERS_DIR now from LP_CONTAINERS_DIR (compose env, filled at
generation via a new CONTAINERS_DIR_TAG), legacy-safe default for un-recreated
containers.
- backup default path falls back to the backups root; exclude paths.sh from the
sourced-file arrays (bootstrap file, sourced explicitly).
The CLI-wrapper heredoc + root helpers still reference /docker; those get baked
in phase 3. No layout/ownership change yet (phase 2).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
Same class of bug as the topbar partial: icon and data-file references were
relative (icons/apps/x.svg, data/apps/...), so on deep path routes (/app/<name>,
/admin/config/x) the browser resolved them against the route dir and the SPA
catch-all served index.html with HTTP 200 instead of 404 — broken images and
silently-wrong JSON.
Make every reference absolute (anchored on the quote/backtick so already-absolute
/icons paths are untouched):
- JS: all icons/ and data/ literals + templates across components/utils/system
- html/topbar.html: logo <img>
- generators: webui_config.sh and webui_create_app_categories.sh now emit
/icons/... into apps.json / apps-categories.json (regenerated on install)
- updated the two icon-path comments to match
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
The CLI wrapper already runs as the manager (libreportal) but then did
'sudo ./start.sh', so the whole runtime executed as root — the reason
NOPASSWD:ALL was load-bearing. Drop that sudo so start.sh runs as the
manager; also drop the now-redundant sudo from the wrapper's own
manager-owned ops (config sed, /docker/configs + /docker/install
mkdir/cp/chown/rm, 'sudo -u libreportal' git clone, chmod). Only the
'cp -f init.sh /root/' copies stay root.
Running as the manager surfaced data-plane writes that only worked under
root; fixed to be owner-correct:
- webui_system_metrics: .metrics_{cpu,net}_prev state via runFileWrite
- atomicWriteWebUI: path-aware temp+chmod+mv (atomic same-dir rename as
the path owner) instead of bare >/mv
- webui_app_config last_update trigger via runFileWrite
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
The WebUI data snapshots (locations.json, dashboard.json, snapshots_*.json,
etc.) are regenerated on every wizard/config change. Each file emitted two
extra success lines via createTouch — "Touching <file>" and "Updating
<file> with <user> ownership" — which spammed the output around the genuinely
useful "... JSON regenerated" line.
Add an optional "silent" flag to createTouch (third arg; default keeps the
existing loud behaviour for interactive install flows) and pass it from every
WebUI data generator/task. Touch + chown still run; only the logging is
suppressed for these background regenerations.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
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>