librelad a28eed0729 fix(services): route per-service restart through the task system + CLI
The Services tab restart button POSTed to a backend endpoint that (a)
checked the app's compose path from INSIDE the webui container, where
the host's containers root isn't mounted — so every restart failed with
'Compose file not found' — and (b) queued a raw 'docker compose restart'
that the host task processor would run as the manager user, which can't
talk to the rootless daemon anyway. Errors surfaced via a bare alert().

Per-service restart now follows the exact shape of the whole-app verbs:

- CLI: 'libreportal app restart <app> [service]' — the optional service
  arg makes dockerRestartApp restart just that compose service, via
  dockerCommandRun (right user in rootless mode) from the app dir on the
  host, where the compose file actually lives. Service names validated
  against compose-legal characters before touching a shell line.
- WebUI: the button dispatches a 'service_restart' task action through
  the task router (mutations-via-tasks), runs in the background with the
  standard task toast + link — no page switch — and failures use the
  notification system instead of alert(). Because the task runs host-
  side, restarting the WebUI's own libreportal-service now works too.
- Backend: the mutating restart endpoint and its now-unused helpers are
  removed; service-routes.js is read-only surface (status + log tails).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-06-12 23:26:40 +01:00

49 lines
2.1 KiB
Bash
Executable File

#!/bin/bash
dockerRestartApp()
{
local app_name="$1"
local service_name="$2" # optional: restart just this one compose service
if [[ -z "$app_name" ]]; then
isNotice "No app name provided. Unable to restart containers."
return 1
fi
# Single-service restart — the WebUI Services tab's per-row button routes
# here as a task (libreportal app restart <app> <service>). Uses compose so
# the restart respects the deployed service definition, and dockerCommandRun
# so it runs as the right user in rootless mode.
if [[ -n "$service_name" ]]; then
# The name reaches a shell line — accept only compose-legal names.
if [[ ! "$service_name" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]]; then
isError "Invalid service name: $service_name"
return 1
fi
local app_dir="${containers_dir%/}/$app_name"
if [[ ! -f "$app_dir/docker-compose.yml" ]]; then
isError "No compose file for '$app_name' at $app_dir/docker-compose.yml"
return 1
fi
isNotice "Restarting service '$service_name' of '$app_name'. Please wait..."
local result; result=$(dockerCommandRun "cd $app_dir && docker compose restart $service_name" 2>&1)
checkSuccess "Restarted service '$service_name' of '$app_name'"
return
fi
isNotice "Restarting Docker containers for '$app_name'. Please wait..."
# Restart containers in one go
local result; result=$(dockerCommandRun "docker ps -aqf name=$app_name | xargs -r docker restart" >/dev/null 2>&1)
checkSuccess "Restarted Docker containers matching '$app_name'"
# App-specific restart hook — host-installed apps define restart<App> to
# restart their systemd services etc. (mirrors dockerUninstallApp's
# uninstall<App> hook). No-op for pure-docker apps that don't define one.
local app_name_ucfirst="$(tr '[:lower:]' '[:upper:]' <<< ${app_name:0:1})${app_name:1}"
local restartFuncName="restart${app_name_ucfirst}"
if declare -f "$restartFuncName" >/dev/null 2>&1; then
"$restartFuncName"
fi
}