#!/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 [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 [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)"