From 898068a390701e909b087e6924404546922cd1c5 Mon Sep 17 00:00:00 2001 From: librelad Date: Mon, 25 May 2026 22:45:33 +0100 Subject: [PATCH] refactor(apps): make app tools + helpers fully self-contained per app MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each app now carries everything under containers//: Tools-tab actions in tools/ (declaration .tools.json + function _.sh) and logic helpers in scripts/ (e.g. _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 Signed-off-by: librelad --- .../adguard/scripts}/adguard_auth.sh | 0 .../bookstack/scripts}/bookstack_auth.sh | 0 .../bookstack/tools/bookstack.tools.json | 106 +++++ .../tools}/bookstack_create_account.sh | 0 .../bookstack/tools}/bookstack_delete_user.sh | 0 .../bookstack/tools}/bookstack_list_users.sh | 0 .../tools}/bookstack_reset_password.sh | 0 .../bookstack/tools}/bookstack_set_admin.sh | 0 .../crowdsec/scripts}/crowdsec_alerts_list.sh | 0 .../scripts}/crowdsec_console_disable.sh | 0 .../scripts}/crowdsec_console_enroll.sh | 0 .../scripts}/crowdsec_decisions_list.sh | 0 .../scripts}/crowdsec_fix_priority.sh | 0 .../crowdsec/scripts}/crowdsec_hub_update.sh | 0 .../crowdsec/scripts}/crowdsec_metrics.sh | 0 .../crowdsec/scripts}/crowdsec_status.sh | 0 .../crowdsec/scripts}/crowdsec_unban.sh | 0 .../crowdsec/scripts}/crowdsec_update.sh | 0 .../scripts}/crowdsec_verify_firewall.sh | 0 .../dashy/scripts}/dashy_update_conf.sh | 0 containers/dashy/tools/dashy.tools.json | 21 + .../dashy/tools}/dashy_manage_shortcuts.sh | 0 .../focalboard/scripts}/focalboard_auth.sh | 0 .../scripts}/focalboard_set_admin.sh | 0 .../focalboard/tools/focalboard.tools.json | 82 ++++ .../tools}/focalboard_create_account.sh | 0 .../tools}/focalboard_delete_user.sh | 0 .../tools}/focalboard_list_users.sh | 0 .../tools}/focalboard_reset_password.sh | 0 .../gitea/scripts}/gitea_auth.sh | 0 containers/gitea/tools/gitea.tools.json | 105 +++++ .../gitea/tools}/gitea_create_account.sh | 0 .../gitea/tools}/gitea_delete_user.sh | 0 .../gitea/tools}/gitea_list_users.sh | 0 .../gitea/tools}/gitea_reset_password.sh | 0 .../gitea/tools}/gitea_set_admin.sh | 0 .../scripts}/gluetun_recreate_routed.sh | 0 containers/gluetun/tools/gluetun.tools.json | 11 + .../tools}/gluetun_refresh_providers.sh | 0 .../invidious/scripts}/invidious_auth.sh | 0 .../invidious/scripts}/invidious_set_admin.sh | 0 .../invidious/tools/invidious.tools.json | 75 ++++ .../tools}/invidious_create_account.sh | 0 .../invidious/tools}/invidious_delete_user.sh | 0 .../invidious/tools}/invidious_list_users.sh | 0 .../tools}/invidious_reset_password.sh | 0 .../nextcloud/scripts}/nextcloud_auth.sh | 0 .../nextcloud/tools/nextcloud.tools.json | 177 +++++++++ .../tools}/nextcloud_add_trusted_domain.sh | 0 .../tools}/nextcloud_create_account.sh | 0 .../nextcloud/tools}/nextcloud_delete_user.sh | 0 .../nextcloud/tools}/nextcloud_list_users.sh | 0 .../tools}/nextcloud_rescan_files.sh | 0 .../tools}/nextcloud_reset_password.sh | 0 .../nextcloud/tools}/nextcloud_set_admin.sh | 0 .../tools}/nextcloud_system_status.sh | 0 .../nextcloud/tools}/nextcloud_tail_logs.sh | 0 .../tools}/nextcloud_toggle_maintenance.sh | 0 .../scripts}/owncloud_setup_config.sh | 0 containers/pihole/tools/pihole.tools.json | 11 + .../pihole/tools}/pihole_apply_dns_updater.sh | 0 .../traefik/scripts}/traefik_auth.sh | 0 containers/traefik/tools/traefik.tools.json | 24 ++ .../traefik/tools}/traefik_reset_password.sh | 0 docs/DEVELOPMENT.md | 20 +- scripts/app/auth_adapter.sh | 2 +- .../docker/app/functions/function_app_tool.sh | 6 +- scripts/source/files/arrays/files_app.sh | 61 --- .../mattermost/mattermost_auth.sh | 0 .../mattermost/mattermost_create_account.sh | 0 .../mattermost/mattermost_delete_user.sh | 0 .../mattermost/mattermost_list_users.sh | 0 .../mattermost/mattermost_reset_password.sh | 0 .../mattermost/mattermost_set_admin.sh | 0 .../webui/data/generators/apps/webui_tools.sh | 363 +----------------- 75 files changed, 642 insertions(+), 422 deletions(-) rename {scripts/app/containers/adguard => containers/adguard/scripts}/adguard_auth.sh (100%) rename {scripts/app/containers/bookstack => containers/bookstack/scripts}/bookstack_auth.sh (100%) create mode 100644 containers/bookstack/tools/bookstack.tools.json rename {scripts/app/containers/bookstack => containers/bookstack/tools}/bookstack_create_account.sh (100%) rename {scripts/app/containers/bookstack => containers/bookstack/tools}/bookstack_delete_user.sh (100%) rename {scripts/app/containers/bookstack => containers/bookstack/tools}/bookstack_list_users.sh (100%) rename {scripts/app/containers/bookstack => containers/bookstack/tools}/bookstack_reset_password.sh (100%) rename {scripts/app/containers/bookstack => containers/bookstack/tools}/bookstack_set_admin.sh (100%) rename {scripts/app/containers/crowdsec => containers/crowdsec/scripts}/crowdsec_alerts_list.sh (100%) rename {scripts/app/containers/crowdsec => containers/crowdsec/scripts}/crowdsec_console_disable.sh (100%) rename {scripts/app/containers/crowdsec => containers/crowdsec/scripts}/crowdsec_console_enroll.sh (100%) rename {scripts/app/containers/crowdsec => containers/crowdsec/scripts}/crowdsec_decisions_list.sh (100%) rename {scripts/app/containers/crowdsec => containers/crowdsec/scripts}/crowdsec_fix_priority.sh (100%) rename {scripts/app/containers/crowdsec => containers/crowdsec/scripts}/crowdsec_hub_update.sh (100%) rename {scripts/app/containers/crowdsec => containers/crowdsec/scripts}/crowdsec_metrics.sh (100%) rename {scripts/app/containers/crowdsec => containers/crowdsec/scripts}/crowdsec_status.sh (100%) rename {scripts/app/containers/crowdsec => containers/crowdsec/scripts}/crowdsec_unban.sh (100%) rename {scripts/app/containers/crowdsec => containers/crowdsec/scripts}/crowdsec_update.sh (100%) rename {scripts/app/containers/crowdsec => containers/crowdsec/scripts}/crowdsec_verify_firewall.sh (100%) rename {scripts/app/containers/dashy => containers/dashy/scripts}/dashy_update_conf.sh (100%) create mode 100644 containers/dashy/tools/dashy.tools.json rename {scripts/app/containers/dashy => containers/dashy/tools}/dashy_manage_shortcuts.sh (100%) rename {scripts/app/containers/focalboard => containers/focalboard/scripts}/focalboard_auth.sh (100%) rename {scripts/app/containers/focalboard => containers/focalboard/scripts}/focalboard_set_admin.sh (100%) create mode 100644 containers/focalboard/tools/focalboard.tools.json rename {scripts/app/containers/focalboard => containers/focalboard/tools}/focalboard_create_account.sh (100%) rename {scripts/app/containers/focalboard => containers/focalboard/tools}/focalboard_delete_user.sh (100%) rename {scripts/app/containers/focalboard => containers/focalboard/tools}/focalboard_list_users.sh (100%) rename {scripts/app/containers/focalboard => containers/focalboard/tools}/focalboard_reset_password.sh (100%) rename {scripts/app/containers/gitea => containers/gitea/scripts}/gitea_auth.sh (100%) create mode 100644 containers/gitea/tools/gitea.tools.json rename {scripts/app/containers/gitea => containers/gitea/tools}/gitea_create_account.sh (100%) rename {scripts/app/containers/gitea => containers/gitea/tools}/gitea_delete_user.sh (100%) rename {scripts/app/containers/gitea => containers/gitea/tools}/gitea_list_users.sh (100%) rename {scripts/app/containers/gitea => containers/gitea/tools}/gitea_reset_password.sh (100%) rename {scripts/app/containers/gitea => containers/gitea/tools}/gitea_set_admin.sh (100%) rename {scripts/app/containers/gluetun => containers/gluetun/scripts}/gluetun_recreate_routed.sh (100%) create mode 100644 containers/gluetun/tools/gluetun.tools.json rename {scripts/app/containers/gluetun => containers/gluetun/tools}/gluetun_refresh_providers.sh (100%) rename {scripts/app/containers/invidious => containers/invidious/scripts}/invidious_auth.sh (100%) rename {scripts/app/containers/invidious => containers/invidious/scripts}/invidious_set_admin.sh (100%) create mode 100644 containers/invidious/tools/invidious.tools.json rename {scripts/app/containers/invidious => containers/invidious/tools}/invidious_create_account.sh (100%) rename {scripts/app/containers/invidious => containers/invidious/tools}/invidious_delete_user.sh (100%) rename {scripts/app/containers/invidious => containers/invidious/tools}/invidious_list_users.sh (100%) rename {scripts/app/containers/invidious => containers/invidious/tools}/invidious_reset_password.sh (100%) rename {scripts/app/containers/nextcloud => containers/nextcloud/scripts}/nextcloud_auth.sh (100%) create mode 100644 containers/nextcloud/tools/nextcloud.tools.json rename {scripts/app/containers/nextcloud => containers/nextcloud/tools}/nextcloud_add_trusted_domain.sh (100%) rename {scripts/app/containers/nextcloud => containers/nextcloud/tools}/nextcloud_create_account.sh (100%) rename {scripts/app/containers/nextcloud => containers/nextcloud/tools}/nextcloud_delete_user.sh (100%) rename {scripts/app/containers/nextcloud => containers/nextcloud/tools}/nextcloud_list_users.sh (100%) rename {scripts/app/containers/nextcloud => containers/nextcloud/tools}/nextcloud_rescan_files.sh (100%) rename {scripts/app/containers/nextcloud => containers/nextcloud/tools}/nextcloud_reset_password.sh (100%) rename {scripts/app/containers/nextcloud => containers/nextcloud/tools}/nextcloud_set_admin.sh (100%) rename {scripts/app/containers/nextcloud => containers/nextcloud/tools}/nextcloud_system_status.sh (100%) rename {scripts/app/containers/nextcloud => containers/nextcloud/tools}/nextcloud_tail_logs.sh (100%) rename {scripts/app/containers/nextcloud => containers/nextcloud/tools}/nextcloud_toggle_maintenance.sh (100%) rename {scripts/app/containers/owncloud => containers/owncloud/scripts}/owncloud_setup_config.sh (100%) create mode 100644 containers/pihole/tools/pihole.tools.json rename {scripts/app/containers/pihole => containers/pihole/tools}/pihole_apply_dns_updater.sh (100%) rename {scripts/app/containers/traefik => containers/traefik/scripts}/traefik_auth.sh (100%) create mode 100644 containers/traefik/tools/traefik.tools.json rename {scripts/app/containers/traefik => containers/traefik/tools}/traefik_reset_password.sh (100%) rename scripts/{app/containers => unused/OLD_CONTAINERS}/mattermost/mattermost_auth.sh (100%) rename scripts/{app/containers => unused/OLD_CONTAINERS}/mattermost/mattermost_create_account.sh (100%) rename scripts/{app/containers => unused/OLD_CONTAINERS}/mattermost/mattermost_delete_user.sh (100%) rename scripts/{app/containers => unused/OLD_CONTAINERS}/mattermost/mattermost_list_users.sh (100%) rename scripts/{app/containers => unused/OLD_CONTAINERS}/mattermost/mattermost_reset_password.sh (100%) rename scripts/{app/containers => unused/OLD_CONTAINERS}/mattermost/mattermost_set_admin.sh (100%) diff --git a/scripts/app/containers/adguard/adguard_auth.sh b/containers/adguard/scripts/adguard_auth.sh similarity index 100% rename from scripts/app/containers/adguard/adguard_auth.sh rename to containers/adguard/scripts/adguard_auth.sh diff --git a/scripts/app/containers/bookstack/bookstack_auth.sh b/containers/bookstack/scripts/bookstack_auth.sh similarity index 100% rename from scripts/app/containers/bookstack/bookstack_auth.sh rename to containers/bookstack/scripts/bookstack_auth.sh diff --git a/containers/bookstack/tools/bookstack.tools.json b/containers/bookstack/tools/bookstack.tools.json new file mode 100644 index 0000000..de6e014 --- /dev/null +++ b/containers/bookstack/tools/bookstack.tools.json @@ -0,0 +1,106 @@ +{ + "tools": [ + { + "id": "reset_password", + "category": "users", + "label": "Reset User Password", + "description": "Reset an existing Bookstack user's password. Leave the password field blank to generate a random one — it is shown in the task log.", + "icon": "🔑", + "fields": [ + { + "name": "email", + "label": "User email", + "type": "text", + "placeholder": "user@example.com", + "required": true + }, + { + "name": "password", + "label": "New password (leave blank to generate)", + "type": "password", + "placeholder": "Leave blank for random" + } + ] + }, + { + "id": "create_account", + "category": "users", + "label": "Create User Account", + "description": "Create a new Bookstack user. Tick \"Make admin\" to grant full admin rights; otherwise the new user gets the default registration role. Leave the password blank to generate a random one.", + "icon": "👤", + "fields": [ + { + "name": "email", + "label": "Email", + "type": "text", + "placeholder": "user@example.com", + "required": true + }, + { + "name": "name", + "label": "Display name", + "type": "text", + "required": true + }, + { + "name": "password", + "label": "Password (leave blank to generate)", + "type": "password", + "placeholder": "Leave blank for random" + }, + { + "name": "admin", + "label": "Make admin", + "type": "checkbox", + "default": false + } + ] + }, + { + "id": "list_users", + "category": "users", + "label": "List Users", + "description": "Show every Bookstack user with their roles.", + "icon": "📋", + "fields": [] + }, + { + "id": "delete_user", + "category": "users", + "label": "Delete User Account", + "description": "Permanently delete a user account.", + "icon": "🗑", + "destructive": true, + "confirm": "This cannot be undone.", + "fields": [ + { + "name": "email", + "label": "User email", + "type": "text", + "required": true + } + ] + }, + { + "id": "set_admin", + "category": "users", + "label": "Set Admin Status", + "description": "Promote a user to admin or demote them to a normal user.", + "icon": "👑", + "fields": [ + { + "name": "email", + "label": "User email", + "type": "text", + "required": true + }, + { + "name": "admin", + "label": "Make admin", + "type": "checkbox", + "default": false + } + ] + } + ] +} diff --git a/scripts/app/containers/bookstack/bookstack_create_account.sh b/containers/bookstack/tools/bookstack_create_account.sh similarity index 100% rename from scripts/app/containers/bookstack/bookstack_create_account.sh rename to containers/bookstack/tools/bookstack_create_account.sh diff --git a/scripts/app/containers/bookstack/bookstack_delete_user.sh b/containers/bookstack/tools/bookstack_delete_user.sh similarity index 100% rename from scripts/app/containers/bookstack/bookstack_delete_user.sh rename to containers/bookstack/tools/bookstack_delete_user.sh diff --git a/scripts/app/containers/bookstack/bookstack_list_users.sh b/containers/bookstack/tools/bookstack_list_users.sh similarity index 100% rename from scripts/app/containers/bookstack/bookstack_list_users.sh rename to containers/bookstack/tools/bookstack_list_users.sh diff --git a/scripts/app/containers/bookstack/bookstack_reset_password.sh b/containers/bookstack/tools/bookstack_reset_password.sh similarity index 100% rename from scripts/app/containers/bookstack/bookstack_reset_password.sh rename to containers/bookstack/tools/bookstack_reset_password.sh diff --git a/scripts/app/containers/bookstack/bookstack_set_admin.sh b/containers/bookstack/tools/bookstack_set_admin.sh similarity index 100% rename from scripts/app/containers/bookstack/bookstack_set_admin.sh rename to containers/bookstack/tools/bookstack_set_admin.sh diff --git a/scripts/app/containers/crowdsec/crowdsec_alerts_list.sh b/containers/crowdsec/scripts/crowdsec_alerts_list.sh similarity index 100% rename from scripts/app/containers/crowdsec/crowdsec_alerts_list.sh rename to containers/crowdsec/scripts/crowdsec_alerts_list.sh diff --git a/scripts/app/containers/crowdsec/crowdsec_console_disable.sh b/containers/crowdsec/scripts/crowdsec_console_disable.sh similarity index 100% rename from scripts/app/containers/crowdsec/crowdsec_console_disable.sh rename to containers/crowdsec/scripts/crowdsec_console_disable.sh diff --git a/scripts/app/containers/crowdsec/crowdsec_console_enroll.sh b/containers/crowdsec/scripts/crowdsec_console_enroll.sh similarity index 100% rename from scripts/app/containers/crowdsec/crowdsec_console_enroll.sh rename to containers/crowdsec/scripts/crowdsec_console_enroll.sh diff --git a/scripts/app/containers/crowdsec/crowdsec_decisions_list.sh b/containers/crowdsec/scripts/crowdsec_decisions_list.sh similarity index 100% rename from scripts/app/containers/crowdsec/crowdsec_decisions_list.sh rename to containers/crowdsec/scripts/crowdsec_decisions_list.sh diff --git a/scripts/app/containers/crowdsec/crowdsec_fix_priority.sh b/containers/crowdsec/scripts/crowdsec_fix_priority.sh similarity index 100% rename from scripts/app/containers/crowdsec/crowdsec_fix_priority.sh rename to containers/crowdsec/scripts/crowdsec_fix_priority.sh diff --git a/scripts/app/containers/crowdsec/crowdsec_hub_update.sh b/containers/crowdsec/scripts/crowdsec_hub_update.sh similarity index 100% rename from scripts/app/containers/crowdsec/crowdsec_hub_update.sh rename to containers/crowdsec/scripts/crowdsec_hub_update.sh diff --git a/scripts/app/containers/crowdsec/crowdsec_metrics.sh b/containers/crowdsec/scripts/crowdsec_metrics.sh similarity index 100% rename from scripts/app/containers/crowdsec/crowdsec_metrics.sh rename to containers/crowdsec/scripts/crowdsec_metrics.sh diff --git a/scripts/app/containers/crowdsec/crowdsec_status.sh b/containers/crowdsec/scripts/crowdsec_status.sh similarity index 100% rename from scripts/app/containers/crowdsec/crowdsec_status.sh rename to containers/crowdsec/scripts/crowdsec_status.sh diff --git a/scripts/app/containers/crowdsec/crowdsec_unban.sh b/containers/crowdsec/scripts/crowdsec_unban.sh similarity index 100% rename from scripts/app/containers/crowdsec/crowdsec_unban.sh rename to containers/crowdsec/scripts/crowdsec_unban.sh diff --git a/scripts/app/containers/crowdsec/crowdsec_update.sh b/containers/crowdsec/scripts/crowdsec_update.sh similarity index 100% rename from scripts/app/containers/crowdsec/crowdsec_update.sh rename to containers/crowdsec/scripts/crowdsec_update.sh diff --git a/scripts/app/containers/crowdsec/crowdsec_verify_firewall.sh b/containers/crowdsec/scripts/crowdsec_verify_firewall.sh similarity index 100% rename from scripts/app/containers/crowdsec/crowdsec_verify_firewall.sh rename to containers/crowdsec/scripts/crowdsec_verify_firewall.sh diff --git a/scripts/app/containers/dashy/dashy_update_conf.sh b/containers/dashy/scripts/dashy_update_conf.sh similarity index 100% rename from scripts/app/containers/dashy/dashy_update_conf.sh rename to containers/dashy/scripts/dashy_update_conf.sh diff --git a/containers/dashy/tools/dashy.tools.json b/containers/dashy/tools/dashy.tools.json new file mode 100644 index 0000000..43bb349 --- /dev/null +++ b/containers/dashy/tools/dashy.tools.json @@ -0,0 +1,21 @@ +{ + "tools": [ + { + "id": "manage_shortcuts", + "label": "Manage Shortcuts", + "description": "Pick which service URLs appear as shortcuts on the Dashy dashboard.", + "icon": "🧩", + "fields": [ + { + "name": "selected", + "label": "URLs to show on the dashboard", + "type": "app_urls_multi", + "prefillFromCfgKey": "CFG_DASHY_SHORTCUTS", + "excludeApps": [ + "dashy" + ] + } + ] + } + ] +} diff --git a/scripts/app/containers/dashy/dashy_manage_shortcuts.sh b/containers/dashy/tools/dashy_manage_shortcuts.sh similarity index 100% rename from scripts/app/containers/dashy/dashy_manage_shortcuts.sh rename to containers/dashy/tools/dashy_manage_shortcuts.sh diff --git a/scripts/app/containers/focalboard/focalboard_auth.sh b/containers/focalboard/scripts/focalboard_auth.sh similarity index 100% rename from scripts/app/containers/focalboard/focalboard_auth.sh rename to containers/focalboard/scripts/focalboard_auth.sh diff --git a/scripts/app/containers/focalboard/focalboard_set_admin.sh b/containers/focalboard/scripts/focalboard_set_admin.sh similarity index 100% rename from scripts/app/containers/focalboard/focalboard_set_admin.sh rename to containers/focalboard/scripts/focalboard_set_admin.sh diff --git a/containers/focalboard/tools/focalboard.tools.json b/containers/focalboard/tools/focalboard.tools.json new file mode 100644 index 0000000..b29d975 --- /dev/null +++ b/containers/focalboard/tools/focalboard.tools.json @@ -0,0 +1,82 @@ +{ + "tools": [ + { + "id": "reset_password", + "category": "users", + "label": "Reset User Password", + "description": "Reset an existing Focalboard user's password.", + "icon": "🔑", + "fields": [ + { + "name": "username", + "label": "Username", + "type": "text", + "required": true + }, + { + "name": "password", + "label": "New password (leave blank to generate)", + "type": "password", + "placeholder": "Leave blank for random" + } + ] + }, + { + "id": "create_account", + "category": "users", + "label": "Create User Account", + "description": "Create a new Focalboard user. Leave password blank to generate one.", + "icon": "👤", + "fields": [ + { + "name": "username", + "label": "Username", + "type": "text", + "required": true + }, + { + "name": "email", + "label": "Email", + "type": "text", + "required": true + }, + { + "name": "password", + "label": "Password (leave blank to generate)", + "type": "password" + }, + { + "name": "admin", + "label": "Make admin", + "type": "checkbox", + "default": false + } + ] + }, + { + "id": "list_users", + "category": "users", + "label": "List Users", + "description": "Show every Focalboard user account.", + "icon": "📋", + "fields": [] + }, + { + "id": "delete_user", + "category": "users", + "label": "Delete User Account", + "description": "Permanently delete a user account.", + "icon": "🗑", + "destructive": true, + "confirm": "This cannot be undone.", + "fields": [ + { + "name": "username", + "label": "Username", + "type": "text", + "required": true + } + ] + } + ] +} diff --git a/scripts/app/containers/focalboard/focalboard_create_account.sh b/containers/focalboard/tools/focalboard_create_account.sh similarity index 100% rename from scripts/app/containers/focalboard/focalboard_create_account.sh rename to containers/focalboard/tools/focalboard_create_account.sh diff --git a/scripts/app/containers/focalboard/focalboard_delete_user.sh b/containers/focalboard/tools/focalboard_delete_user.sh similarity index 100% rename from scripts/app/containers/focalboard/focalboard_delete_user.sh rename to containers/focalboard/tools/focalboard_delete_user.sh diff --git a/scripts/app/containers/focalboard/focalboard_list_users.sh b/containers/focalboard/tools/focalboard_list_users.sh similarity index 100% rename from scripts/app/containers/focalboard/focalboard_list_users.sh rename to containers/focalboard/tools/focalboard_list_users.sh diff --git a/scripts/app/containers/focalboard/focalboard_reset_password.sh b/containers/focalboard/tools/focalboard_reset_password.sh similarity index 100% rename from scripts/app/containers/focalboard/focalboard_reset_password.sh rename to containers/focalboard/tools/focalboard_reset_password.sh diff --git a/scripts/app/containers/gitea/gitea_auth.sh b/containers/gitea/scripts/gitea_auth.sh similarity index 100% rename from scripts/app/containers/gitea/gitea_auth.sh rename to containers/gitea/scripts/gitea_auth.sh diff --git a/containers/gitea/tools/gitea.tools.json b/containers/gitea/tools/gitea.tools.json new file mode 100644 index 0000000..dd2b217 --- /dev/null +++ b/containers/gitea/tools/gitea.tools.json @@ -0,0 +1,105 @@ +{ + "tools": [ + { + "id": "reset_password", + "category": "users", + "label": "Reset User Password", + "description": "Reset a Gitea user's password using the built-in `gitea admin user change-password` CLI. Leave the password blank to generate a random one.", + "icon": "🔑", + "fields": [ + { + "name": "username", + "label": "Username", + "type": "text", + "required": true + }, + { + "name": "password", + "label": "New password (leave blank to generate)", + "type": "password", + "placeholder": "Leave blank for random" + } + ] + }, + { + "id": "create_account", + "category": "users", + "label": "Create User Account", + "description": "Create a new Gitea user via the built-in `gitea admin user create` CLI. Tick \"Make admin\" for full admin rights. Leave the password blank to generate a random one. The account starts ready to log in (no forced password change).", + "icon": "👤", + "fields": [ + { + "name": "username", + "label": "Username", + "type": "text", + "required": true + }, + { + "name": "email", + "label": "Email", + "type": "text", + "placeholder": "user@example.com", + "required": true + }, + { + "name": "password", + "label": "Password (leave blank to generate)", + "type": "password", + "placeholder": "Leave blank for random" + }, + { + "name": "admin", + "label": "Make admin", + "type": "checkbox", + "default": false + } + ] + }, + { + "id": "list_users", + "category": "users", + "label": "List Users", + "description": "Show every Gitea user account.", + "icon": "📋", + "fields": [] + }, + { + "id": "delete_user", + "category": "users", + "label": "Delete User Account", + "description": "Permanently delete a user account.", + "icon": "🗑", + "destructive": true, + "confirm": "This cannot be undone.", + "fields": [ + { + "name": "username", + "label": "Username", + "type": "text", + "required": true + } + ] + }, + { + "id": "set_admin", + "category": "users", + "label": "Set Admin Status", + "description": "Promote a user to admin or demote them to a normal user.", + "icon": "👑", + "fields": [ + { + "name": "username", + "label": "Username", + "type": "text", + "required": true + }, + { + "name": "admin", + "label": "Make admin", + "type": "checkbox", + "default": false + } + ] + } + ] +} diff --git a/scripts/app/containers/gitea/gitea_create_account.sh b/containers/gitea/tools/gitea_create_account.sh similarity index 100% rename from scripts/app/containers/gitea/gitea_create_account.sh rename to containers/gitea/tools/gitea_create_account.sh diff --git a/scripts/app/containers/gitea/gitea_delete_user.sh b/containers/gitea/tools/gitea_delete_user.sh similarity index 100% rename from scripts/app/containers/gitea/gitea_delete_user.sh rename to containers/gitea/tools/gitea_delete_user.sh diff --git a/scripts/app/containers/gitea/gitea_list_users.sh b/containers/gitea/tools/gitea_list_users.sh similarity index 100% rename from scripts/app/containers/gitea/gitea_list_users.sh rename to containers/gitea/tools/gitea_list_users.sh diff --git a/scripts/app/containers/gitea/gitea_reset_password.sh b/containers/gitea/tools/gitea_reset_password.sh similarity index 100% rename from scripts/app/containers/gitea/gitea_reset_password.sh rename to containers/gitea/tools/gitea_reset_password.sh diff --git a/scripts/app/containers/gitea/gitea_set_admin.sh b/containers/gitea/tools/gitea_set_admin.sh similarity index 100% rename from scripts/app/containers/gitea/gitea_set_admin.sh rename to containers/gitea/tools/gitea_set_admin.sh diff --git a/scripts/app/containers/gluetun/gluetun_recreate_routed.sh b/containers/gluetun/scripts/gluetun_recreate_routed.sh similarity index 100% rename from scripts/app/containers/gluetun/gluetun_recreate_routed.sh rename to containers/gluetun/scripts/gluetun_recreate_routed.sh diff --git a/containers/gluetun/tools/gluetun.tools.json b/containers/gluetun/tools/gluetun.tools.json new file mode 100644 index 0000000..1cec705 --- /dev/null +++ b/containers/gluetun/tools/gluetun.tools.json @@ -0,0 +1,11 @@ +{ + "tools": [ + { + "id": "refresh_providers", + "label": "Refresh VPN Providers", + "description": "Re-scan VPN providers and country lists, regenerating the snapshot used by the country picker.", + "icon": "🔄", + "fields": [] + } + ] +} diff --git a/scripts/app/containers/gluetun/gluetun_refresh_providers.sh b/containers/gluetun/tools/gluetun_refresh_providers.sh similarity index 100% rename from scripts/app/containers/gluetun/gluetun_refresh_providers.sh rename to containers/gluetun/tools/gluetun_refresh_providers.sh diff --git a/scripts/app/containers/invidious/invidious_auth.sh b/containers/invidious/scripts/invidious_auth.sh similarity index 100% rename from scripts/app/containers/invidious/invidious_auth.sh rename to containers/invidious/scripts/invidious_auth.sh diff --git a/scripts/app/containers/invidious/invidious_set_admin.sh b/containers/invidious/scripts/invidious_set_admin.sh similarity index 100% rename from scripts/app/containers/invidious/invidious_set_admin.sh rename to containers/invidious/scripts/invidious_set_admin.sh diff --git a/containers/invidious/tools/invidious.tools.json b/containers/invidious/tools/invidious.tools.json new file mode 100644 index 0000000..54bb0dd --- /dev/null +++ b/containers/invidious/tools/invidious.tools.json @@ -0,0 +1,75 @@ +{ + "tools": [ + { + "id": "reset_password", + "category": "users", + "label": "Reset User Password", + "description": "Reset an Invidious user's password (Postgres bcrypt update).", + "icon": "🔑", + "fields": [ + { + "name": "email", + "label": "Email", + "type": "text", + "required": true + }, + { + "name": "password", + "label": "New password (leave blank to generate)", + "type": "password" + } + ] + }, + { + "id": "create_account", + "category": "users", + "label": "Create User Account", + "description": "Create a new Invidious user (Invidious uses email as username).", + "icon": "👤", + "fields": [ + { + "name": "email", + "label": "Email", + "type": "text", + "required": true + }, + { + "name": "password", + "label": "Password (leave blank to generate)", + "type": "password" + }, + { + "name": "admin", + "label": "Make admin", + "type": "checkbox", + "default": false + } + ] + }, + { + "id": "list_users", + "category": "users", + "label": "List Users", + "description": "Show every Invidious user account.", + "icon": "📋", + "fields": [] + }, + { + "id": "delete_user", + "category": "users", + "label": "Delete User Account", + "description": "Permanently delete a user account.", + "icon": "🗑", + "destructive": true, + "confirm": "This cannot be undone.", + "fields": [ + { + "name": "email", + "label": "Email", + "type": "text", + "required": true + } + ] + } + ] +} diff --git a/scripts/app/containers/invidious/invidious_create_account.sh b/containers/invidious/tools/invidious_create_account.sh similarity index 100% rename from scripts/app/containers/invidious/invidious_create_account.sh rename to containers/invidious/tools/invidious_create_account.sh diff --git a/scripts/app/containers/invidious/invidious_delete_user.sh b/containers/invidious/tools/invidious_delete_user.sh similarity index 100% rename from scripts/app/containers/invidious/invidious_delete_user.sh rename to containers/invidious/tools/invidious_delete_user.sh diff --git a/scripts/app/containers/invidious/invidious_list_users.sh b/containers/invidious/tools/invidious_list_users.sh similarity index 100% rename from scripts/app/containers/invidious/invidious_list_users.sh rename to containers/invidious/tools/invidious_list_users.sh diff --git a/scripts/app/containers/invidious/invidious_reset_password.sh b/containers/invidious/tools/invidious_reset_password.sh similarity index 100% rename from scripts/app/containers/invidious/invidious_reset_password.sh rename to containers/invidious/tools/invidious_reset_password.sh diff --git a/scripts/app/containers/nextcloud/nextcloud_auth.sh b/containers/nextcloud/scripts/nextcloud_auth.sh similarity index 100% rename from scripts/app/containers/nextcloud/nextcloud_auth.sh rename to containers/nextcloud/scripts/nextcloud_auth.sh diff --git a/containers/nextcloud/tools/nextcloud.tools.json b/containers/nextcloud/tools/nextcloud.tools.json new file mode 100644 index 0000000..1a81922 --- /dev/null +++ b/containers/nextcloud/tools/nextcloud.tools.json @@ -0,0 +1,177 @@ +{ + "tools": [ + { + "id": "reset_password", + "category": "users", + "label": "Reset User Password", + "description": "Reset an existing Nextcloud user's password. Leave the password field blank to generate a random one — it is shown in the task log.", + "icon": "🔑", + "fields": [ + { + "name": "username", + "label": "Username", + "type": "text", + "placeholder": "alice", + "required": true + }, + { + "name": "password", + "label": "New password (leave blank to generate)", + "type": "password", + "placeholder": "Leave blank for random" + } + ] + }, + { + "id": "create_account", + "category": "users", + "label": "Create User Account", + "description": "Create a new Nextcloud user. Tick \"Make admin\" to add them to the admin group. Leave the password blank to generate a random one.", + "icon": "👤", + "fields": [ + { + "name": "username", + "label": "Username", + "type": "text", + "placeholder": "alice", + "required": true + }, + { + "name": "display_name", + "label": "Display name", + "type": "text", + "placeholder": "Alice Smith" + }, + { + "name": "password", + "label": "Password (leave blank to generate)", + "type": "password", + "placeholder": "Leave blank for random" + }, + { + "name": "admin", + "label": "Make admin", + "type": "checkbox", + "default": false + } + ] + }, + { + "id": "list_users", + "category": "users", + "label": "List Users", + "description": "Show every Nextcloud user with their display name and admin flag.", + "icon": "📋", + "fields": [] + }, + { + "id": "delete_user", + "category": "users", + "label": "Delete User Account", + "description": "Permanently delete a user and all their files.", + "icon": "🗑", + "destructive": true, + "confirm": "This cannot be undone. The user's files will be removed.", + "fields": [ + { + "name": "username", + "label": "Username", + "type": "text", + "required": true + } + ] + }, + { + "id": "set_admin", + "category": "users", + "label": "Set Admin Status", + "description": "Add a user to (or remove from) the admin group.", + "icon": "👑", + "fields": [ + { + "name": "username", + "label": "Username", + "type": "text", + "required": true + }, + { + "name": "admin", + "label": "Make admin", + "type": "checkbox", + "default": false + } + ] + }, + { + "id": "toggle_maintenance", + "category": "maintenance", + "label": "Toggle Maintenance Mode", + "description": "Lock all users out and show a maintenance notice — required before running upgrades or repairs from the CLI.", + "icon": "🚧", + "fields": [ + { + "name": "enable", + "label": "Enable maintenance mode", + "type": "checkbox", + "default": true + } + ] + }, + { + "id": "rescan_files", + "category": "maintenance", + "label": "Rescan Files", + "description": "Re-index Nextcloud's file metadata. Run this after files were added or removed on disk outside Nextcloud (rsync, restore, manual copy). Leave the username blank to scan every user.", + "icon": "🔄", + "fields": [ + { + "name": "username", + "label": "Username (blank = all users)", + "type": "text", + "placeholder": "blank for all" + } + ] + }, + { + "id": "add_trusted_domain", + "category": "system", + "label": "Add Trusted Domain", + "description": "Append a hostname to Nextcloud's trusted_domains list so requests to that host are accepted.", + "icon": "🌐", + "fields": [ + { + "name": "domain", + "label": "Domain", + "type": "text", + "placeholder": "cloud.example.org", + "required": true + } + ] + }, + { + "id": "system_status", + "category": "system", + "label": "System Status", + "description": "Show Nextcloud's version, install state, and maintenance flag.", + "icon": "â„šī¸", + "fields": [] + }, + { + "id": "tail_logs", + "category": "system", + "label": "Tail Logs", + "description": "Show the most recent lines of nextcloud.log.", + "icon": "📜", + "fields": [ + { + "name": "lines", + "label": "Lines", + "type": "number", + "default": 100, + "min": 10, + "max": 1000 + } + ] + } + ] +} diff --git a/scripts/app/containers/nextcloud/nextcloud_add_trusted_domain.sh b/containers/nextcloud/tools/nextcloud_add_trusted_domain.sh similarity index 100% rename from scripts/app/containers/nextcloud/nextcloud_add_trusted_domain.sh rename to containers/nextcloud/tools/nextcloud_add_trusted_domain.sh diff --git a/scripts/app/containers/nextcloud/nextcloud_create_account.sh b/containers/nextcloud/tools/nextcloud_create_account.sh similarity index 100% rename from scripts/app/containers/nextcloud/nextcloud_create_account.sh rename to containers/nextcloud/tools/nextcloud_create_account.sh diff --git a/scripts/app/containers/nextcloud/nextcloud_delete_user.sh b/containers/nextcloud/tools/nextcloud_delete_user.sh similarity index 100% rename from scripts/app/containers/nextcloud/nextcloud_delete_user.sh rename to containers/nextcloud/tools/nextcloud_delete_user.sh diff --git a/scripts/app/containers/nextcloud/nextcloud_list_users.sh b/containers/nextcloud/tools/nextcloud_list_users.sh similarity index 100% rename from scripts/app/containers/nextcloud/nextcloud_list_users.sh rename to containers/nextcloud/tools/nextcloud_list_users.sh diff --git a/scripts/app/containers/nextcloud/nextcloud_rescan_files.sh b/containers/nextcloud/tools/nextcloud_rescan_files.sh similarity index 100% rename from scripts/app/containers/nextcloud/nextcloud_rescan_files.sh rename to containers/nextcloud/tools/nextcloud_rescan_files.sh diff --git a/scripts/app/containers/nextcloud/nextcloud_reset_password.sh b/containers/nextcloud/tools/nextcloud_reset_password.sh similarity index 100% rename from scripts/app/containers/nextcloud/nextcloud_reset_password.sh rename to containers/nextcloud/tools/nextcloud_reset_password.sh diff --git a/scripts/app/containers/nextcloud/nextcloud_set_admin.sh b/containers/nextcloud/tools/nextcloud_set_admin.sh similarity index 100% rename from scripts/app/containers/nextcloud/nextcloud_set_admin.sh rename to containers/nextcloud/tools/nextcloud_set_admin.sh diff --git a/scripts/app/containers/nextcloud/nextcloud_system_status.sh b/containers/nextcloud/tools/nextcloud_system_status.sh similarity index 100% rename from scripts/app/containers/nextcloud/nextcloud_system_status.sh rename to containers/nextcloud/tools/nextcloud_system_status.sh diff --git a/scripts/app/containers/nextcloud/nextcloud_tail_logs.sh b/containers/nextcloud/tools/nextcloud_tail_logs.sh similarity index 100% rename from scripts/app/containers/nextcloud/nextcloud_tail_logs.sh rename to containers/nextcloud/tools/nextcloud_tail_logs.sh diff --git a/scripts/app/containers/nextcloud/nextcloud_toggle_maintenance.sh b/containers/nextcloud/tools/nextcloud_toggle_maintenance.sh similarity index 100% rename from scripts/app/containers/nextcloud/nextcloud_toggle_maintenance.sh rename to containers/nextcloud/tools/nextcloud_toggle_maintenance.sh diff --git a/scripts/app/containers/owncloud/owncloud_setup_config.sh b/containers/owncloud/scripts/owncloud_setup_config.sh similarity index 100% rename from scripts/app/containers/owncloud/owncloud_setup_config.sh rename to containers/owncloud/scripts/owncloud_setup_config.sh diff --git a/containers/pihole/tools/pihole.tools.json b/containers/pihole/tools/pihole.tools.json new file mode 100644 index 0000000..d23bdb3 --- /dev/null +++ b/containers/pihole/tools/pihole.tools.json @@ -0,0 +1,11 @@ +{ + "tools": [ + { + "id": "apply_dns_updater", + "label": "Apply DNS Updater", + "description": "Rewrite this server's /etc/resolv.conf to use Pi-hole as its DNS resolver right now. Same action that runs automatically when the global DNS Updater requirement is enabled.", + "icon": "🌐", + "fields": [] + } + ] +} diff --git a/scripts/app/containers/pihole/pihole_apply_dns_updater.sh b/containers/pihole/tools/pihole_apply_dns_updater.sh similarity index 100% rename from scripts/app/containers/pihole/pihole_apply_dns_updater.sh rename to containers/pihole/tools/pihole_apply_dns_updater.sh diff --git a/scripts/app/containers/traefik/traefik_auth.sh b/containers/traefik/scripts/traefik_auth.sh similarity index 100% rename from scripts/app/containers/traefik/traefik_auth.sh rename to containers/traefik/scripts/traefik_auth.sh diff --git a/containers/traefik/tools/traefik.tools.json b/containers/traefik/tools/traefik.tools.json new file mode 100644 index 0000000..bf010b5 --- /dev/null +++ b/containers/traefik/tools/traefik.tools.json @@ -0,0 +1,24 @@ +{ + "tools": [ + { + "id": "reset_password", + "label": "Reset Dashboard Login", + "description": "Reset the Traefik dashboard credentials (CFG_TRAEFIK_USER / CFG_TRAEFIK_PASS) and regenerate the htpasswd entry in protectionauth.yml. Leave fields blank to keep the current username and/or generate a new random password.", + "icon": "🔑", + "fields": [ + { + "name": "username", + "label": "Username (leave blank to keep current)", + "type": "text", + "placeholder": "admin" + }, + { + "name": "password", + "label": "New password (leave blank to generate)", + "type": "password", + "placeholder": "Leave blank for random" + } + ] + } + ] +} diff --git a/scripts/app/containers/traefik/traefik_reset_password.sh b/containers/traefik/tools/traefik_reset_password.sh similarity index 100% rename from scripts/app/containers/traefik/traefik_reset_password.sh rename to containers/traefik/tools/traefik_reset_password.sh diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index e67ccec..97c527d 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -170,15 +170,17 @@ is a dev-only escape hatch. ## Conventions - **Versioning:** semver in `VERSION`. Bump before building; `latest.json` carries it. -- **App tools are self-contained** (preferred): put the declaration in - `containers//tools/.tools.json` and each function in - `containers//tools/_.sh` (function `app`). - Both are picked up automatically — the container scan live-sources the `.sh`, and - `webui_tools.sh` auto-merges the `.tools.json`. No central edits, no array regen → - the app is a true drop-in. (The legacy central style — an entry in the - `webui_tools.sh` heredoc + the function under `scripts/app/containers//` — - still works; both coexist, so apps migrate one at a time. Only *declared tools* - move; shared logic helpers like `_auth.sh` stay in `scripts/app/`.) +- **An app is a self-contained drop-in.** Everything for an app lives under + `containers//`: + - **Tools tab actions:** declare them in `containers//tools/.tools.json` + (`{ "tools": [ â€Ļ ] }`) and put each function beside it at + `containers//tools/_.sh` (function `app`). + - **Logic helpers** (anything that isn't a Tools-tab action — e.g. the auth + adapter, post-install fixups): `containers//scripts/_*.sh`. + - The container scan **live-sources** every `.sh` under `containers//` + (`tools/` and `scripts/`), and `webui_tools.sh` **auto-merges** the + `.tools.json`. So dropping the folder onto an install is all it takes — no + central edits, no array regen. - **New runtime script?** Add it under `scripts//â€Ļ` and run `scripts/source/files/generate_arrays.sh run` so it's sourced (build/standalone tooling under `scripts/release` and `scripts/system` is intentionally excluded). diff --git a/scripts/app/auth_adapter.sh b/scripts/app/auth_adapter.sh index bd25b55..5cea55f 100644 --- a/scripts/app/auth_adapter.sh +++ b/scripts/app/auth_adapter.sh @@ -5,7 +5,7 @@ # Each app declares an auth profile in its config: # CFG__AUTH_PROFILE = single_password | user_password | multi_user | none # -# And implements adapter functions in scripts/app/containers//_auth.sh: +# And implements adapter functions in containers//scripts/_auth.sh: # authAdapter__setPassword "$user" "$password" # authAdapter__createUser "$user" "$password" "$email" "$isAdmin" # authAdapter__listUsers diff --git a/scripts/docker/app/functions/function_app_tool.sh b/scripts/docker/app/functions/function_app_tool.sh index 84c24e9..ee4e4ca 100644 --- a/scripts/docker/app/functions/function_app_tool.sh +++ b/scripts/docker/app/functions/function_app_tool.sh @@ -17,11 +17,11 @@ dockerAppRunTool() # One-file-per-tool convention: tool id `_` resolves to # function `app`. Each tool lives at - # scripts/app/containers//_.sh. + # containers//tools/_.sh (live-sourced by the container scan). # # Examples: # gluetun + refresh_providers → appGluetunRefreshProviders - # mattermost + reset_password → appMattermostResetPassword + # nextcloud + reset_password → appNextcloudResetPassword local app_name_ucfirst="$(tr '[:lower:]' '[:upper:]' <<< ${app_name:0:1})${app_name:1}" local tool_pascal="" @@ -35,7 +35,7 @@ dockerAppRunTool() local toolFuncName="app${app_name_ucfirst}${tool_pascal}" if ! declare -f "$toolFuncName" >/dev/null 2>&1; then - isError "App '$app_name' has no tool '$tool_name' (missing function $toolFuncName — expected in scripts/app/containers/$app_name/$app_name_${tool_name}.sh)." + isError "App '$app_name' has no tool '$tool_name' (missing function $toolFuncName — expected in containers/$app_name/tools/${app_name}_${tool_name}.sh)." return 1 fi diff --git a/scripts/source/files/arrays/files_app.sh b/scripts/source/files/arrays/files_app.sh index 721b159..2df0e73 100755 --- a/scripts/source/files/arrays/files_app.sh +++ b/scripts/source/files/arrays/files_app.sh @@ -10,66 +10,5 @@ app_scripts=( "app/app_status.sh" "app/app_update_specifics.sh" "app/auth_adapter.sh" - "app/containers/adguard/adguard_auth.sh" - "app/containers/bookstack/bookstack_auth.sh" - "app/containers/bookstack/bookstack_create_account.sh" - "app/containers/bookstack/bookstack_delete_user.sh" - "app/containers/bookstack/bookstack_list_users.sh" - "app/containers/bookstack/bookstack_reset_password.sh" - "app/containers/bookstack/bookstack_set_admin.sh" - "app/containers/crowdsec/crowdsec_alerts_list.sh" - "app/containers/crowdsec/crowdsec_console_disable.sh" - "app/containers/crowdsec/crowdsec_console_enroll.sh" - "app/containers/crowdsec/crowdsec_decisions_list.sh" - "app/containers/crowdsec/crowdsec_fix_priority.sh" - "app/containers/crowdsec/crowdsec_hub_update.sh" - "app/containers/crowdsec/crowdsec_metrics.sh" - "app/containers/crowdsec/crowdsec_status.sh" - "app/containers/crowdsec/crowdsec_unban.sh" - "app/containers/crowdsec/crowdsec_update.sh" - "app/containers/crowdsec/crowdsec_verify_firewall.sh" - "app/containers/dashy/dashy_manage_shortcuts.sh" - "app/containers/dashy/dashy_update_conf.sh" - "app/containers/focalboard/focalboard_auth.sh" - "app/containers/focalboard/focalboard_create_account.sh" - "app/containers/focalboard/focalboard_delete_user.sh" - "app/containers/focalboard/focalboard_list_users.sh" - "app/containers/focalboard/focalboard_reset_password.sh" - "app/containers/focalboard/focalboard_set_admin.sh" - "app/containers/gitea/gitea_auth.sh" - "app/containers/gitea/gitea_create_account.sh" - "app/containers/gitea/gitea_delete_user.sh" - "app/containers/gitea/gitea_list_users.sh" - "app/containers/gitea/gitea_reset_password.sh" - "app/containers/gitea/gitea_set_admin.sh" - "app/containers/gluetun/gluetun_recreate_routed.sh" - "app/containers/gluetun/gluetun_refresh_providers.sh" - "app/containers/invidious/invidious_auth.sh" - "app/containers/invidious/invidious_create_account.sh" - "app/containers/invidious/invidious_delete_user.sh" - "app/containers/invidious/invidious_list_users.sh" - "app/containers/invidious/invidious_reset_password.sh" - "app/containers/invidious/invidious_set_admin.sh" - "app/containers/mattermost/mattermost_auth.sh" - "app/containers/mattermost/mattermost_create_account.sh" - "app/containers/mattermost/mattermost_delete_user.sh" - "app/containers/mattermost/mattermost_list_users.sh" - "app/containers/mattermost/mattermost_reset_password.sh" - "app/containers/mattermost/mattermost_set_admin.sh" - "app/containers/nextcloud/nextcloud_add_trusted_domain.sh" - "app/containers/nextcloud/nextcloud_auth.sh" - "app/containers/nextcloud/nextcloud_create_account.sh" - "app/containers/nextcloud/nextcloud_delete_user.sh" - "app/containers/nextcloud/nextcloud_list_users.sh" - "app/containers/nextcloud/nextcloud_rescan_files.sh" - "app/containers/nextcloud/nextcloud_reset_password.sh" - "app/containers/nextcloud/nextcloud_set_admin.sh" - "app/containers/nextcloud/nextcloud_system_status.sh" - "app/containers/nextcloud/nextcloud_tail_logs.sh" - "app/containers/nextcloud/nextcloud_toggle_maintenance.sh" - "app/containers/owncloud/owncloud_setup_config.sh" - "app/containers/pihole/pihole_apply_dns_updater.sh" - "app/containers/traefik/traefik_auth.sh" - "app/containers/traefik/traefik_reset_password.sh" ) diff --git a/scripts/app/containers/mattermost/mattermost_auth.sh b/scripts/unused/OLD_CONTAINERS/mattermost/mattermost_auth.sh similarity index 100% rename from scripts/app/containers/mattermost/mattermost_auth.sh rename to scripts/unused/OLD_CONTAINERS/mattermost/mattermost_auth.sh diff --git a/scripts/app/containers/mattermost/mattermost_create_account.sh b/scripts/unused/OLD_CONTAINERS/mattermost/mattermost_create_account.sh similarity index 100% rename from scripts/app/containers/mattermost/mattermost_create_account.sh rename to scripts/unused/OLD_CONTAINERS/mattermost/mattermost_create_account.sh diff --git a/scripts/app/containers/mattermost/mattermost_delete_user.sh b/scripts/unused/OLD_CONTAINERS/mattermost/mattermost_delete_user.sh similarity index 100% rename from scripts/app/containers/mattermost/mattermost_delete_user.sh rename to scripts/unused/OLD_CONTAINERS/mattermost/mattermost_delete_user.sh diff --git a/scripts/app/containers/mattermost/mattermost_list_users.sh b/scripts/unused/OLD_CONTAINERS/mattermost/mattermost_list_users.sh similarity index 100% rename from scripts/app/containers/mattermost/mattermost_list_users.sh rename to scripts/unused/OLD_CONTAINERS/mattermost/mattermost_list_users.sh diff --git a/scripts/app/containers/mattermost/mattermost_reset_password.sh b/scripts/unused/OLD_CONTAINERS/mattermost/mattermost_reset_password.sh similarity index 100% rename from scripts/app/containers/mattermost/mattermost_reset_password.sh rename to scripts/unused/OLD_CONTAINERS/mattermost/mattermost_reset_password.sh diff --git a/scripts/app/containers/mattermost/mattermost_set_admin.sh b/scripts/unused/OLD_CONTAINERS/mattermost/mattermost_set_admin.sh similarity index 100% rename from scripts/app/containers/mattermost/mattermost_set_admin.sh rename to scripts/unused/OLD_CONTAINERS/mattermost/mattermost_set_admin.sh diff --git a/scripts/webui/data/generators/apps/webui_tools.sh b/scripts/webui/data/generators/apps/webui_tools.sh index e40d4ab..b40e5b7 100644 --- a/scripts/webui/data/generators/apps/webui_tools.sh +++ b/scripts/webui/data/generators/apps/webui_tools.sh @@ -3,10 +3,13 @@ # Single source of truth for the Tools tab. The frontend reads # /data/apps/generated/apps-tools.json — this generator emits it. # -# To add a tool: -# 1. Add an entry under that app's "tools" array below. -# 2. Drop a one-function file at scripts/app/containers//_.sh -# with `app()` defined inside. +# Tools are self-contained per app. To add one: +# 1. Declare it in containers//tools/.tools.json (a { "tools": [ â€Ļ ] } +# object — auto-merged below; no edit to this file needed). +# 2. Drop the one-function file beside it at +# containers//tools/_.sh with +# `app()` defined inside (live-sourced by the +# container scan). # 3. Re-run the WebUI updater (or call this function directly) to # regenerate apps-tools.json. # @@ -49,355 +52,19 @@ webuiGenerateAppsToolsConfig() { runFileOp mkdir -p "$(dirname "$output_file")" - # Heredoc carries the JSON literal verbatim — much easier to edit - # than escaping nested quotes through jq -n. + # 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": { - "pihole": { - "tools": [ - { - "id": "apply_dns_updater", - "label": "Apply DNS Updater", - "description": "Rewrite this server's /etc/resolv.conf to use Pi-hole as its DNS resolver right now. Same action that runs automatically when the global DNS Updater requirement is enabled.", - "icon": "🌐", - "fields": [] - } - ] - }, - "dashy": { - "tools": [ - { - "id": "manage_shortcuts", - "label": "Manage Shortcuts", - "description": "Pick which service URLs appear as shortcuts on the Dashy dashboard.", - "icon": "🧩", - "fields": [ - { - "name": "selected", - "label": "URLs to show on the dashboard", - "type": "app_urls_multi", - "prefillFromCfgKey": "CFG_DASHY_SHORTCUTS", - "excludeApps": ["dashy"] - } - ] - } - ] - }, - "bookstack": { - "tools": [ - { - "id": "reset_password", - "category": "users", - "label": "Reset User Password", - "description": "Reset an existing Bookstack user's password. Leave the password field blank to generate a random one — it is shown in the task log.", - "icon": "🔑", - "fields": [ - { "name": "email", "label": "User email", "type": "text", "placeholder": "user@example.com", "required": true }, - { "name": "password", "label": "New password (leave blank to generate)", "type": "password", "placeholder": "Leave blank for random" } - ] - }, - { - "id": "create_account", - "category": "users", - "label": "Create User Account", - "description": "Create a new Bookstack user. Tick \"Make admin\" to grant full admin rights; otherwise the new user gets the default registration role. Leave the password blank to generate a random one.", - "icon": "👤", - "fields": [ - { "name": "email", "label": "Email", "type": "text", "placeholder": "user@example.com", "required": true }, - { "name": "name", "label": "Display name", "type": "text", "required": true }, - { "name": "password", "label": "Password (leave blank to generate)", "type": "password", "placeholder": "Leave blank for random" }, - { "name": "admin", "label": "Make admin", "type": "checkbox", "default": false } - ] - }, - { "id": "list_users", "category": "users", "label": "List Users", "description": "Show every Bookstack user with their roles.", "icon": "📋", "fields": [] }, - { - "id": "delete_user", "category": "users", "label": "Delete User Account", - "description": "Permanently delete a user account.", "icon": "🗑", - "destructive": true, "confirm": "This cannot be undone.", - "fields": [ - { "name": "email", "label": "User email", "type": "text", "required": true } - ] - }, - { - "id": "set_admin", "category": "users", "label": "Set Admin Status", - "description": "Promote a user to admin or demote them to a normal user.", "icon": "👑", - "fields": [ - { "name": "email", "label": "User email", "type": "text", "required": true }, - { "name": "admin", "label": "Make admin", "type": "checkbox", "default": false } - ] - } - ] - }, - "focalboard": { - "tools": [ - { - "id": "reset_password", - "category": "users", - "label": "Reset User Password", - "description": "Reset an existing Focalboard user's password.", - "icon": "🔑", - "fields": [ - { "name": "username", "label": "Username", "type": "text", "required": true }, - { "name": "password", "label": "New password (leave blank to generate)", "type": "password", "placeholder": "Leave blank for random" } - ] - }, - { - "id": "create_account", - "category": "users", - "label": "Create User Account", - "description": "Create a new Focalboard user. Leave password blank to generate one.", - "icon": "👤", - "fields": [ - { "name": "username", "label": "Username", "type": "text", "required": true }, - { "name": "email", "label": "Email", "type": "text", "required": true }, - { "name": "password", "label": "Password (leave blank to generate)", "type": "password" }, - { "name": "admin", "label": "Make admin", "type": "checkbox", "default": false } - ] - }, - { - "id": "list_users", - "category": "users", - "label": "List Users", - "description": "Show every Focalboard user account.", - "icon": "📋", - "fields": [] - }, - { - "id": "delete_user", "category": "users", "label": "Delete User Account", - "description": "Permanently delete a user account.", "icon": "🗑", - "destructive": true, "confirm": "This cannot be undone.", - "fields": [ - { "name": "username", "label": "Username", "type": "text", "required": true } - ] - } - ] - }, - "mattermost": { - "tools": [ - { - "id": "reset_password", "category": "users", "label": "Reset User Password", - "description": "Reset a Mattermost user's password via mmctl.", "icon": "🔑", - "fields": [ - { "name": "username", "label": "Username or Email", "type": "text", "required": true }, - { "name": "password", "label": "New password (leave blank to generate)", "type": "password" } - ] - }, - { - "id": "create_account", "category": "users", "label": "Create User Account", - "description": "Create a new Mattermost user via mmctl.", "icon": "👤", - "fields": [ - { "name": "username", "label": "Username", "type": "text", "required": true }, - { "name": "email", "label": "Email", "type": "text", "required": true }, - { "name": "password", "label": "Password (leave blank to generate)", "type": "password" }, - { "name": "admin", "label": "Make system admin", "type": "checkbox", "default": false } - ] - }, - { "id": "list_users", "category": "users", "label": "List Users", "description": "Show every Mattermost user account.", "icon": "📋", "fields": [] }, - { - "id": "delete_user", "category": "users", "label": "Delete User Account", - "description": "Permanently delete a user account.", "icon": "🗑", - "destructive": true, "confirm": "This cannot be undone.", - "fields": [ - { "name": "username", "label": "Username/Email", "type": "text", "required": true } - ] - }, - { - "id": "set_admin", "category": "users", "label": "Set Admin Status", - "description": "Promote a user to admin or demote them to a normal user.", "icon": "👑", - "fields": [ - { "name": "username", "label": "Username/Email", "type": "text", "required": true }, - { "name": "admin", "label": "Make admin", "type": "checkbox", "default": false } - ] - } - ] - }, - "invidious": { - "tools": [ - { - "id": "reset_password", "category": "users", "label": "Reset User Password", - "description": "Reset an Invidious user's password (Postgres bcrypt update).", "icon": "🔑", - "fields": [ - { "name": "email", "label": "Email", "type": "text", "required": true }, - { "name": "password", "label": "New password (leave blank to generate)", "type": "password" } - ] - }, - { - "id": "create_account", "category": "users", "label": "Create User Account", - "description": "Create a new Invidious user (Invidious uses email as username).", "icon": "👤", - "fields": [ - { "name": "email", "label": "Email", "type": "text", "required": true }, - { "name": "password", "label": "Password (leave blank to generate)", "type": "password" }, - { "name": "admin", "label": "Make admin", "type": "checkbox", "default": false } - ] - }, - { "id": "list_users", "category": "users", "label": "List Users", "description": "Show every Invidious user account.", "icon": "📋", "fields": [] }, - { - "id": "delete_user", "category": "users", "label": "Delete User Account", - "description": "Permanently delete a user account.", "icon": "🗑", - "destructive": true, "confirm": "This cannot be undone.", - "fields": [ - { "name": "email", "label": "Email", "type": "text", "required": true } - ] - } - ] - }, - "gitea": { - "tools": [ - { - "id": "reset_password", - "category": "users", - "label": "Reset User Password", - "description": "Reset a Gitea user's password using the built-in `gitea admin user change-password` CLI. Leave the password blank to generate a random one.", - "icon": "🔑", - "fields": [ - { "name": "username", "label": "Username", "type": "text", "required": true }, - { "name": "password", "label": "New password (leave blank to generate)", "type": "password", "placeholder": "Leave blank for random" } - ] - }, - { - "id": "create_account", - "category": "users", - "label": "Create User Account", - "description": "Create a new Gitea user via the built-in `gitea admin user create` CLI. Tick \"Make admin\" for full admin rights. Leave the password blank to generate a random one. The account starts ready to log in (no forced password change).", - "icon": "👤", - "fields": [ - { "name": "username", "label": "Username", "type": "text", "required": true }, - { "name": "email", "label": "Email", "type": "text", "placeholder": "user@example.com", "required": true }, - { "name": "password", "label": "Password (leave blank to generate)", "type": "password", "placeholder": "Leave blank for random" }, - { "name": "admin", "label": "Make admin", "type": "checkbox", "default": false } - ] - }, - { "id": "list_users", "category": "users", "label": "List Users", "description": "Show every Gitea user account.", "icon": "📋", "fields": [] }, - { - "id": "delete_user", "category": "users", "label": "Delete User Account", - "description": "Permanently delete a user account.", "icon": "🗑", - "destructive": true, "confirm": "This cannot be undone.", - "fields": [ - { "name": "username", "label": "Username", "type": "text", "required": true } - ] - }, - { - "id": "set_admin", "category": "users", "label": "Set Admin Status", - "description": "Promote a user to admin or demote them to a normal user.", "icon": "👑", - "fields": [ - { "name": "username", "label": "Username", "type": "text", "required": true }, - { "name": "admin", "label": "Make admin", "type": "checkbox", "default": false } - ] - } - ] - }, - "gluetun": { - "tools": [ - { - "id": "refresh_providers", - "label": "Refresh VPN Providers", - "description": "Re-scan VPN providers and country lists, regenerating the snapshot used by the country picker.", - "icon": "🔄", - "fields": [] - } - ] - }, - "traefik": { - "tools": [ - { - "id": "reset_password", - "label": "Reset Dashboard Login", - "description": "Reset the Traefik dashboard credentials (CFG_TRAEFIK_USER / CFG_TRAEFIK_PASS) and regenerate the htpasswd entry in protectionauth.yml. Leave fields blank to keep the current username and/or generate a new random password.", - "icon": "🔑", - "fields": [ - { "name": "username", "label": "Username (leave blank to keep current)", "type": "text", "placeholder": "admin" }, - { "name": "password", "label": "New password (leave blank to generate)", "type": "password", "placeholder": "Leave blank for random" } - ] - } - ] - }, - "nextcloud": { - "tools": [ - { - "id": "reset_password", - "category": "users", - "label": "Reset User Password", - "description": "Reset an existing Nextcloud user's password. Leave the password field blank to generate a random one — it is shown in the task log.", - "icon": "🔑", - "fields": [ - { "name": "username", "label": "Username", "type": "text", "placeholder": "alice", "required": true }, - { "name": "password", "label": "New password (leave blank to generate)", "type": "password", "placeholder": "Leave blank for random" } - ] - }, - { - "id": "create_account", - "category": "users", - "label": "Create User Account", - "description": "Create a new Nextcloud user. Tick \"Make admin\" to add them to the admin group. Leave the password blank to generate a random one.", - "icon": "👤", - "fields": [ - { "name": "username", "label": "Username", "type": "text", "placeholder": "alice", "required": true }, - { "name": "display_name", "label": "Display name", "type": "text", "placeholder": "Alice Smith" }, - { "name": "password", "label": "Password (leave blank to generate)", "type": "password", "placeholder": "Leave blank for random" }, - { "name": "admin", "label": "Make admin", "type": "checkbox", "default": false } - ] - }, - { "id": "list_users", "category": "users", "label": "List Users", "description": "Show every Nextcloud user with their display name and admin flag.", "icon": "📋", "fields": [] }, - { - "id": "delete_user", "category": "users", "label": "Delete User Account", - "description": "Permanently delete a user and all their files.", "icon": "🗑", - "destructive": true, "confirm": "This cannot be undone. The user's files will be removed.", - "fields": [ - { "name": "username", "label": "Username", "type": "text", "required": true } - ] - }, - { - "id": "set_admin", "category": "users", "label": "Set Admin Status", - "description": "Add a user to (or remove from) the admin group.", "icon": "👑", - "fields": [ - { "name": "username", "label": "Username", "type": "text", "required": true }, - { "name": "admin", "label": "Make admin", "type": "checkbox", "default": false } - ] - }, - { - "id": "toggle_maintenance", "category": "maintenance", "label": "Toggle Maintenance Mode", - "description": "Lock all users out and show a maintenance notice — required before running upgrades or repairs from the CLI.", "icon": "🚧", - "fields": [ - { "name": "enable", "label": "Enable maintenance mode", "type": "checkbox", "default": true } - ] - }, - { - "id": "rescan_files", "category": "maintenance", "label": "Rescan Files", - "description": "Re-index Nextcloud's file metadata. Run this after files were added or removed on disk outside Nextcloud (rsync, restore, manual copy). Leave the username blank to scan every user.", "icon": "🔄", - "fields": [ - { "name": "username", "label": "Username (blank = all users)", "type": "text", "placeholder": "blank for all" } - ] - }, - { - "id": "add_trusted_domain", "category": "system", "label": "Add Trusted Domain", - "description": "Append a hostname to Nextcloud's trusted_domains list so requests to that host are accepted.", "icon": "🌐", - "fields": [ - { "name": "domain", "label": "Domain", "type": "text", "placeholder": "cloud.example.org", "required": true } - ] - }, - { - "id": "system_status", "category": "system", "label": "System Status", - "description": "Show Nextcloud's version, install state, and maintenance flag.", "icon": "â„šī¸", - "fields": [] - }, - { - "id": "tail_logs", "category": "system", "label": "Tail Logs", - "description": "Show the most recent lines of nextcloud.log.", "icon": "📜", - "fields": [ - { "name": "lines", "label": "Lines", "type": "number", "default": 100, "min": 10, "max": 1000 } - ] - } - ] - } - } + "apps": {} } JSON - # Merge per-app tool declarations so a DROPPED-IN app (e.g. from LibrePortal-Infra) - # registers its own Tools tab actions without editing this file. Each app may ship - # containers//.tools.json = { "tools": [ â€Ļ ] } (same schema as above); - # it sets .apps[]. Core apps declared in the heredoc need no such file. + # 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//tools/.tools.json = { "tools": [ â€Ļ ] }; + # it sets .apps[]. 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