Files
bookshelf/server/create-user.sh
T
claude 6c17e42037 Baseline: wave 1A server complete, wave 1B Android scaffold + design system green
assembleDebug, testDebugUnitTest, and recordPaparazziDebug all pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bThmkmyUUdqQpy3MXFFe5
2026-09-06 01:58:37 +00:00

84 lines
3.2 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# create-user.sh — superuser-driven account creation for Bookshelf.
#
# There is no self-registration path (users.createRule = null, see
# setup-schema.sh), so the only way to create an app account is for the
# server owner to run this script as the superuser. Intended for the two
# household accounts (owner + spouse), but works for any number of users.
#
# Usage:
# ./create-user.sh <email> <password> [name]
# PB_URL=https://bookshelf.example.com ./create-user.sh alice@example.com "correct horse battery staple" Alice
#
# PB_URL, PB_EMAIL (superuser), PB_PASS (superuser) come from env, or fall
# back to ./.dev-credentials, same as setup-schema.sh.
set -u -o pipefail
NEW_EMAIL="${1:-}"
NEW_PASS="${2:-}"
NEW_NAME="${3:-}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CREDS_FILE="$SCRIPT_DIR/.dev-credentials"
log() { printf '%s\n' "$*" >&2; }
ok() { printf '\033[32m✓\033[0m %s\n' "$*" >&2; }
fail() { printf '\033[31m✗ ERROR:\033[0m %s\n' "$*" >&2; exit 1; }
command -v curl >/dev/null 2>&1 || fail "curl is required but not installed."
command -v jq >/dev/null 2>&1 || fail "jq is required but not installed."
if [[ -z "$NEW_EMAIL" || -z "$NEW_PASS" ]]; then
fail "Usage: $0 <email> <password> [name]"
fi
if [[ ${#NEW_PASS} -lt 8 ]]; then
fail "Password must be at least 8 characters (PocketBase minimum)."
fi
PB_URL="${PB_URL:-http://127.0.0.1:8090}"
PB_URL="${PB_URL%/}"
PB_EMAIL="${PB_EMAIL:-}"
PB_PASS="${PB_PASS:-}"
if [[ -z "$PB_EMAIL" || -z "$PB_PASS" ]]; then
if [[ -f "$CREDS_FILE" ]]; then
# shellcheck disable=SC1090
source "$CREDS_FILE"
PB_EMAIL="${PB_EMAIL:-${PB_SUPERUSER_EMAIL:-}}"
PB_PASS="${PB_PASS:-${PB_SUPERUSER_PASS:-}}"
fi
fi
[[ -n "$PB_EMAIL" ]] || fail "No superuser email given. Set PB_EMAIL or provide $CREDS_FILE."
[[ -n "$PB_PASS" ]] || fail "No superuser password given. Set PB_PASS or provide $CREDS_FILE."
log "Creating Bookshelf account on $PB_URL"
log " Email: $NEW_EMAIL"
AUTH_RESP="$(curl -s -w '\n%{http_code}' -X POST "$PB_URL/api/collections/_superusers/auth-with-password" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg identity "$PB_EMAIL" --arg password "$PB_PASS" '{identity:$identity, password:$password}')")"
AUTH_BODY="$(printf '%s' "$AUTH_RESP" | sed '$d')"
AUTH_CODE="$(printf '%s' "$AUTH_RESP" | tail -n1)"
[[ "$AUTH_CODE" == "200" ]] || fail "Superuser login failed (HTTP $AUTH_CODE): $AUTH_BODY"
TOKEN="$(printf '%s' "$AUTH_BODY" | jq -r '.token')"
PAYLOAD="$(jq -n \
--arg email "$NEW_EMAIL" \
--arg password "$NEW_PASS" \
--arg name "$NEW_NAME" \
'{email:$email, password:$password, passwordConfirm:$password, name:$name, emailVisibility:true, verified:true}')"
CREATE_RESP="$(curl -s -w '\n%{http_code}' -X POST "$PB_URL/api/collections/users/records" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d "$PAYLOAD")"
CREATE_BODY="$(printf '%s' "$CREATE_RESP" | sed '$d')"
CREATE_CODE="$(printf '%s' "$CREATE_RESP" | tail -n1)"
if [[ "$CREATE_CODE" != "200" ]]; then
fail "Failed to create user (HTTP $CREATE_CODE): $CREATE_BODY"
fi
USER_ID="$(printf '%s' "$CREATE_BODY" | jq -r '.id')"
ok "Created user '$NEW_EMAIL' (id: $USER_ID)"