diff --git a/scripts/cli/commands/app/cli_app_commands.sh b/scripts/cli/commands/app/cli_app_commands.sh index 953db78..0ec6a6d 100755 --- a/scripts/cli/commands/app/cli_app_commands.sh +++ b/scripts/cli/commands/app/cli_app_commands.sh @@ -37,15 +37,6 @@ cliHandleAppCommands() ;; "install") - # Two paths in / out: - # * The task processor's recursive invocation carries - # LIBREPORTAL_TASK_EXEC=1 — run the install inline so it - # actually does the work (otherwise we'd loop forever - # re-enqueueing). - # * The user typed `libreportal app install dashy` — enqueue - # a task and follow its log in the foreground, so the - # CLI + WebUI watch the same job and locking serialises - # parallel attempts. `--detach` queues and exits. if [[ "$LIBREPORTAL_TASK_EXEC" == "1" ]]; then dockerInstallApp "$app_name" "$config" "$reset_network" else @@ -53,17 +44,17 @@ cliHandleAppCommands() for _arg in "$config" "$initial_command5" "$initial_command6"; do [[ "$_arg" == "--detach" ]] && _mode="--detach" done - # config / --reset-network passthrough — strip the flags - # that are CLI-only, keep what dockerInstallApp expects. + # 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_TASK_EXEC=1 libreportal app install $app_name" + 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 @@ -71,43 +62,78 @@ cliHandleAppCommands() # 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 - dockerUninstallApp "$app_name" "$_del_images" "$_del_tasks" + 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") - dockerStartApp "$app_name" + if [[ "$LIBREPORTAL_TASK_EXEC" == "1" ]]; then + dockerStartApp "$app_name" + else + cliTaskRun "libreportal app start $app_name" "start" "$app_name" + fi ;; - + "stop") - dockerStopApp "$app_name" + if [[ "$LIBREPORTAL_TASK_EXEC" == "1" ]]; then + dockerStopApp "$app_name" + else + cliTaskRun "libreportal app stop $app_name" "stop" "$app_name" + fi ;; - + "restart") - dockerRestartApp "$app_name" + if [[ "$LIBREPORTAL_TASK_EXEC" == "1" ]]; then + dockerRestartApp "$app_name" + else + cliTaskRun "libreportal app restart $app_name" "restart" "$app_name" + fi ;; - + "up") - dockerComposeUp "$app_name" + if [[ "$LIBREPORTAL_TASK_EXEC" == "1" ]]; then + dockerComposeUp "$app_name" + else + cliTaskRun "libreportal app up $app_name" "up" "$app_name" + fi ;; - + "down") - dockerComposeDown "$app_name" + if [[ "$LIBREPORTAL_TASK_EXEC" == "1" ]]; then + dockerComposeDown "$app_name" + else + cliTaskRun "libreportal app down $app_name" "down" "$app_name" + fi ;; - + "reload") - dockerRestartAppViaInstall "$app_name" + 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 - else + elif [[ "$LIBREPORTAL_TASK_EXEC" == "1" ]]; then backupAppStart "$app_name" + else + cliTaskRun "libreportal app backup $app_name" "backup" "$app_name" fi ;; @@ -115,8 +141,17 @@ cliHandleAppCommands() if [[ -z "$app_name" ]]; then isNotice "No app provided." cliShowAppHelp - else + 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 ;; diff --git a/scripts/cli/commands/backup/cli_backup_commands.sh b/scripts/cli/commands/backup/cli_backup_commands.sh index 5361d37..3c5afba 100755 --- a/scripts/cli/commands/backup/cli_backup_commands.sh +++ b/scripts/cli/commands/backup/cli_backup_commands.sh @@ -19,7 +19,11 @@ cliHandleBackupCommands() ""|help) cliShowBackupHelp ;; create) [[ -z "$name" ]] && { isNotice "No app name provided."; cliShowBackupHelp; return; } - backupAppStart "$name" + if [[ "$LIBREPORTAL_TASK_EXEC" == "1" ]]; then + backupAppStart "$name" + else + cliTaskRun "libreportal backup app create $name" "backup" "$name" + fi ;; schedule) [[ -z "$name" ]] && { isNotice "No app name provided."; cliShowBackupHelp; return; } diff --git a/scripts/cli/commands/update/cli_update_commands.sh b/scripts/cli/commands/update/cli_update_commands.sh index af03026..30d16e4 100755 --- a/scripts/cli/commands/update/cli_update_commands.sh +++ b/scripts/cli/commands/update/cli_update_commands.sh @@ -18,9 +18,14 @@ cliHandleUpdateCommands() webuiSystemUpdateCheck "force" ;; "apply"|"now") - # Non-interactive: perform the update. Used by the WebUI - # "Update now" action. - webuiRunUpdate + # Non-interactive update. Routes through the task processor + # (same path the WebUI's "Update now" button uses) so CLI + # and WebUI updates share locking + the audit trail. + if [[ "$LIBREPORTAL_TASK_EXEC" == "1" ]]; then + webuiRunUpdate + else + cliTaskRun "libreportal update apply" "update" "" "" + fi ;; *) isNotice "Invalid update command: ${RED}$update_type${NC}" diff --git a/scripts/crontab/task/crontab_task_processor.sh b/scripts/crontab/task/crontab_task_processor.sh index 892efb6..8ba768e 100755 --- a/scripts/crontab/task/crontab_task_processor.sh +++ b/scripts/crontab/task/crontab_task_processor.sh @@ -270,6 +270,14 @@ runTask() { runFileOp chmod 666 "$logFile" 2>/dev/null export LIBREPORTAL_NONINTERACTIVE=1 + # Universal task-execution bypass: when this env var is set, any CLI + # handler that would normally route through the task processor runs + # inline instead. Without it, the recursive `libreportal app install …` + # we exec below would itself enqueue another task — infinite recursion. + # Lives on the processor's eval-subshell env so it travels with every + # command, including the WebUI's pre-existing tasks that don't carry + # the prefix themselves. + export LIBREPORTAL_TASK_EXEC=1 # Run the command in a subshell so: # * `eval` inherits the daemon's full env (PATH, functions, vars). Switching