diff --git a/docker/logto-bootstrap.sh b/docker/logto-bootstrap.sh index 3d4dfca..aa2f588 100644 --- a/docker/logto-bootstrap.sh +++ b/docker/logto-bootstrap.sh @@ -25,10 +25,18 @@ API_RESOURCE_INDICATOR="https://api.cameleer.local" API_RESOURCE_NAME="Cameleer SaaS API" # Users (configurable via env vars) +# In SaaS mode, SAAS_ADMIN_USER is the admin's email address (e.g. admin@company.com). +# The local part (before @) is used as the Logto username; the full value as primaryEmail. SAAS_ADMIN_USER="${SAAS_ADMIN_USER:-admin}" SAAS_ADMIN_PASS="${SAAS_ADMIN_PASS:-admin}" -# In SaaS mode, SAAS_ADMIN_USER is the admin's email address. -# Use it as both username and primaryEmail in Logto. +# Extract username (local part) for Logto — Logto rejects @ in usernames +if echo "$SAAS_ADMIN_USER" | grep -q '@'; then + ADMIN_USERNAME="${SAAS_ADMIN_USER%%@*}" + ADMIN_EMAIL="$SAAS_ADMIN_USER" +else + ADMIN_USERNAME="$SAAS_ADMIN_USER" + ADMIN_EMAIL="" +fi # No server config — servers are provisioned dynamically by the admin console @@ -391,20 +399,27 @@ log "API resource scopes assigned to organization roles." # ============================================================ # --- Platform Owner --- -log "Checking for platform owner user '$SAAS_ADMIN_USER'..." -ADMIN_USER_ID=$(api_get "/api/users?search=$SAAS_ADMIN_USER" | jq -r ".[] | select(.username == \"$SAAS_ADMIN_USER\") | .id") +log "Checking for platform owner user '$ADMIN_USERNAME'..." +ADMIN_USER_ID=$(api_get "/api/users?search=$ADMIN_USERNAME" | jq -r ".[] | select(.username == \"$ADMIN_USERNAME\") | .id") if [ -n "$ADMIN_USER_ID" ]; then log "Platform owner exists: $ADMIN_USER_ID" else - log "Creating platform owner '$SAAS_ADMIN_USER'..." - ADMIN_RESPONSE=$(api_post "/api/users" "{ - \"username\": \"$SAAS_ADMIN_USER\", - \"password\": \"$SAAS_ADMIN_PASS\", - \"name\": \"Platform Owner\", - \"primaryEmail\": \"$SAAS_ADMIN_USER\" - }") + # Build user JSON — include primaryEmail only if SAAS_ADMIN_USER is an email + ADMIN_USER_JSON="{\"username\": \"$ADMIN_USERNAME\", \"password\": \"$SAAS_ADMIN_PASS\", \"name\": \"Platform Owner\"" + if [ -n "$ADMIN_EMAIL" ]; then + ADMIN_USER_JSON="$ADMIN_USER_JSON, \"primaryEmail\": \"$ADMIN_EMAIL\"" + log "Creating platform owner '$ADMIN_USERNAME' (email: $ADMIN_EMAIL)..." + else + log "Creating platform owner '$ADMIN_USERNAME'..." + fi + ADMIN_USER_JSON="$ADMIN_USER_JSON}" + ADMIN_RESPONSE=$(api_post "/api/users" "$ADMIN_USER_JSON") ADMIN_USER_ID=$(echo "$ADMIN_RESPONSE" | jq -r '.id') - log "Created platform owner: $ADMIN_USER_ID" + if [ -z "$ADMIN_USER_ID" ] || [ "$ADMIN_USER_ID" = "null" ]; then + log "ERROR: Failed to create platform owner. Response: $(echo "$ADMIN_RESPONSE" | head -c 300)" + else + log "Created platform owner: $ADMIN_USER_ID" + fi fi # --- Grant SaaS admin Logto console access (admin tenant, port 3002) --- @@ -444,12 +459,12 @@ else -d "$2" "${LOGTO_ADMIN_ENDPOINT}${1}" 2>/dev/null || true } - # Check if admin user already exists on admin tenant - ADMIN_TENANT_USER_ID=$(admin_api_get "/api/users?search=$SAAS_ADMIN_USER" | jq -r ".[] | select(.username == \"$SAAS_ADMIN_USER\") | .id" 2>/dev/null) + # Check if admin user already exists on admin tenant (uses ADMIN_USERNAME, not email) + ADMIN_TENANT_USER_ID=$(admin_api_get "/api/users?search=$ADMIN_USERNAME" | jq -r ".[] | select(.username == \"$ADMIN_USERNAME\") | .id" 2>/dev/null) if [ -z "$ADMIN_TENANT_USER_ID" ] || [ "$ADMIN_TENANT_USER_ID" = "null" ]; then - log "Creating admin console user '$SAAS_ADMIN_USER'..." + log "Creating admin console user '$ADMIN_USERNAME'..." ADMIN_TENANT_RESPONSE=$(admin_api_post "/api/users" "{ - \"username\": \"$SAAS_ADMIN_USER\", + \"username\": \"$ADMIN_USERNAME\", \"password\": \"$SAAS_ADMIN_PASS\", \"name\": \"Platform Admin\" }") diff --git a/installer b/installer index 21ea951..531a173 160000 --- a/installer +++ b/installer @@ -1 +1 @@ -Subproject commit 21ea9515a2e0a8e2deabf637331c1fdf62faf147 +Subproject commit 531a17397be1ab8c6bcbe279f717e147bfd77f4e