A free, open, self-hosted app platform (GNU AGPLv3): one-click app deploys, Traefik reverse proxy with automatic SSL, rootless Docker support, gluetun VPN routing, and a web dashboard to manage it all. Free & open forever to self-host; optional paid hosted services fund it. See PROMISE.md. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: librelad <librelad@digitalangels.vip>
116 lines
5.1 KiB
Bash
116 lines
5.1 KiB
Bash
#!/bin/bash
|
|
|
|
# Bookstack stores users in MySQL/SQLite via Laravel/Eloquent. We use
|
|
# `php artisan tinker` to call the same User model the web UI does, so
|
|
# password hashing + role assignment match exactly what Bookstack expects.
|
|
|
|
_bookstackArtisan() {
|
|
local container="bookstack"
|
|
local php_body="$1"
|
|
shift
|
|
sudo docker exec -i -w /app/www "$@" "$container" php artisan tinker --execute="$php_body" 2>&1
|
|
}
|
|
|
|
authAdapter_bookstack_setPassword() {
|
|
local email="$1" password="$2"
|
|
[[ -z "$email" ]] && { isError "Email is required."; return 1; }
|
|
[[ -z "$password" ]] && password=$(generateRandomPassword)
|
|
|
|
local out
|
|
out=$(_bookstackArtisan '
|
|
$u = \BookStack\Users\Models\User::where("email", getenv("EZ_EMAIL"))->first();
|
|
if (!$u) { echo "EZ_USER_NOT_FOUND"; exit; }
|
|
$u->password = bcrypt(getenv("EZ_PASS"));
|
|
$u->save();
|
|
echo "EZ_RESET_OK:" . $u->name;
|
|
' -e "EZ_EMAIL=$email" -e "EZ_PASS=$password")
|
|
if echo "$out" | grep -q EZ_USER_NOT_FOUND; then
|
|
isError "No Bookstack user with email '$email'."; return 1
|
|
fi
|
|
if ! echo "$out" | grep -q EZ_RESET_OK; then
|
|
isError "Bookstack reset failed: $out"; return 1
|
|
fi
|
|
|
|
if [[ "$email" == "${CFG_BOOKSTACK_ADMIN_USER:-}" ]]; then
|
|
authPersistCfg bookstack ADMIN_PASSWORD "$password"
|
|
fi
|
|
isSuccessful "Bookstack password set for $email — New password: $password"
|
|
}
|
|
|
|
authAdapter_bookstack_createUser() {
|
|
local email="$1" password="$2" name="$3" isAdmin="$4"
|
|
[[ -z "$email" ]] && { isError "Email is required."; return 1; }
|
|
[[ -z "$name" ]] && name="${email%@*}"
|
|
[[ -z "$password" ]] && password=$(generateRandomPassword)
|
|
local roleSlug="public"
|
|
[[ "$isAdmin" == "true" ]] && roleSlug="admin"
|
|
|
|
local out
|
|
out=$(_bookstackArtisan '
|
|
if (\BookStack\Users\Models\User::where("email", getenv("EZ_EMAIL"))->exists()) { echo "EZ_USER_EXISTS"; exit; }
|
|
$u = new \BookStack\Users\Models\User();
|
|
$u->name = getenv("EZ_NAME"); $u->email = getenv("EZ_EMAIL");
|
|
$u->password = bcrypt(getenv("EZ_PASS")); $u->email_confirmed = true;
|
|
$base = preg_replace("/[^a-z0-9]+/", "-", strtolower($u->name ?: explode("@", $u->email)[0]));
|
|
$base = trim($base, "-"); if ($base === "") $base = "user";
|
|
$slug = $base; $i = 1;
|
|
while (\BookStack\Users\Models\User::where("slug", $slug)->exists()) { $i++; $slug = $base . "-" . $i; }
|
|
$u->slug = $slug;
|
|
$u->save();
|
|
$role = \BookStack\Users\Models\Role::where("system_name", getenv("EZ_ROLE"))->first();
|
|
if ($role) $u->attachRole($role);
|
|
echo "EZ_USER_CREATED:" . $u->id;
|
|
' -e "EZ_EMAIL=$email" -e "EZ_NAME=$name" -e "EZ_PASS=$password" -e "EZ_ROLE=$roleSlug")
|
|
if echo "$out" | grep -q EZ_USER_EXISTS; then isError "User '$email' already exists."; return 1; fi
|
|
if ! echo "$out" | grep -q EZ_USER_CREATED; then isError "Bookstack create failed: $out"; return 1; fi
|
|
|
|
if [[ "$isAdmin" == "true" && -z "${CFG_BOOKSTACK_ADMIN_USER:-}" ]]; then
|
|
authPersistCfg bookstack ADMIN_USER "$email"
|
|
authPersistCfg bookstack ADMIN_PASSWORD "$password"
|
|
fi
|
|
isSuccessful "Bookstack user created — Email: $email — Password: $password"
|
|
}
|
|
|
|
authAdapter_bookstack_listUsers() {
|
|
_bookstackArtisan '
|
|
foreach (\BookStack\Users\Models\User::all() as $u) {
|
|
$roles = $u->roles->pluck("display_name")->join(",");
|
|
echo "EZ_USER\t" . $u->email . "\t" . $u->name . "\t" . ($roles ?: "—") . "\n";
|
|
}
|
|
'
|
|
}
|
|
|
|
authAdapter_bookstack_deleteUser() {
|
|
local email="$1"
|
|
[[ -z "$email" ]] && { isError "Email is required."; return 1; }
|
|
local out
|
|
out=$(_bookstackArtisan '
|
|
$u = \BookStack\Users\Models\User::where("email", getenv("EZ_EMAIL"))->first();
|
|
if (!$u) { echo "EZ_USER_NOT_FOUND"; exit; }
|
|
$u->delete();
|
|
echo "EZ_USER_DELETED";
|
|
' -e "EZ_EMAIL=$email")
|
|
echo "$out" | grep -q EZ_USER_NOT_FOUND && { isError "No Bookstack user with email '$email'."; return 1; }
|
|
echo "$out" | grep -q EZ_USER_DELETED || { isError "Bookstack delete failed: $out"; return 1; }
|
|
isSuccessful "Bookstack user '$email' deleted."
|
|
}
|
|
|
|
authAdapter_bookstack_setAdmin() {
|
|
local email="$1" isAdmin="$2"
|
|
[[ -z "$email" ]] && { isError "Email is required."; return 1; }
|
|
local target="${isAdmin}"; [[ "$target" != "true" ]] && target="false"
|
|
local out
|
|
out=$(_bookstackArtisan '
|
|
$u = \BookStack\Users\Models\User::where("email", getenv("EZ_EMAIL"))->first();
|
|
if (!$u) { echo "EZ_USER_NOT_FOUND"; exit; }
|
|
$admin = \BookStack\Users\Models\Role::where("system_name", "admin")->first();
|
|
if (!$admin) { echo "EZ_ROLE_NOT_FOUND"; exit; }
|
|
if (getenv("EZ_TARGET") === "true") { $u->attachRole($admin); }
|
|
else { $u->detachRole($admin); }
|
|
echo "EZ_OK";
|
|
' -e "EZ_EMAIL=$email" -e "EZ_TARGET=$target")
|
|
echo "$out" | grep -q EZ_USER_NOT_FOUND && { isError "No Bookstack user '$email'."; return 1; }
|
|
echo "$out" | grep -q EZ_OK || { isError "Bookstack role change failed: $out"; return 1; }
|
|
isSuccessful "Bookstack user '$email' admin → $target."
|
|
}
|