#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" PLATFORM_REPO_DIR="${PLATFORM_REPO_DIR:-$ROOT_DIR/../unrip3}" KUBECONFIG_PATH="${KUBECONFIG_PATH:-${KUBECONFIG:-$PLATFORM_REPO_DIR/.state/hetzner/kubeconfig.yaml}}" NAMESPACE="${OPERATOR_DASHBOARD_DEV_NAMESPACE:-unrip}" SERVICE_NAME="${OPERATOR_DASHBOARD_DEV_SERVICE:-operator-dashboard}" UPSTREAM_PORT="${OPERATOR_DASHBOARD_DEV_UPSTREAM_PORT:-18090}" SERVICE_PORT="${OPERATOR_DASHBOARD_DEV_SERVICE_PORT:-8090}" DEV_HOST="${OPERATOR_DASHBOARD_DEV_HOST:-0.0.0.0}" DEV_PORT="${OPERATOR_DASHBOARD_DEV_PORT:-5173}" USERNAME="${OPERATOR_DASHBOARD_DEV_USERNAME:-admin}" UPSTREAM_HEALTH_TIMEOUT_SECONDS="${OPERATOR_DASHBOARD_DEV_HEALTH_TIMEOUT_SECONDS:-2}" EXISTING_UPSTREAM_PROBE_TIMEOUT_SECONDS="${OPERATOR_DASHBOARD_DEV_EXISTING_PORT_TIMEOUT_SECONDS:-0.5}" UPSTREAM_READY_ATTEMPTS="${OPERATOR_DASHBOARD_DEV_READY_ATTEMPTS:-50}" UPSTREAM_READY_SLEEP_SECONDS="${OPERATOR_DASHBOARD_DEV_READY_SLEEP_SECONDS:-0.2}" KUBECTL_REQUEST_TIMEOUT="${OPERATOR_DASHBOARD_DEV_KUBECTL_REQUEST_TIMEOUT:-10s}" PORT_FORWARD_LOG="${OPERATOR_DASHBOARD_DEV_PORT_FORWARD_LOG:-/tmp/operator-dashboard-dev-port-forward.log}" if [[ -z "${OPERATOR_DASHBOARD_DEV_BASIC_AUTH_PASSWORD:-}" ]]; then if ! command -v pass >/dev/null 2>&1; then echo "pass is required unless OPERATOR_DASHBOARD_DEV_BASIC_AUTH_PASSWORD is set" >&2 exit 1 fi OPERATOR_DASHBOARD_DEV_BASIC_AUTH_PASSWORD="$(pass unrip/dashboard)" fi export OPERATOR_DASHBOARD_DEV_BASIC_AUTH_HEADER="Basic $(printf '%s:%s' "$USERNAME" "$OPERATOR_DASHBOARD_DEV_BASIC_AUTH_PASSWORD" | base64 | tr -d '\n')" export OPERATOR_DASHBOARD_DEV_UPSTREAM_URL="http://127.0.0.1:${UPSTREAM_PORT}" export OPERATOR_DASHBOARD_DEV_HOST="$DEV_HOST" export OPERATOR_DASHBOARD_DEV_PORT="$DEV_PORT" cleanup() { if [[ -n "${PORT_FORWARD_SUPERVISOR_PID:-}" ]]; then pkill -P "$PORT_FORWARD_SUPERVISOR_PID" >/dev/null 2>&1 || true kill "$PORT_FORWARD_SUPERVISOR_PID" >/dev/null 2>&1 || true wait "$PORT_FORWARD_SUPERVISOR_PID" >/dev/null 2>&1 || true fi } trap cleanup EXIT INT TERM maintain_port_forward() { while true; do KUBECONFIG="$KUBECONFIG_PATH" kubectl --request-timeout="$KUBECTL_REQUEST_TIMEOUT" port-forward -n "$NAMESPACE" "svc/${SERVICE_NAME}" "${UPSTREAM_PORT}:${SERVICE_PORT}" || true sleep 1 done } upstream_ready() { local timeout_seconds="${1:-$UPSTREAM_HEALTH_TIMEOUT_SECONDS}" curl -sf \ --connect-timeout "$timeout_seconds" \ --max-time "$timeout_seconds" \ "${OPERATOR_DASHBOARD_DEV_UPSTREAM_URL}/healthz" >/dev/null 2>&1 } port_in_use() { timeout 1 bash -c ":/dev/null 2>&1 } if port_in_use; then if upstream_ready "$EXISTING_UPSTREAM_PROBE_TIMEOUT_SECONDS"; then echo "reusing existing operator-dashboard upstream on port ${UPSTREAM_PORT}" >&2 else echo "port ${UPSTREAM_PORT} is already in use but /healthz did not respond; stop the stale port-forward or set OPERATOR_DASHBOARD_DEV_UPSTREAM_PORT" >&2 exit 1 fi cd "$ROOT_DIR" exec node_modules/.bin/vite --config vite.operator-dashboard.config.mjs --host "$DEV_HOST" --port "$DEV_PORT" fi : >"$PORT_FORWARD_LOG" maintain_port_forward >>"$PORT_FORWARD_LOG" 2>&1 & PORT_FORWARD_SUPERVISOR_PID=$! for _ in $(seq 1 "$UPSTREAM_READY_ATTEMPTS"); do if upstream_ready; then break fi if grep -q "address already in use" "$PORT_FORWARD_LOG"; then break fi sleep "$UPSTREAM_READY_SLEEP_SECONDS" done if ! upstream_ready; then echo "operator-dashboard upstream did not become ready; see $PORT_FORWARD_LOG" >&2 if grep -q "address already in use" "$PORT_FORWARD_LOG"; then echo "port ${UPSTREAM_PORT} is already in use; stop the old port-forward or set OPERATOR_DASHBOARD_DEV_UPSTREAM_PORT" >&2 fi exit 1 fi cd "$ROOT_DIR" exec node_modules/.bin/vite --config vite.operator-dashboard.config.mjs --host "$DEV_HOST" --port "$DEV_PORT"