#!/usr/bin/env bash # Install Server UI as Docker container (profile server-ui). set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" STACK_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" CONTROL_PLANE_STACK="$(cd "${STACK_DIR}/../control-plane" && pwd)" SERVICE_NAME="server-ui.service" UI_PORT="${SERVER_UI_PORT:-8091}" ENV_FILE="${CONTROL_PLANE_STACK}/.env" if [[ "${EUID}" -ne 0 ]]; then echo "Run as root: sudo ${SCRIPT_DIR}/install-docker.sh" exit 1 fi set_env_var() { local file="$1" key="$2" val="$3" if grep -q "^${key}=" "${file}"; then sed -i "s|^${key}=.*|${key}=${val}|" "${file}" else echo "${key}=${val}" >> "${file}" fi } detect_repo_root() { if [[ -n "${REPO_ROOT:-}" ]]; then echo "${REPO_ROOT}" return fi local candidate candidate="$(cd "${STACK_DIR}/../.." && pwd)" if [[ -d "${candidate}/stacks/localai" ]]; then echo "${candidate}" return fi echo "${candidate}" } echo "=== Server UI — install (Docker) ===" if ! command -v docker >/dev/null 2>&1; then echo "ERROR: docker not found. Install Docker CE first." exit 1 fi bash "${SCRIPT_DIR}/setup-control-plane-env.sh" REPO_ROOT_VAL="$(detect_repo_root)" DOCKER_GID="$(getent group docker | cut -d: -f3 || echo 999)" if [[ ! -f "${ENV_FILE}" ]]; then cp "${CONTROL_PLANE_STACK}/.env.example" "${ENV_FILE}" fi set_env_var "${ENV_FILE}" "REPO_ROOT" "${REPO_ROOT_VAL}" set_env_var "${ENV_FILE}" "SERVER_UI_HOST" "0.0.0.0" set_env_var "${ENV_FILE}" "SERVER_UI_PORT" "${UI_PORT}" set_env_var "${ENV_FILE}" "GPU_FAN_AGENT_URL" "http://host.docker.internal:18090" set_env_var "${ENV_FILE}" "DOCKER_GID" "${DOCKER_GID}" # Disable native systemd if present if systemctl is-enabled "${SERVICE_NAME}" &>/dev/null; then echo "Disabling native ${SERVICE_NAME}..." systemctl disable --now "${SERVICE_NAME}" 2>/dev/null || true fi cd "${STACK_DIR}" export REPO_ROOT="${REPO_ROOT_VAL}" export DOCKER_GID echo "Building server-ui image..." docker compose --profile server-ui build echo "Starting server-ui container..." docker compose --profile server-ui up -d sleep 2 LAN_IP="$(hostname -I 2>/dev/null | awk '{print $1}')" API_KEY_VAL="$(grep '^API_KEY=' "${ENV_FILE}" | cut -d= -f2- || true)" echo "" echo "Runtime: Docker (profile server-ui)" echo "Repo mount: ${REPO_ROOT_VAL} → /repo" echo "Env: ${ENV_FILE}" echo "Container: $(docker compose --profile server-ui ps --format '{{.Name}} {{.Status}}' 2>/dev/null || docker ps --filter name=server-ui --format '{{.Names}} {{.Status}}')" echo "" echo "Web UI (LAN):" echo " http://${LAN_IP:-}:${UI_PORT}/" echo "" echo "API key:" echo " grep ^API_KEY= ${ENV_FILE}" echo "" if [[ -n "${API_KEY_VAL}" ]]; then HEALTH="$(curl -sf "http://127.0.0.1:${UI_PORT}/api/health" 2>/dev/null || echo '{"ok":false}')" echo "Health: ${HEALTH}" GF_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: ${GF_HEALTH}" fi echo "" echo "Logs:" echo " docker compose --profile server-ui logs -f" echo "Stop:" echo " docker compose --profile server-ui down"