Make multi-instance actually install, and stop apps stealing each other's network rows
Instance install (bugs found by running one end to end): - The cloned compose kept the TYPE's tag namespace (#LIBREPORTAL|BOOKSTACK_APP_KEY_1_TAG|...) while the config had been re-namespaced to CFG_<SLUG>_*, so tagsProcessorAppConfigValues matched nothing, the placeholders survived and the pre-start guard refused to launch. Rewrite the tag names and *_DATA tokens too — narrowly, so an app whose compose sets a real env var named after itself is untouched. - Tools/hooks kept uppercase CFG_<TYPE>_ reads, so an instance provisioned itself from the type's config and ignored its own values. - Cloned hooks were never loaded: both loaders run at startup, before the instance dir exists, so _appCallHook's `declare -F` found nothing and every <slug>_install_* hook silently no-opped — for bookstack that is the readiness probe and the admin bootstrap. Source the instance's own scripts in-process, then regen arrays + manifest for later runs. - bookstack's hook hardcoded the container name after `docker exec -e ...` flags, where the rewriter can't see it, so an instance's admin bootstrap ran against the BASE app's container — including a tinker DELETE of a user. Target "$app_name" instead, and teach the rewriter the container="<type>" assignment form used by auth adapters. network_resources uniqueness: UNIQUE(resource_type, resource_value) is right for 'ip' and 'port' but the port-tag writer stores descriptive rows in the same table with INSERT OR REPLACE, so every install DELETED the matching row from whichever app held it. traefik_managed and url_accessible are booleans, so the whole table could only ever hold one row of each. Observed live: installing a second bookstack took all four traefik_managed/url_accessible rows from stoat and bookstack, and removing that instance took the stolen rows with it. Replace it with a partial unique index scoped to ip/port, and migrate existing databases in place (SQLite can't drop a constraint, so the table is rebuilt inside a transaction). The migration is invoked from portUpdateComposeTags, not just databaseCreateTables — the latter only runs from startPreInstall, which a working install never re-runs. Verified: two bookstacks now hold port_tag_internal=80, traefik_managed and url_accessible simultaneously; duplicate host ports and IPs are still rejected; instance installs, serves HTTP 200, provisions its own admin in its own database, and removes cleanly with no orphan rows. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
f9ec4cc986
commit
ff25b08ee8
@ -46,17 +46,23 @@ bookstack_install_post_start()
|
||||
fi
|
||||
isSuccessful "Bookstack is online (HTTP ${bookstack_http_code})."
|
||||
|
||||
# Target the container by $app_name, never the literal "bookstack": under
|
||||
# multi-instance this hook is cloned for each instance, and a hardcoded name
|
||||
# pointed every instance's admin bootstrap at the BASE app's container —
|
||||
# provisioning (and, in the branch below, DELETING) users in the wrong
|
||||
# database. instanceCreate's rewriter can't catch it either, since the
|
||||
# container name here doesn't directly follow `docker exec`.
|
||||
local bookstack_create_output
|
||||
bookstack_create_output=$(runFileOp docker exec \
|
||||
-e EZ_BS_NEW_EMAIL="$bookstack_target_email" \
|
||||
-e EZ_BS_NEW_PASS="$bookstack_target_pass" \
|
||||
bookstack sh -c 'cd /app/www && s6-setuidgid abc php artisan bookstack:create-admin --no-ansi --email="$EZ_BS_NEW_EMAIL" --name=Admin --password="$EZ_BS_NEW_PASS" 2>&1')
|
||||
"$app_name" sh -c 'cd /app/www && s6-setuidgid abc php artisan bookstack:create-admin --no-ansi --email="$EZ_BS_NEW_EMAIL" --name=Admin --password="$EZ_BS_NEW_PASS" 2>&1')
|
||||
local bookstack_create_rc=$?
|
||||
if [[ $bookstack_create_rc -eq 0 ]]; then
|
||||
isSuccessful "Bookstack admin account created (email: $bookstack_target_email)."
|
||||
|
||||
if [[ "$bookstack_target_email" != "admin@admin.com" ]]; then
|
||||
runFileOp docker exec -i bookstack php /app/www/artisan tinker --no-ansi >/dev/null 2>&1 <<'PHP'
|
||||
runFileOp docker exec -i "$app_name" php /app/www/artisan tinker --no-ansi >/dev/null 2>&1 <<'PHP'
|
||||
$c = class_exists('\BookStack\Users\Models\User') ? '\BookStack\Users\Models\User' : '\BookStack\Auth\User';
|
||||
optional($c::where('email', 'admin@admin.com')->first())->delete();
|
||||
PHP
|
||||
|
||||
@ -104,8 +104,7 @@ databaseCreateTables()
|
||||
status TEXT DEFAULT 'active',
|
||||
created_date DATE DEFAULT CURRENT_DATE,
|
||||
created_time TIME DEFAULT CURRENT_TIME,
|
||||
UNIQUE(app_name, resource_type, service_name),
|
||||
UNIQUE(resource_type, resource_value)
|
||||
UNIQUE(app_name, resource_type, service_name)
|
||||
);")
|
||||
checkSuccess "Creating unified network_resources table"
|
||||
|
||||
@ -121,7 +120,81 @@ databaseCreateTables()
|
||||
local result; result=$(sqlite3 "$docker_dir/$db_file" "CREATE INDEX IF NOT EXISTS idx_network_resources_parent_service ON network_resources(parent_service);")
|
||||
checkSuccess "Creating network resources parent service index"
|
||||
fi
|
||||
|
||||
# Runs for fresh AND existing databases (this function is re-run on every
|
||||
# startup), so an install created before the constraint was scoped is
|
||||
# repaired in place.
|
||||
databaseMigrateNetworkResourcesUnique
|
||||
else
|
||||
echo "SQLite3 is not installed. Skipping table creation."
|
||||
fi
|
||||
}
|
||||
|
||||
# Scope the global-uniqueness constraint on network_resources to the resources
|
||||
# that are actually globally unique.
|
||||
#
|
||||
# The table shipped with UNIQUE(resource_type, resource_value). That is right for
|
||||
# 'ip' and 'port' — no two apps may hold the same host port or container IP — but
|
||||
# the port-tag writer stores descriptive rows in the same table and writes them
|
||||
# with INSERT OR REPLACE, so each install DELETED the matching row from whichever
|
||||
# app held it:
|
||||
# port_tag_internal the CONTAINER-side port: every web app uses 80
|
||||
# traefik_managed 'true'/'false' — two possible values, so the whole table
|
||||
# url_accessible could only ever hold ONE row of each, for one app
|
||||
# Consequences: the firewall rebuild's traefik_managed LEFT JOIN reads NULL for
|
||||
# every app but the last one installed, and in rooted mode a stolen
|
||||
# port_tag_internal makes ufw-docker fall back to the EXTERNAL port — the exact
|
||||
# "cannot find the published port" failure firewall_rebuild_from_db.sh warns
|
||||
# about. Rows already lost are not reconstructable here; each app repopulates its
|
||||
# own on the next `libreportal app install <app>`.
|
||||
#
|
||||
# SQLite cannot drop a table constraint, so this rebuilds the table when the
|
||||
# legacy constraint is still present, then enforces the narrow rule as a partial
|
||||
# unique index. Idempotent: once migrated, the guard skips it.
|
||||
databaseMigrateNetworkResourcesUnique()
|
||||
{
|
||||
local table_sql
|
||||
table_sql=$(sqlite3 "$docker_dir/$db_file" "SELECT sql FROM sqlite_master WHERE type='table' AND name='network_resources';" 2>/dev/null)
|
||||
[[ -n "$table_sql" ]] || return 0
|
||||
|
||||
if [[ "$table_sql" == *"UNIQUE(resource_type, resource_value)"* ]]; then
|
||||
# Single transaction: either the whole swap lands or the old table stays.
|
||||
local result; result=$(sqlite3 "$docker_dir/$db_file" "
|
||||
PRAGMA foreign_keys=off;
|
||||
BEGIN TRANSACTION;
|
||||
CREATE TABLE network_resources_migrated (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
app_name TEXT NOT NULL,
|
||||
resource_type TEXT NOT NULL,
|
||||
resource_value TEXT NOT NULL,
|
||||
service_name TEXT DEFAULT 'main',
|
||||
parent_service TEXT DEFAULT NULL,
|
||||
status TEXT DEFAULT 'active',
|
||||
created_date DATE DEFAULT CURRENT_DATE,
|
||||
created_time TIME DEFAULT CURRENT_TIME,
|
||||
UNIQUE(app_name, resource_type, service_name)
|
||||
);
|
||||
INSERT INTO network_resources_migrated
|
||||
SELECT id, app_name, resource_type, resource_value, service_name,
|
||||
parent_service, status, created_date, created_time
|
||||
FROM network_resources;
|
||||
DROP TABLE network_resources;
|
||||
ALTER TABLE network_resources_migrated RENAME TO network_resources;
|
||||
CREATE INDEX IF NOT EXISTS idx_network_resources_app ON network_resources(app_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_network_resources_type ON network_resources(resource_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_network_resources_value ON network_resources(resource_value);
|
||||
CREATE INDEX IF NOT EXISTS idx_network_resources_status ON network_resources(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_network_resources_parent_service ON network_resources(parent_service);
|
||||
COMMIT;
|
||||
" 2>&1)
|
||||
if [[ -n "$result" ]]; then
|
||||
isNotice "network_resources migration reported: $result"
|
||||
fi
|
||||
checkSuccess "Scoping network_resources uniqueness to ip/port"
|
||||
fi
|
||||
|
||||
# The narrow rule, as a partial index — still blocks two apps claiming the
|
||||
# same host port or IP, and lets the descriptive rows coexist. Silent: it is
|
||||
# a no-op on every startup after the first.
|
||||
local result; result=$(sqlite3 "$docker_dir/$db_file" "CREATE UNIQUE INDEX IF NOT EXISTS idx_network_resources_global_value ON network_resources(resource_type, resource_value) WHERE resource_type IN ('ip','port');" 2>/dev/null)
|
||||
}
|
||||
|
||||
@ -94,6 +94,19 @@ _instanceRewriteCompose() {
|
||||
sed -i -E "s/(container_name:[[:space:]]*)${type}\b/\1${slug}/g" "$f"
|
||||
# 3. The files-backup label's container ref (libreportal.backup.files: "<type>:/...").
|
||||
sed -i -E "s/(libreportal\.backup\.files:[[:space:]]*\")${type}\b/\1${slug}/g" "$f"
|
||||
# 4. The per-app tag namespace. tagsProcessorAppConfigValues derives tag names
|
||||
# mechanically from the config keys (CFG_<APP>_APP_KEY_1 -> the tag
|
||||
# <APP>_APP_KEY_1_TAG), so a clone still carrying the TYPE's tag names has
|
||||
# nothing to match its own CFG_<SLUG>_* vars: the placeholders survive and
|
||||
# the pre-start guard refuses to launch the instance.
|
||||
# Deliberately narrow — the tag name right after the #LIBREPORTAL| marker,
|
||||
# and the *_DATA placeholder tokens. A blanket <TYPE>_ rewrite would also
|
||||
# hit an app whose compose sets a real container env var named after itself
|
||||
# (- <TYPE>_SECRET=...), renaming the variable the image reads.
|
||||
local type_u="${type^^}" slug_u="${slug^^}"
|
||||
type_u="${type_u//-/_}"; slug_u="${slug_u//-/_}"
|
||||
sed -i -E "s/(#LIBREPORTAL\|)${type_u}_/\1${slug_u}_/g" "$f"
|
||||
sed -i -E "s/\b${type_u}_([A-Z0-9_]*)_DATA\b/${slug_u}_\1_DATA/g" "$f"
|
||||
}
|
||||
|
||||
# Clone + prefix-rename the per-app tools/scripts so an instance's helpers target
|
||||
@ -117,6 +130,17 @@ _instanceRewriteTools() {
|
||||
sed -i -E "s/\b${type}_/${slug}_/g" "$f"
|
||||
sed -i -E "s/(docker[[:space:]]+(exec|logs|restart|stop|start|inspect)[[:space:]]+)${type}\b/\1${slug}/g" "$f"
|
||||
sed -i -E "s/\b${type}\.config\b/${slug}.config/g" "$f"
|
||||
# Config reads are uppercase and so escape the lowercase rename above:
|
||||
# an instance hook left reading CFG_<TYPE>_ADMIN_EMAIL would provision
|
||||
# itself from the type's config (its own value silently ignored).
|
||||
sed -i -E "s/\bCFG_${type^^}_/CFG_${slug^^}_/g" "$f"
|
||||
# container="<type>" / container_name="<type>" holds the docker target
|
||||
# for exec-based helpers (auth adapters, tools). The bare literal has
|
||||
# no trailing underscore, so the rename above misses it and the clone
|
||||
# would operate on the BASE app's container. Kept to these two
|
||||
# assignment forms — a blanket bare-<type> rewrite would hit image
|
||||
# names and prose.
|
||||
sed -i -E "s/(\b(container|container_name)=\")${type}(\")/\1${slug}\3/g" "$f"
|
||||
done
|
||||
done
|
||||
}
|
||||
@ -202,7 +226,27 @@ instanceCreate() {
|
||||
|
||||
isSuccessful "Instance template ready: $slug (instance of $type)"
|
||||
|
||||
# 5. Hand off to the standard installer — from here it's just another app.
|
||||
# 5. Make the instance's freshly-cloned installers/hooks callable in THIS
|
||||
# process. Both loaders ran at startup, before this dir existed: the eager
|
||||
# scan (sourceScanFiles "containers") never saw it, and the lazy manifest
|
||||
# has no stub for it. Without this, _appCallHook's `declare -F` finds
|
||||
# nothing and every <slug>_install_* hook silently no-ops — for bookstack
|
||||
# that is the readiness probe and the admin-account bootstrap, so the
|
||||
# instance installs "successfully" with no usable login.
|
||||
local _inst_f
|
||||
while IFS= read -r -d '' _inst_f; do
|
||||
source "$_inst_f"
|
||||
done < <(find "$inst_dir" -maxdepth 2 -type d -name resources -prune -o -type f -name '*.sh' -print0 2>/dev/null)
|
||||
|
||||
# Persist that for later runs (and the WebUI): regenerate the file arrays +
|
||||
# function manifest now that a new app dir exists. Best-effort — a stale
|
||||
# manifest only affects lazy mode, and the in-process sourcing above already
|
||||
# covers this install.
|
||||
if declare -F lpRegenArrays >/dev/null 2>&1; then
|
||||
lpRegenArrays force >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
# 6. Hand off to the standard installer — from here it's just another app.
|
||||
if ! declare -F dockerInstallApp >/dev/null 2>&1; then
|
||||
isError "dockerInstallApp unavailable; instance template created but not installed."
|
||||
return 1
|
||||
|
||||
@ -5,7 +5,18 @@ portUpdateComposeTags()
|
||||
{
|
||||
local app_name="$1"
|
||||
local full_file_path="$2"
|
||||
|
||||
|
||||
# This function is the only INSERT OR REPLACE writer into network_resources,
|
||||
# so it is where the legacy UNIQUE(resource_type, resource_value) did its
|
||||
# damage — each run silently stole other apps' port_tag_internal /
|
||||
# traefik_managed / url_accessible rows. Repair the constraint here rather
|
||||
# than only in databaseCreateTables: that runs from startPreInstall, which a
|
||||
# working install never re-runs, so existing installs would keep corrupting
|
||||
# themselves until a full reinstall. One guarded SELECT; a no-op once done.
|
||||
if declare -F databaseMigrateNetworkResourcesUnique >/dev/null 2>&1; then
|
||||
databaseMigrateNetworkResourcesUnique
|
||||
fi
|
||||
|
||||
local port_tags_found=()
|
||||
local port_numbers=()
|
||||
|
||||
|
||||
@ -58,12 +58,12 @@ declare -gA LP_FN_MAP=(
|
||||
[appLinkdingResetPassword]="linkding/tools/linkding_reset_password.sh"
|
||||
[appLinkdingSetAdmin]="linkding/tools/linkding_set_admin.sh"
|
||||
[appMatrixCreateAccount]="matrix/tools/matrix_create_account.sh"
|
||||
[appMatrixDeactivateUser]="matrix/tools/matrix_deactivate_user.sh"
|
||||
[appMatrixDeleteUser]="matrix/tools/matrix_delete_user.sh"
|
||||
[appMatrixListUsers]="matrix/tools/matrix_list_users.sh"
|
||||
[appMatrixResetPassword]="matrix/tools/matrix_reset_password.sh"
|
||||
[appMatrixSetAdmin]="matrix/tools/matrix_set_admin.sh"
|
||||
[appMattermostCreateAccount]="mattermost/tools/mattermost_create_account.sh"
|
||||
[appMattermostDeactivateUser]="mattermost/tools/mattermost_deactivate_user.sh"
|
||||
[appMattermostDeleteUser]="mattermost/tools/mattermost_delete_user.sh"
|
||||
[appMattermostListUsers]="mattermost/tools/mattermost_list_users.sh"
|
||||
[appMattermostResetPassword]="mattermost/tools/mattermost_reset_password.sh"
|
||||
[appMattermostSetAdmin]="mattermost/tools/mattermost_set_admin.sh"
|
||||
@ -86,7 +86,7 @@ declare -gA LP_FN_MAP=(
|
||||
[_appReqServiceInstalled]="checks/requirements/check_app_install.sh"
|
||||
[_appReqServiceMsg]="checks/requirements/check_app_install.sh"
|
||||
[appRocketchatCreateAccount]="rocketchat/tools/rocketchat_create_account.sh"
|
||||
[appRocketchatDeactivateUser]="rocketchat/tools/rocketchat_deactivate_user.sh"
|
||||
[appRocketchatDeleteUser]="rocketchat/tools/rocketchat_delete_user.sh"
|
||||
[appRocketchatEnableUser]="rocketchat/tools/rocketchat_enable_user.sh"
|
||||
[appRocketchatListUsers]="rocketchat/tools/rocketchat_list_users.sh"
|
||||
[appRocketchatResetPassword]="rocketchat/tools/rocketchat_reset_password.sh"
|
||||
@ -103,7 +103,7 @@ declare -gA LP_FN_MAP=(
|
||||
[appStalwartSetMode]="stalwart/tools/stalwart_set_mode.sh"
|
||||
[appStalwartShowDns]="stalwart/tools/stalwart_show_dns.sh"
|
||||
[appStatus]="app/app_status.sh"
|
||||
[appStoatDisableUser]="stoat/tools/stoat_disable_user.sh"
|
||||
[appStoatDeleteUser]="stoat/tools/stoat_delete_user.sh"
|
||||
[appStoatEnableUser]="stoat/tools/stoat_enable_user.sh"
|
||||
[appStoatListUsers]="stoat/tools/stoat_list_users.sh"
|
||||
[appTraefikExtraMiddlewares_onlyoffice]="onlyoffice/scripts/onlyoffice_traefik.sh"
|
||||
@ -405,6 +405,7 @@ declare -gA LP_FN_MAP=(
|
||||
[databaseListAllApps]="database/app/db_list_all_apps.sh"
|
||||
[databaseListInstalledApp]="database/app/db_list_installed_app.sh"
|
||||
[databaseListInstalledApps]="database/app/db_list_installed_apps.sh"
|
||||
[databaseMigrateNetworkResourcesUnique]="database/tables/db_create_tables.sh"
|
||||
[databaseOptionInsert]="database/insert/db_insert_option.sh"
|
||||
[databasePortOpenInsert]="database/insert/db_insert_port_open.sh"
|
||||
[databasePortUsedInsert]="database/insert/db_insert_port_used.sh"
|
||||
@ -1181,12 +1182,12 @@ declare -gA LP_FN_ROOT=(
|
||||
[appLinkdingResetPassword]="containers"
|
||||
[appLinkdingSetAdmin]="containers"
|
||||
[appMatrixCreateAccount]="containers"
|
||||
[appMatrixDeactivateUser]="containers"
|
||||
[appMatrixDeleteUser]="containers"
|
||||
[appMatrixListUsers]="containers"
|
||||
[appMatrixResetPassword]="containers"
|
||||
[appMatrixSetAdmin]="containers"
|
||||
[appMattermostCreateAccount]="containers"
|
||||
[appMattermostDeactivateUser]="containers"
|
||||
[appMattermostDeleteUser]="containers"
|
||||
[appMattermostListUsers]="containers"
|
||||
[appMattermostResetPassword]="containers"
|
||||
[appMattermostSetAdmin]="containers"
|
||||
@ -1209,7 +1210,7 @@ declare -gA LP_FN_ROOT=(
|
||||
[_appReqServiceInstalled]="scripts"
|
||||
[_appReqServiceMsg]="scripts"
|
||||
[appRocketchatCreateAccount]="containers"
|
||||
[appRocketchatDeactivateUser]="containers"
|
||||
[appRocketchatDeleteUser]="containers"
|
||||
[appRocketchatEnableUser]="containers"
|
||||
[appRocketchatListUsers]="containers"
|
||||
[appRocketchatResetPassword]="containers"
|
||||
@ -1226,7 +1227,7 @@ declare -gA LP_FN_ROOT=(
|
||||
[appStalwartSetMode]="containers"
|
||||
[appStalwartShowDns]="containers"
|
||||
[appStatus]="scripts"
|
||||
[appStoatDisableUser]="containers"
|
||||
[appStoatDeleteUser]="containers"
|
||||
[appStoatEnableUser]="containers"
|
||||
[appStoatListUsers]="containers"
|
||||
[appTraefikExtraMiddlewares_onlyoffice]="containers"
|
||||
@ -1528,6 +1529,7 @@ declare -gA LP_FN_ROOT=(
|
||||
[databaseListAllApps]="scripts"
|
||||
[databaseListInstalledApp]="scripts"
|
||||
[databaseListInstalledApps]="scripts"
|
||||
[databaseMigrateNetworkResourcesUnique]="scripts"
|
||||
[databaseOptionInsert]="scripts"
|
||||
[databasePortOpenInsert]="scripts"
|
||||
[databasePortUsedInsert]="scripts"
|
||||
@ -2339,12 +2341,12 @@ appLinkdingListUsers() { unset -f appLinkdingListUsers; __lpAutoload "${install_
|
||||
appLinkdingResetPassword() { unset -f appLinkdingResetPassword; __lpAutoload "${install_containers_dir}linkding/tools/linkding_reset_password.sh"; appLinkdingResetPassword "$@"; }
|
||||
appLinkdingSetAdmin() { unset -f appLinkdingSetAdmin; __lpAutoload "${install_containers_dir}linkding/tools/linkding_set_admin.sh"; appLinkdingSetAdmin "$@"; }
|
||||
appMatrixCreateAccount() { unset -f appMatrixCreateAccount; __lpAutoload "${install_containers_dir}matrix/tools/matrix_create_account.sh"; appMatrixCreateAccount "$@"; }
|
||||
appMatrixDeactivateUser() { unset -f appMatrixDeactivateUser; __lpAutoload "${install_containers_dir}matrix/tools/matrix_deactivate_user.sh"; appMatrixDeactivateUser "$@"; }
|
||||
appMatrixDeleteUser() { unset -f appMatrixDeleteUser; __lpAutoload "${install_containers_dir}matrix/tools/matrix_delete_user.sh"; appMatrixDeleteUser "$@"; }
|
||||
appMatrixListUsers() { unset -f appMatrixListUsers; __lpAutoload "${install_containers_dir}matrix/tools/matrix_list_users.sh"; appMatrixListUsers "$@"; }
|
||||
appMatrixResetPassword() { unset -f appMatrixResetPassword; __lpAutoload "${install_containers_dir}matrix/tools/matrix_reset_password.sh"; appMatrixResetPassword "$@"; }
|
||||
appMatrixSetAdmin() { unset -f appMatrixSetAdmin; __lpAutoload "${install_containers_dir}matrix/tools/matrix_set_admin.sh"; appMatrixSetAdmin "$@"; }
|
||||
appMattermostCreateAccount() { unset -f appMattermostCreateAccount; __lpAutoload "${install_containers_dir}mattermost/tools/mattermost_create_account.sh"; appMattermostCreateAccount "$@"; }
|
||||
appMattermostDeactivateUser() { unset -f appMattermostDeactivateUser; __lpAutoload "${install_containers_dir}mattermost/tools/mattermost_deactivate_user.sh"; appMattermostDeactivateUser "$@"; }
|
||||
appMattermostDeleteUser() { unset -f appMattermostDeleteUser; __lpAutoload "${install_containers_dir}mattermost/tools/mattermost_delete_user.sh"; appMattermostDeleteUser "$@"; }
|
||||
appMattermostListUsers() { unset -f appMattermostListUsers; __lpAutoload "${install_containers_dir}mattermost/tools/mattermost_list_users.sh"; appMattermostListUsers "$@"; }
|
||||
appMattermostResetPassword() { unset -f appMattermostResetPassword; __lpAutoload "${install_containers_dir}mattermost/tools/mattermost_reset_password.sh"; appMattermostResetPassword "$@"; }
|
||||
appMattermostSetAdmin() { unset -f appMattermostSetAdmin; __lpAutoload "${install_containers_dir}mattermost/tools/mattermost_set_admin.sh"; appMattermostSetAdmin "$@"; }
|
||||
@ -2367,7 +2369,7 @@ _appReqHasDomain() { unset -f _appReqHasDomain; __lpAutoload "${install_scripts_
|
||||
_appReqServiceInstalled() { unset -f _appReqServiceInstalled; __lpAutoload "${install_scripts_dir}checks/requirements/check_app_install.sh"; _appReqServiceInstalled "$@"; }
|
||||
_appReqServiceMsg() { unset -f _appReqServiceMsg; __lpAutoload "${install_scripts_dir}checks/requirements/check_app_install.sh"; _appReqServiceMsg "$@"; }
|
||||
appRocketchatCreateAccount() { unset -f appRocketchatCreateAccount; __lpAutoload "${install_containers_dir}rocketchat/tools/rocketchat_create_account.sh"; appRocketchatCreateAccount "$@"; }
|
||||
appRocketchatDeactivateUser() { unset -f appRocketchatDeactivateUser; __lpAutoload "${install_containers_dir}rocketchat/tools/rocketchat_deactivate_user.sh"; appRocketchatDeactivateUser "$@"; }
|
||||
appRocketchatDeleteUser() { unset -f appRocketchatDeleteUser; __lpAutoload "${install_containers_dir}rocketchat/tools/rocketchat_delete_user.sh"; appRocketchatDeleteUser "$@"; }
|
||||
appRocketchatEnableUser() { unset -f appRocketchatEnableUser; __lpAutoload "${install_containers_dir}rocketchat/tools/rocketchat_enable_user.sh"; appRocketchatEnableUser "$@"; }
|
||||
appRocketchatListUsers() { unset -f appRocketchatListUsers; __lpAutoload "${install_containers_dir}rocketchat/tools/rocketchat_list_users.sh"; appRocketchatListUsers "$@"; }
|
||||
appRocketchatResetPassword() { unset -f appRocketchatResetPassword; __lpAutoload "${install_containers_dir}rocketchat/tools/rocketchat_reset_password.sh"; appRocketchatResetPassword "$@"; }
|
||||
@ -2384,7 +2386,7 @@ appSetupComposeTags_wireguard() { unset -f appSetupComposeTags_wireguard; __lpAu
|
||||
appStalwartSetMode() { unset -f appStalwartSetMode; __lpAutoload "${install_containers_dir}stalwart/tools/stalwart_set_mode.sh"; appStalwartSetMode "$@"; }
|
||||
appStalwartShowDns() { unset -f appStalwartShowDns; __lpAutoload "${install_containers_dir}stalwart/tools/stalwart_show_dns.sh"; appStalwartShowDns "$@"; }
|
||||
appStatus() { unset -f appStatus; __lpAutoload "${install_scripts_dir}app/app_status.sh"; appStatus "$@"; }
|
||||
appStoatDisableUser() { unset -f appStoatDisableUser; __lpAutoload "${install_containers_dir}stoat/tools/stoat_disable_user.sh"; appStoatDisableUser "$@"; }
|
||||
appStoatDeleteUser() { unset -f appStoatDeleteUser; __lpAutoload "${install_containers_dir}stoat/tools/stoat_delete_user.sh"; appStoatDeleteUser "$@"; }
|
||||
appStoatEnableUser() { unset -f appStoatEnableUser; __lpAutoload "${install_containers_dir}stoat/tools/stoat_enable_user.sh"; appStoatEnableUser "$@"; }
|
||||
appStoatListUsers() { unset -f appStoatListUsers; __lpAutoload "${install_containers_dir}stoat/tools/stoat_list_users.sh"; appStoatListUsers "$@"; }
|
||||
appTraefikExtraMiddlewares_onlyoffice() { unset -f appTraefikExtraMiddlewares_onlyoffice; __lpAutoload "${install_containers_dir}onlyoffice/scripts/onlyoffice_traefik.sh"; appTraefikExtraMiddlewares_onlyoffice "$@"; }
|
||||
@ -2686,6 +2688,7 @@ databaseInstallApp() { unset -f databaseInstallApp; __lpAutoload "${install_scri
|
||||
databaseListAllApps() { unset -f databaseListAllApps; __lpAutoload "${install_scripts_dir}database/app/db_list_all_apps.sh"; databaseListAllApps "$@"; }
|
||||
databaseListInstalledApp() { unset -f databaseListInstalledApp; __lpAutoload "${install_scripts_dir}database/app/db_list_installed_app.sh"; databaseListInstalledApp "$@"; }
|
||||
databaseListInstalledApps() { unset -f databaseListInstalledApps; __lpAutoload "${install_scripts_dir}database/app/db_list_installed_apps.sh"; databaseListInstalledApps "$@"; }
|
||||
databaseMigrateNetworkResourcesUnique() { unset -f databaseMigrateNetworkResourcesUnique; __lpAutoload "${install_scripts_dir}database/tables/db_create_tables.sh"; databaseMigrateNetworkResourcesUnique "$@"; }
|
||||
databaseOptionInsert() { unset -f databaseOptionInsert; __lpAutoload "${install_scripts_dir}database/insert/db_insert_option.sh"; databaseOptionInsert "$@"; }
|
||||
databasePortOpenInsert() { unset -f databasePortOpenInsert; __lpAutoload "${install_scripts_dir}database/insert/db_insert_port_open.sh"; databasePortOpenInsert "$@"; }
|
||||
databasePortUsedInsert() { unset -f databasePortUsedInsert; __lpAutoload "${install_scripts_dir}database/insert/db_insert_port_used.sh"; databasePortUsedInsert "$@"; }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user