librelad 898068a390 refactor(apps): make app tools + helpers fully self-contained per app
Each app now carries everything under containers/<app>/: Tools-tab actions in
tools/ (declaration <app>.tools.json + function <app>_<tool_id>.sh) and logic
helpers in scripts/ (e.g. <app>_auth.sh). The container scan live-sources every
.sh under the app (maxdepth 3, prunes only resources/) and webui_tools.sh
auto-merges the .tools.json, so an app is a true drop-in — no central edit, no
array regen.

- Empty the central webui_tools.sh heredoc; all 34 tools across 11 apps now
  come from per-app declarations (verified byte-identical to the old output).
- Retire the orphaned mattermost tool scripts to scripts/unused (there is no
  containers/mattermost; its install fn already lived in unused).
- Update the dispatch comment/error path, the auth-adapter doc, and
  DEVELOPMENT.md to the new convention.
- Regenerate static arrays (files_app.sh no longer lists app/containers/*).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 22:45:33 +01:00

92 lines
4.2 KiB
Bash

#!/bin/bash
# Single source of truth for the Tools tab. The frontend reads
# /data/apps/generated/apps-tools.json — this generator emits it.
#
# Tools are self-contained per app. To add one:
# 1. Declare it in containers/<app>/tools/<app>.tools.json (a { "tools": [ … ] }
# object — auto-merged below; no edit to this file needed).
# 2. Drop the one-function file beside it at
# containers/<app>/tools/<app>_<tool_id>.sh with
# `app<App><PascalCase(toolId)>()` defined inside (live-sourced by the
# container scan).
# 3. Re-run the WebUI updater (or call this function directly) to
# regenerate apps-tools.json.
#
# Tool entry schema (matches what tools-manager.js expects):
# {
# id: "<unique within the app>", # passed to dispatcher
# label: "Reset User Password", # button + modal heading
# description: "One-line summary", # shown on card + modal
# icon: "🔑", # any emoji
# destructive: false, # red button if true
# confirm: "Are you sure?", # forces a modal even
# # when fields is empty
# fields: [] # see field schema below
# }
#
# Field schema (each form input the modal collects):
# { name, label, type: text|password|number|select|checkbox|textarea,
# default, placeholder, required, options (for select), min, max }
webuiGenerateAppsToolsConfig() {
# The WebUI task service is a long-lived bash process that sources
# this file once at startup. When the auto-updater rewrites this
# script on disk (e.g. to add a new app's tools entry), the function
# in memory stays stale until the service restarts — producing
# apps-tools.json without the new entries. Re-source ourselves on
# every call so heredoc edits take effect immediately, then dispatch
# to the freshly-loaded version. The guard prevents infinite
# recursion.
# Skip the reload if the file is missing right now — happens briefly
# during update.sh quick (find -delete + cp). Fall through to the
# in-memory version (one cycle stale at worst).
if [[ -z "$_WEBUI_TOOLS_RELOADED" && -f "${BASH_SOURCE[0]}" ]]; then
local _WEBUI_TOOLS_RELOADED=1
source "${BASH_SOURCE[0]}"
webuiGenerateAppsToolsConfig "$@"
return $?
fi
local output_file="${containers_dir}libreportal/frontend/data/apps/generated/apps-tools.json"
local tmp="$(mktemp)"
runFileOp mkdir -p "$(dirname "$output_file")"
# Start empty; every app declares its own tools (merged below). The heredoc is
# kept as the seed so a central tool could be added here if ever needed.
cat > "$tmp" <<'JSON'
{
"apps": {}
}
JSON
# Merge per-app tool declarations so any app — core or DROPPED-IN (e.g. from
# LibrePortal-Infra) — registers its own Tools tab actions without editing this
# file. Each app ships containers/<app>/tools/<app>.tools.json = { "tools": [ … ] };
# it sets .apps[<app>]. The tool functions live beside it in the same tools/
# folder and are live-sourced by the container scan.
if command -v jq >/dev/null 2>&1; then
local _tj _app
for _tj in "${install_containers_dir}"*/tools/*.tools.json; do
[[ -f "$_tj" ]] || continue
_app="$(basename "$(dirname "$(dirname "$_tj")")")" # …/<app>/tools/<x>.tools.json
if jq -e . "$_tj" >/dev/null 2>&1; then
jq --arg app "$_app" --slurpfile t "$_tj" '.apps[$app] = $t[0]' "$tmp" > "$tmp.m" && mv "$tmp.m" "$tmp"
else
isNotice "Skipping invalid tools file: $_tj"
fi
done
fi
if command -v jq >/dev/null 2>&1; then
if ! jq . "$tmp" >/dev/null 2>&1; then
isNotice "Generated apps-tools.json failed JSON validation; keeping existing file."
rm -f "$tmp"
return 0
fi
fi
runFileWrite "$output_file" < "$tmp"; rm -f "$tmp"
isSuccessful "Generated apps-tools.json ($(jq '[.apps[].tools | length] | add' "$output_file" 2>/dev/null || echo "?") tool(s) across $(jq '.apps | length' "$output_file" 2>/dev/null || echo "?") app(s))."
}