LibrePortal/scripts/cli/commands/app/cli_app_commands.sh
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

192 lines
7.2 KiB
Bash
Executable File

#!/bin/bash
# App Commands Handler
# Handles all app subcommands by calling core functions
cliHandleAppCommands()
{
local action="$initial_command2"
local app_name="$initial_command3"
local config="$initial_command4"
local restore_arg2="$initial_command4"
local restore_arg3="$initial_command5"
local restore_arg4="$initial_command6"
local tool_name="$initial_command4"
local tool_args="$initial_command5"
local reset_network="false"
if [[ "$config" == "--reset-network" ]]; then
reset_network="true"
config=""
elif [[ "$initial_command5" == "--reset-network" ]]; then
reset_network="true"
fi
case "$action" in
"list")
if [[ -z "$app_name" ]]; then
cliShowAppHelp
elif [ "$app_name" = "available" ]; then
appScanAvailable
elif [ "$app_name" = "installed" ]; then
databaseListInstalledApps
else
isNotice "Invalid list type: $app_name"
cliShowAppHelp
fi
;;
"install")
if [[ "$LIBREPORTAL_TASK_EXEC" == "1" ]]; then
dockerInstallApp "$app_name" "$config" "$reset_network"
else
local _mode=""
for _arg in "$config" "$initial_command5" "$initial_command6"; do
[[ "$_arg" == "--detach" ]] && _mode="--detach"
done
# config / --reset-network passthrough — strip CLI-only
# flags, keep what dockerInstallApp expects.
local _passthrough_config="$config"
[[ "$_passthrough_config" == "--detach" || "$_passthrough_config" == "--reset-network" ]] && _passthrough_config=""
local _cmd="libreportal app install $app_name"
[[ -n "$_passthrough_config" ]] && _cmd+=" '$_passthrough_config'"
[[ "$reset_network" == "true" ]] && _cmd+=" --reset-network"
cliTaskRun "$_cmd" "install" "$app_name" "$_mode"
fi
;;
"uninstall")
# Optional `--delete-images` flag (in any of the trailing
# positions) tells the uninstall to also remove the app's
# docker images. Default behaviour: keep them so a reinstall
# is fast and offline-friendly.
local _del_images="false"
local _del_tasks="false"
local _u_mode=""
for _arg in "$config" "$initial_command5" "$initial_command6" "$initial_command7"; do
[[ "$_arg" == "--delete-images" ]] && _del_images="true"
[[ "$_arg" == "--delete-tasks" ]] && _del_tasks="true"
[[ "$_arg" == "--detach" ]] && _u_mode="--detach"
done
if [[ "$LIBREPORTAL_TASK_EXEC" == "1" ]]; then
dockerUninstallApp "$app_name" "$_del_images" "$_del_tasks"
else
local _cmd="libreportal app uninstall $app_name"
[[ "$_del_images" == "true" ]] && _cmd+=" --delete-images"
[[ "$_del_tasks" == "true" ]] && _cmd+=" --delete-tasks"
cliTaskRun "$_cmd" "uninstall" "$app_name" "$_u_mode"
fi
;;
"start")
if [[ "$LIBREPORTAL_TASK_EXEC" == "1" ]]; then
dockerStartApp "$app_name"
else
cliTaskRun "libreportal app start $app_name" "start" "$app_name"
fi
;;
"stop")
if [[ "$LIBREPORTAL_TASK_EXEC" == "1" ]]; then
dockerStopApp "$app_name"
else
cliTaskRun "libreportal app stop $app_name" "stop" "$app_name"
fi
;;
"restart")
# Optional 4th arg = one compose service to restart instead of the
# whole app (the WebUI Services tab routes per-service restarts
# here): libreportal app restart <app> [service]
if [[ "$LIBREPORTAL_TASK_EXEC" == "1" ]]; then
dockerRestartApp "$app_name" "$config"
else
cliTaskRun "libreportal app restart $app_name${config:+ $config}" "restart" "$app_name"
fi
;;
"up")
if [[ "$LIBREPORTAL_TASK_EXEC" == "1" ]]; then
dockerComposeUp "$app_name"
else
cliTaskRun "libreportal app up $app_name" "up" "$app_name"
fi
;;
"down")
if [[ "$LIBREPORTAL_TASK_EXEC" == "1" ]]; then
dockerComposeDown "$app_name"
else
cliTaskRun "libreportal app down $app_name" "down" "$app_name"
fi
;;
"reload")
if [[ "$LIBREPORTAL_TASK_EXEC" == "1" ]]; then
dockerRestartAppViaInstall "$app_name"
else
cliTaskRun "libreportal app reload $app_name" "reload" "$app_name"
fi
;;
"backup")
if [[ -z "$app_name" ]]; then
isNotice "No app provided."
cliShowAppHelp
elif [[ "$LIBREPORTAL_TASK_EXEC" == "1" ]]; then
backupAppStart "$app_name"
else
cliTaskRun "libreportal app backup $app_name" "backup" "$app_name"
fi
;;
"restore")
if [[ -z "$app_name" ]]; then
isNotice "No app provided."
cliShowAppHelp
elif [[ "$LIBREPORTAL_TASK_EXEC" == "1" ]]; then
cliAppRestore "$app_name" "$restore_arg2" "$restore_arg3" "$restore_arg4"
else
# Pass the positional args through verbatim. They may
# include local|remote1|… selector, filename, password —
# quote each to survive shell re-parse in the processor.
local _cmd="libreportal app restore $app_name"
for _a in "$restore_arg2" "$restore_arg3" "$restore_arg4"; do
[[ -n "$_a" ]] && _cmd+=" '$_a'"
done
cliTaskRun "$_cmd" "restore" "$app_name"
fi
;;
"status")
if [[ -z "$app_name" ]]; then
isNotice "No app provided."
cliShowAppHelp
else
appStatus "$app_name"
fi
;;
"tool")
# `libreportal app tool list [<app>]` — discover available tools.
# When the second arg is the literal `list`, the third is treated
# as an optional app filter. Otherwise the standard run shape
# applies: `libreportal app tool <app> <tool_id> [args]`.
if [[ "$app_name" == "list" ]]; then
cliAppToolList "$tool_name"
elif [[ -z "$app_name" || -z "$tool_name" ]]; then
isNotice "Usage: libreportal app tool <app_name> <tool_name> [args]"
isNotice " libreportal app tool list [<app_name>]"
cliShowAppHelp
else
dockerAppRunTool "$app_name" "$tool_name" "$tool_args"
fi
;;
*)
isNotice "Invalid app command: $action"
cliShowAppHelp
;;
esac
}