- Keycloak SSO button is now the prominent primary action on the login page - Local username/password form hidden behind collapsible "Admin access" toggle - Falls back to username/password form when Keycloak is not configured - Rename container to mgmt-keycloak, backup filename to mgmt-backup Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
213 lines
7.0 KiB
Bash
Executable File
213 lines
7.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -e
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
BACKEND_DIR="$SCRIPT_DIR/backend"
|
|
FRONTEND_DIR="$SCRIPT_DIR/frontend"
|
|
PIDFILE="$SCRIPT_DIR/.dev.pids"
|
|
KC_CONTAINER="mgmt-keycloak-dev"
|
|
KC_PORT=8180
|
|
KC_URL="http://localhost:$KC_PORT"
|
|
KC_SECRET_FILE="$SCRIPT_DIR/.keycloak-secret"
|
|
KC_OIDC_SECRET_FILE="$SCRIPT_DIR/.keycloak-oidc-secret"
|
|
|
|
# ── Helpers ──────────────────────────────────────────────────────
|
|
|
|
wait_for_keycloak() {
|
|
echo "Waiting for Keycloak to be ready..."
|
|
local attempts=0
|
|
local max_attempts=60
|
|
while [ $attempts -lt $max_attempts ]; do
|
|
if curl -sf "$KC_URL/realms/master" >/dev/null 2>&1; then
|
|
echo "Keycloak is ready."
|
|
return 0
|
|
fi
|
|
attempts=$((attempts + 1))
|
|
sleep 2
|
|
done
|
|
echo "ERROR: Keycloak failed to start within $((max_attempts * 2)) seconds."
|
|
exit 1
|
|
}
|
|
|
|
ensure_venv() {
|
|
if [ ! -d "$BACKEND_DIR/.venv" ]; then
|
|
echo "Creating backend virtual environment..."
|
|
python3 -m venv "$BACKEND_DIR/.venv"
|
|
echo "Installing backend dependencies..."
|
|
"$BACKEND_DIR/.venv/bin/pip" install -q -r "$BACKEND_DIR/requirements.txt"
|
|
fi
|
|
}
|
|
|
|
seed_keycloak() {
|
|
echo "Seeding Keycloak..."
|
|
local output
|
|
output=$("$BACKEND_DIR/.venv/bin/python" "$SCRIPT_DIR/scripts/seed-keycloak.py" --url "$KC_URL" 2>&1)
|
|
echo "$output"
|
|
|
|
# Extract client secret from seed output
|
|
local secret
|
|
secret=$(echo "$output" | grep 'KEYCLOAK_CLIENT_SECRET=' | tail -1 | sed 's/.*KEYCLOAK_CLIENT_SECRET=//')
|
|
if [ -n "$secret" ] && [ "$secret" != "<check Keycloak admin console>" ]; then
|
|
echo "$secret" > "$KC_SECRET_FILE"
|
|
elif [ -f "$KC_SECRET_FILE" ]; then
|
|
echo "Using previously saved client secret."
|
|
else
|
|
echo "WARNING: Could not determine client secret. Check Keycloak admin console."
|
|
fi
|
|
|
|
# Extract OIDC client secret
|
|
local oidc_secret
|
|
oidc_secret=$(echo "$output" | grep 'KEYCLOAK_OIDC_CLIENT_SECRET=' | tail -1 | sed 's/.*KEYCLOAK_OIDC_CLIENT_SECRET=//')
|
|
if [ -n "$oidc_secret" ] && [ "$oidc_secret" != "<check Keycloak admin console>" ]; then
|
|
echo "$oidc_secret" > "$KC_OIDC_SECRET_FILE"
|
|
elif [ -f "$KC_OIDC_SECRET_FILE" ]; then
|
|
echo "Using previously saved OIDC client secret."
|
|
else
|
|
echo "WARNING: Could not determine OIDC client secret. Check Keycloak admin console."
|
|
fi
|
|
}
|
|
|
|
# ── Stop ─────────────────────────────────────────────────────────
|
|
|
|
stop_services() {
|
|
# Stop backend/frontend
|
|
if [ -f "$PIDFILE" ]; then
|
|
while read -r pid; do
|
|
kill "$pid" 2>/dev/null || true
|
|
done < "$PIDFILE"
|
|
rm -f "$PIDFILE"
|
|
echo "Backend and frontend stopped."
|
|
else
|
|
local pids
|
|
pids=$(lsof -ti :5001,:5173 2>/dev/null || true)
|
|
if [ -n "$pids" ]; then
|
|
echo "$pids" | xargs kill 2>/dev/null || true
|
|
echo "Backend and frontend stopped."
|
|
fi
|
|
fi
|
|
|
|
# Stop keycloak container
|
|
if podman ps -q -f name="^${KC_CONTAINER}$" 2>/dev/null | grep -q .; then
|
|
podman stop "$KC_CONTAINER" >/dev/null 2>&1
|
|
podman rm "$KC_CONTAINER" >/dev/null 2>&1
|
|
echo "Keycloak stopped."
|
|
else
|
|
podman rm "$KC_CONTAINER" >/dev/null 2>&1 || true
|
|
echo "Keycloak not running."
|
|
fi
|
|
}
|
|
|
|
# ── Start ────────────────────────────────────────────────────────
|
|
|
|
start_services() {
|
|
ensure_venv
|
|
|
|
# Frontend setup
|
|
if [ ! -d "$FRONTEND_DIR/node_modules" ]; then
|
|
echo "Installing frontend dependencies..."
|
|
(cd "$FRONTEND_DIR" && npm install)
|
|
fi
|
|
|
|
# Start Keycloak (if not already running)
|
|
if ! podman ps -q -f name="^${KC_CONTAINER}$" 2>/dev/null | grep -q .; then
|
|
echo "Starting Keycloak on $KC_URL..."
|
|
podman rm "$KC_CONTAINER" >/dev/null 2>&1 || true
|
|
podman run -d --name "$KC_CONTAINER" \
|
|
-p "$KC_PORT:8080" \
|
|
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
|
|
-e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
|
|
quay.io/keycloak/keycloak:26.2 start-dev >/dev/null
|
|
else
|
|
echo "Keycloak already running on $KC_URL"
|
|
fi
|
|
|
|
wait_for_keycloak
|
|
seed_keycloak
|
|
|
|
# Read the client secrets
|
|
local kc_secret=""
|
|
if [ -f "$KC_SECRET_FILE" ]; then
|
|
kc_secret=$(cat "$KC_SECRET_FILE")
|
|
fi
|
|
local kc_oidc_secret=""
|
|
if [ -f "$KC_OIDC_SECRET_FILE" ]; then
|
|
kc_oidc_secret=$(cat "$KC_OIDC_SECRET_FILE")
|
|
fi
|
|
|
|
# Kill anything already on our ports
|
|
local existing
|
|
existing=$(lsof -ti :5001,:5173 2>/dev/null || true)
|
|
if [ -n "$existing" ]; then
|
|
echo "Stopping existing processes on ports 5001/5173..."
|
|
echo "$existing" | xargs kill 2>/dev/null || true
|
|
sleep 1
|
|
# Force kill if still around
|
|
existing=$(lsof -ti :5001,:5173 2>/dev/null || true)
|
|
if [ -n "$existing" ]; then
|
|
echo "$existing" | xargs kill -9 2>/dev/null || true
|
|
sleep 1
|
|
fi
|
|
fi
|
|
|
|
# Start backend with Keycloak env vars
|
|
echo "Starting backend on http://localhost:5001..."
|
|
(
|
|
cd "$BACKEND_DIR"
|
|
export FLASK_DEBUG=true
|
|
export KEYCLOAK_URL="$KC_URL"
|
|
export KEYCLOAK_REALM=osa
|
|
export KEYCLOAK_CLIENT_ID=osa-admin-client
|
|
export KEYCLOAK_CLIENT_SECRET="$kc_secret"
|
|
export KEYCLOAK_OIDC_CLIENT_ID=osa-web
|
|
export KEYCLOAK_OIDC_CLIENT_SECRET="$kc_oidc_secret"
|
|
exec .venv/bin/python run.py
|
|
) &
|
|
BACKEND_PID=$!
|
|
|
|
# Start frontend
|
|
echo "Starting frontend on http://localhost:5173..."
|
|
(cd "$FRONTEND_DIR" && npm run dev) &
|
|
FRONTEND_PID=$!
|
|
|
|
# Save PIDs
|
|
printf '%s\n' "$BACKEND_PID" "$FRONTEND_PID" > "$PIDFILE"
|
|
|
|
echo ""
|
|
echo "OSA Management Suite running (with Keycloak):"
|
|
echo " Frontend: http://localhost:5173"
|
|
echo " Backend: http://localhost:5001"
|
|
echo " Keycloak: $KC_URL (admin/admin)"
|
|
echo " Login: admin / admin"
|
|
echo ""
|
|
echo "Press Ctrl+C to stop backend/frontend."
|
|
echo "Use './dev.sh --stop' to stop everything including Keycloak."
|
|
|
|
cleanup() {
|
|
echo ""
|
|
echo "Shutting down backend and frontend..."
|
|
kill $BACKEND_PID $FRONTEND_PID 2>/dev/null
|
|
wait $BACKEND_PID $FRONTEND_PID 2>/dev/null
|
|
rm -f "$PIDFILE"
|
|
echo "Done. Keycloak is still running — use './dev.sh --stop' to stop it."
|
|
}
|
|
trap cleanup EXIT INT TERM
|
|
|
|
wait
|
|
}
|
|
|
|
# ── Entrypoint ───────────────────────────────────────────────────
|
|
|
|
case "${1:-}" in
|
|
--stop)
|
|
stop_services
|
|
;;
|
|
--restart)
|
|
stop_services
|
|
sleep 1
|
|
start_services
|
|
;;
|
|
*)
|
|
start_services
|
|
;;
|
|
esac
|