#!/usr/bin/env bash # OpsxDB launcher — scene profiles, not random process soup # # ./run.sh # = web # ./run.sh web # Gin 代理 + Vite(浏览器) # ./run.sh gui # Vite + Tauri 桌面(Gin 可选;默认也拉起方便对照) # ./run.sh all # Gin + Vite + Desktop # ./run.sh server|web-only|desktop # 单组件 # ./run.sh package # Tauri 安装包 + sidecar # ./run.sh stop|restart|status|help # # 设计:一份 Vite UI,Web/GUI 共用;Gin 对 Web 刚需,对 GUI 可选。 set -euo pipefail PROJECT_NAME="OpsxDB" SERVER_PORT="${OPSXDB_SERVER_PORT:-8788}" WEB_PORT="${OPSXDB_WEB_PORT:-5174}" SERVER_DIR="apps/server" SERVER_BIN="opsxdb-server" SERVER_LABEL="com.opsx.db.server" WEB_LABEL="com.opsx.db.web" DESKTOP_LABEL="com.opsx.db.desktop" # gui 默认是否顺带起 Gin(可用 OPSXDB_GUI_WITH_SERVER=0 关掉) GUI_WITH_SERVER="${OPSXDB_GUI_WITH_SERVER:-1}" RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m' info() { echo -e "${BLUE}[INFO]${NC} $*"; } success() { echo -e "${GREEN}[OK]${NC} $*"; } warning() { echo -e "${YELLOW}[WARN]${NC} $*"; } error() { echo -e "${RED}[ERR]${NC} $*"; } SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" LOG_DIR="$SCRIPT_DIR/logs" mkdir -p "$LOG_DIR" "$SCRIPT_DIR/$SERVER_DIR/bin" SERVER_LOG="$LOG_DIR/server.log" WEB_LOG="$LOG_DIR/web.log" DESKTOP_LOG="$LOG_DIR/desktop.log" export PATH="${HOME}/.nvm/versions/node/v24.19.0/bin:/opt/homebrew/bin:/usr/local/bin:${HOME}/.cargo/bin:$PATH" check_port() { lsof -ti:"$1" 2>/dev/null | head -1 || true; } kill_port() { local pids pids="$(lsof -ti:"$1" 2>/dev/null || true)" if [ -n "$pids" ]; then info "释放端口 $1" # shellcheck disable=SC2086 kill -9 $pids 2>/dev/null || true sleep 0.3 fi } start_detached() { local label="$1" log_file="$2" command="$3" if [ "$(uname -s)" = "Darwin" ] && command -v launchctl >/dev/null 2>&1; then launchctl remove "$label" 2>/dev/null || true launchctl submit -l "$label" -o "$log_file" -e "$log_file" -- /bin/bash -lc "$command" else nohup /bin/bash -lc "$command" >>"$log_file" 2>&1 /dev/null 2>&1; then launchctl remove "$label" 2>/dev/null || true fi } ensure_npm() { if [ ! -d "$SCRIPT_DIR/node_modules" ]; then warning "缺少 node_modules,正在 npm install..." npm install fi if [ ! -d "$SCRIPT_DIR/apps/desktop/node_modules" ]; then warning "desktop 缺少依赖,正在安装..." npm --prefix "$SCRIPT_DIR/apps/desktop" install fi } build_server() { info "编译 Gin 服务..." (cd "$SCRIPT_DIR/$SERVER_DIR" && go build -o "$SCRIPT_DIR/$SERVER_DIR/bin/$SERVER_BIN" ./cmd/opsxdb) } wait_http() { local url="$1" name="$2" retries="${3:-60}" local i=0 while ! curl -fsS "$url" >/dev/null 2>&1; do i=$((i + 1)) if [ "$i" -ge "$retries" ]; then error "$name 启动超时:$url" return 1 fi sleep 0.5 done } start_server() { if curl -fsS "http://127.0.0.1:$SERVER_PORT/health" >/dev/null 2>&1; then success "后端已在运行 :$SERVER_PORT" return 0 fi info "启动后端 :$SERVER_PORT ..." : >"$SERVER_LOG" kill_port "$SERVER_PORT" build_server # 默认仅本机;对外需 OPSXDB_BIND_PUBLIC=1 local bind_env="OPSXDB_ADDR=127.0.0.1:$SERVER_PORT" if [ -n "${OPSXDB_ADDR:-}" ]; then bind_env="OPSXDB_ADDR=$OPSXDB_ADDR" elif [ "${OPSXDB_BIND_PUBLIC:-0}" = "1" ] || [ "${OPSXDB_BIND_PUBLIC:-}" = "true" ]; then bind_env="OPSXDB_BIND_PUBLIC=1" fi start_detached "$SERVER_LABEL" "$SERVER_LOG" \ "export PATH=\"/opt/homebrew/bin:/usr/local/bin:\$PATH\"; cd \"$SCRIPT_DIR/$SERVER_DIR\" && exec env $bind_env GIN_MODE=debug \"./bin/$SERVER_BIN\"" wait_http "http://127.0.0.1:$SERVER_PORT/health" "后端" success "后端 UP http://127.0.0.1:$SERVER_PORT/health" } stop_server() { stop_detached "$SERVER_LABEL" kill_port "$SERVER_PORT" success "后端已停止" } start_web() { if curl -fsS "http://127.0.0.1:$WEB_PORT/" >/dev/null 2>&1; then success "Web 已在运行 :$WEB_PORT" return 0 fi info "启动 Web UI :$WEB_PORT ..." : >"$WEB_LOG" ensure_npm kill_port "$WEB_PORT" start_detached "$WEB_LABEL" "$WEB_LOG" \ "export PATH=\"/Users/logan/.nvm/versions/node/v24.19.0/bin:/opt/homebrew/bin:/usr/local/bin:\$PATH\"; cd \"$SCRIPT_DIR\" && exec npm run dev -w @opsxdb/web" wait_http "http://127.0.0.1:$WEB_PORT/" "Web UI" 80 success "Web UP http://127.0.0.1:$WEB_PORT" } stop_web() { stop_detached "$WEB_LABEL" kill_port "$WEB_PORT" success "Web 已停止" } start_desktop() { # GUI 依赖同一份 Vite;先保证 5174 start_web info "启动桌面 OpsxDB ..." : >"$DESKTOP_LOG" ensure_npm # 清掉旧的 debug 进程(二进制名 OpsxDB) pkill -f 'target/debug/OpsxDB' 2>/dev/null || true pkill -f 'target/debug/opsxdb-desktop' 2>/dev/null || true stop_detached "$DESKTOP_LABEL" start_detached "$DESKTOP_LABEL" "$DESKTOP_LOG" \ "export PATH=\"/Users/logan/.nvm/versions/node/v24.19.0/bin:/opt/homebrew/bin:/usr/local/bin:\$HOME/.cargo/bin:\$PATH\"; cd \"$SCRIPT_DIR/apps/desktop\" && exec npm run tauri -- dev" local i=0 while true; do if pgrep -f 'target/debug/OpsxDB' >/dev/null 2>&1 \ || pgrep -f 'target/debug/opsxdb-desktop' >/dev/null 2>&1; then success "桌面 OpsxDB 已启动(Dock 名应为 OpsxDB)" return 0 fi if grep -Eiq 'error:|panicked|could not compile' "$DESKTOP_LOG" 2>/dev/null; then error "桌面启动失败,见 $DESKTOP_LOG" tail -n 40 "$DESKTOP_LOG" || true return 1 fi i=$((i + 1)) if [ "$i" -ge 120 ]; then error "桌面启动超时,见 $DESKTOP_LOG" tail -n 40 "$DESKTOP_LOG" || true return 1 fi sleep 1 done } stop_desktop() { stop_detached "$DESKTOP_LABEL" pkill -f 'target/debug/OpsxDB' 2>/dev/null || true pkill -f 'target/debug/opsxdb-desktop' 2>/dev/null || true pkill -f 'apps/desktop/node_modules/.bin/tauri' 2>/dev/null || true success "桌面已停止" } status() { echo "$PROJECT_NAME 状态" if curl -fsS "http://127.0.0.1:$SERVER_PORT/health" >/dev/null 2>&1; then success "server :$SERVER_PORT UP $(curl -fsS "http://127.0.0.1:$SERVER_PORT/health")" else warning "server :$SERVER_PORT DOWN" fi if curl -fsS "http://127.0.0.1:$WEB_PORT/" >/dev/null 2>&1; then success "web :$WEB_PORT UP http://127.0.0.1:$WEB_PORT" else warning "web :$WEB_PORT DOWN" fi if pgrep -f 'target/debug/OpsxDB' >/dev/null 2>&1 \ || pgrep -f 'target/debug/opsxdb-desktop' >/dev/null 2>&1; then success "gui OpsxDB UP" else warning "gui OpsxDB DOWN" fi } package_desktop() { info "构建桌面可安装包(含本机 sidecar)..." ensure_npm build_server local triple triple="$(rustc -vV 2>/dev/null | awk '/host:/ {print $2}')" if [ -z "$triple" ]; then error "无法检测 rustc host triple" return 1 fi local bin_dir="$SCRIPT_DIR/apps/desktop/src-tauri/binaries" mkdir -p "$bin_dir" cp "$SCRIPT_DIR/$SERVER_DIR/bin/$SERVER_BIN" "$bin_dir/opsxdb-server-$triple" chmod +x "$bin_dir/opsxdb-server-$triple" success "sidecar → binaries/opsxdb-server-$triple" ( cd "$SCRIPT_DIR/apps/desktop" export VITE_OPSXDB_API="http://127.0.0.1:$SERVER_PORT" npm run tauri -- build ) success "桌面包构建完成(见 apps/desktop/src-tauri/target/release/bundle)" } usage() { cat <