The main sweep — ~260 call sites across ~100 files move from string
concatenation on a single root to appDir/storageAppDirs/storageAppConfigs.
On a single-root install the resolved paths are identical, so this is a
no-op until a location is registered.
Enumerators were the interesting half. `for d in "$containers_dir"/*/`
appears in the menus, the registry/artifact scanners and the DNS setup —
and a shell glob cannot list a rootless 751 tree at all, which is the
same bug config_find_file.sh already documents in a comment. Routing them
through storageAppDirs (which enumerates as the owning user) fixes that
alongside the multi-root work.
Three places needed judgement rather than substitution:
db_app_scan.sh deletes database rows and port allocations for apps whose
folder is missing, and reaps "empty" app dirs. With a storage location
unmounted, every app on it looks exactly like that. Each of those
branches now gates on appStorageAvailable first — an app on an unplugged
drive is skipped with a notice, never deleted.
instance_create.sh rewrites cloned hooks so an instance touches its own
directory instead of the base app's. Its sed matched ${containers_dir}<type>,
which this sweep just replaced with $(appDir <type>) — so it would have
silently stopped redirecting, and an instance would have written to the
original's files (the adguard auth adapter case its own comment warns
about). Now matches both appDir forms, verified against bare, quoted,
unrelated-app, legacy and prose cases.
peer_shell/peer_pull streamed and extracted relative to the primary root.
Both now use the app's own root, and peer_shell keeps a single-root
fallback since it runs as a restricted SSH shell with no LibrePortal env.
Also fixes a pre-existing bug found on the way: webui_app_config.sh
tested "$containers_dir/frontend/data/last_update", one level short of the
real tree under the libreportal app dir, so the WebUI refresh trigger
after a config update has never once fired.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
100 lines
4.5 KiB
Bash
100 lines
4.5 KiB
Bash
#!/bin/bash
|
|
|
|
# Bookstack install hooks — drive the post-start admin account bootstrap.
|
|
# Generic installApp driver handles compose / start / db / monitoring; this
|
|
# adds the readiness probe + first-admin provisioning the original
|
|
# installBookstack() did inline.
|
|
|
|
bookstack_install_post_start()
|
|
{
|
|
local app_name="$1"
|
|
|
|
local bookstack_target_email="${CFG_BOOKSTACK_ADMIN_EMAIL:-admin@admin.com}"
|
|
local bookstack_target_pass="${CFG_BOOKSTACK_ADMIN_PASSWORD_1:-password}"
|
|
|
|
local bookstack_compose_file="$(appDir "$app_name")/docker-compose.yml"
|
|
local bookstack_port_pair
|
|
bookstack_port_pair=$(tagsManagerGetTagContent "$bookstack_compose_file" "PORTS_TAG_1")
|
|
local bookstack_host_port="${bookstack_port_pair%%:*}"
|
|
local bookstack_probe_url="http://127.0.0.1:${bookstack_host_port}/login"
|
|
|
|
isNotice "Waiting for Bookstack to come online at ${bookstack_probe_url} ..."
|
|
isNotice "This may take up to 20 seconds, please wait..."
|
|
|
|
local bookstack_attempts=0
|
|
local bookstack_ready=0
|
|
local bookstack_http_code
|
|
while ((bookstack_attempts < 60)); do
|
|
bookstack_http_code=$(curl -sS -o /dev/null --max-time 3 -w '%{http_code}' "$bookstack_probe_url" 2>/dev/null)
|
|
if [[ "$bookstack_http_code" =~ ^(200|302)$ ]]; then
|
|
bookstack_ready=1
|
|
break
|
|
fi
|
|
sleep 2
|
|
((bookstack_attempts++))
|
|
done
|
|
|
|
if ((bookstack_ready == 0)); then
|
|
isNotice "Bookstack did not respond on ${bookstack_probe_url} within $((60 * 2))s — admin account left at upstream defaults."
|
|
echo ""
|
|
isNotice "Bookstack admin login (default):"
|
|
echo ""
|
|
echo " Email : admin@admin.com"
|
|
echo " Password : password"
|
|
echo ""
|
|
return 0
|
|
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" \
|
|
"$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 "$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
|
|
isSuccessful "Removed seeded admin@admin.com account."
|
|
fi
|
|
|
|
echo ""
|
|
isNotice "Bookstack admin login:"
|
|
echo ""
|
|
echo " Email : ${bookstack_target_email}"
|
|
echo " Password : ${bookstack_target_pass}"
|
|
echo ""
|
|
elif [[ "$bookstack_create_output" == *"already exists"* ]]; then
|
|
# Reinstall over existing data: the account is already provisioned, so
|
|
# create-admin refuses. NOT a failure, and printing the upstream
|
|
# defaults here would be actively wrong — those credentials were
|
|
# replaced on the first install, and the password in the config was
|
|
# never re-applied to the live account.
|
|
isSuccessful "Bookstack admin account already exists (email: $bookstack_target_email) — kept as-is."
|
|
isNotice "Its password was NOT reset by this install. Sign in with the credentials from the original install, or reset from inside Bookstack."
|
|
echo ""
|
|
else
|
|
isNotice "Bookstack admin auto-create failed (exit $bookstack_create_rc). Output:"
|
|
echo "$bookstack_create_output" | sed 's/^/ /'
|
|
echo ""
|
|
isNotice "Falling back to upstream defaults — update from inside Bookstack."
|
|
echo ""
|
|
isNotice "Bookstack admin login (default):"
|
|
echo ""
|
|
echo " Email : admin@admin.com"
|
|
echo " Password : password"
|
|
echo ""
|
|
fi
|
|
}
|