#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" STACK_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" INSTALL_DIR="/opt/server-ui" SERVICE_NAME="server-ui.service" UI_PORT=8091 CONTROL_PLANE_ENV="/opt/control-plane/.env" if [[ "${EUID}" -ne 0 ]]; then echo "Run as root: sudo ${SCRIPT_DIR}/install.sh" exit 1 fi free_ui_port() { pkill -f "stacks/server-ui/.venv/bin/python app.py" 2>/dev/null || true pkill -f "SERVER_UI_PORT=18091" 2>/dev/null || true local pids pids="$(ss -tlnp "sport = :${UI_PORT}" 2>/dev/null | grep -oP 'pid=\K[0-9]+' | sort -u || true)" for pid in ${pids}; do local cmd cmd="$(ps -p "${pid}" -o cmd= 2>/dev/null || true)" if [[ "${cmd}" == *"stacks/server-ui"* ]]; then echo "Stopping stale dev server-ui on :${UI_PORT} (pid ${pid})" kill "${pid}" 2>/dev/null || true fi done sleep 1 } # Disable Docker deployment if switching to native if [[ -f "${STACK_DIR}/docker-compose.yml" ]]; then (cd "${STACK_DIR}" && docker compose --profile server-ui down 2>/dev/null) || true fi echo "=== Server UI — install (native / systemd) ===" bash "${SCRIPT_DIR}/setup-control-plane-env.sh" apt-get update -qq apt-get install -y python3-venv python3-pip mkdir -p "${INSTALL_DIR}" rsync -a --delete \ --exclude '.venv' \ --exclude '.env' \ --exclude '__pycache__' \ "${STACK_DIR}/" "${INSTALL_DIR}/" python3 -m venv "${INSTALL_DIR}/.venv" "${INSTALL_DIR}/.venv/bin/pip" install --upgrade pip -q "${INSTALL_DIR}/.venv/bin/pip" install -r "${INSTALL_DIR}/requirements.txt" -q install -m 644 "${INSTALL_DIR}/server-ui.service" "/etc/systemd/system/${SERVICE_NAME}" systemctl daemon-reload systemctl enable "${SERVICE_NAME}" free_ui_port systemctl restart "${SERVICE_NAME}" sleep 2 LAN_IP="$(hostname -I 2>/dev/null | awk '{print $1}')" API_KEY_VAL="$(grep '^API_KEY=' "${CONTROL_PLANE_ENV}" | cut -d= -f2- || true)" echo "" echo "Installed to ${INSTALL_DIR}" echo "Service: $(systemctl is-active "${SERVICE_NAME}" 2>/dev/null || echo unknown)" echo "Env: ${CONTROL_PLANE_ENV}" # shellcheck source=print-api-key-instructions.sh source "${SCRIPT_DIR}/print-api-key-instructions.sh" print_api_key_instructions "${CONTROL_PLANE_ENV}" "${UI_PORT}" if [[ -n "${API_KEY_VAL}" ]]; then HEALTH="$(curl -sf "http://127.0.0.1:${UI_PORT}/api/gpu-fan/health" -H "X-API-Key: ${API_KEY_VAL}" 2>/dev/null || echo '{"ok":false}')" echo "GPU Fan proxy health: ${HEALTH}" if ! echo "${HEALTH}" | grep -q '"ok":true'; then echo "WARN: gpu-fan agent may be down — run: sudo ${STACK_DIR%/server-ui}/gpu-fan/scripts/install.sh" fi fi echo "" echo "Logs:" echo " journalctl -u ${SERVICE_NAME} -f" echo "" echo "Unified installer (gpu-fan + server-ui):" echo " sudo ${SCRIPT_DIR}/install-control-plane.sh"