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>
92 lines
4.2 KiB
Bash
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))."
|
|
}
|