Files
opsxapi/run.sh
T
logan dec66db5aa feat: OpsxAPI v1.0 全协议接口工作台
- 六协议调试:HTTP/GraphQL/WebSocket/SSE/Socket.IO/MQTT
- 集合树/多标签/多环境变量/历史记录,localStorage 持久化
- 导入:OpenAPI 2/3/3.1、Postman、Apifox、Apipost、cURL、HAR、
  Insomnia、Hoppscotch、Markdown(自动识别,21 个单测覆盖)
- 导出:OpenAPI 3.0 JSON/YAML、Postman v2.1、Markdown 文档、
  全量备份;单请求生成 cURL/fetch/Python/Go 代码
- Gin 服务:HTTP 代理、NDJSON 流式代理(SSE 透传)、
  WebSocket 隧道(浏览器自定义握手 Header)
- 内置产品官网 /home 与用户文档站 /docs(SSR,随二进制交付)
- lucide 图标体系 + 品牌标识(Logo/favicon),明暗双主题
- 环境变量:彩色徽标切换器、管理弹窗、{{ 自动补全
2026-09-18 00:16:59 +08:00

276 lines
7.9 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# OpsxAPI 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 stop|restart|status|help
#
# 设计:一份 Vite UI,Web/GUI 共用;Gin 对 Web 刚需,对 GUI 可选。
set -euo pipefail
PROJECT_NAME="OpsxAPI"
SERVER_PORT="${OPSXAPI_SERVER_PORT:-8787}"
WEB_PORT="${OPSXAPI_WEB_PORT:-5173}"
SERVER_DIR="apps/server"
SERVER_BIN="opsxapi-server"
SERVER_LABEL="com.opsx.api.server"
WEB_LABEL="com.opsx.api.web"
DESKTOP_LABEL="com.opsx.api.desktop"
# gui 默认是否顺带起 Gin(可用 OPSXAPI_GUI_WITH_SERVER=0 关掉)
GUI_WITH_SERVER="${OPSXAPI_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="/Users/logan/.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 &
fi
}
stop_detached() {
local label="$1"
if [ "$(uname -s)" = "Darwin" ] && command -v launchctl >/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/opsxapi)
}
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
start_detached "$SERVER_LABEL" "$SERVER_LOG" \
"export PATH=\"/opt/homebrew/bin:/usr/local/bin:\$PATH\"; cd \"$SCRIPT_DIR/$SERVER_DIR\" && exec env OPSXAPI_ADDR=\":$SERVER_PORT\" 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 @opsxapi/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;先保证 5173
start_web
info "启动桌面 OpsxAPI ..."
: >"$DESKTOP_LOG"
ensure_npm
# 清掉旧的 debug 进程(二进制名 OpsxAPI)
pkill -f 'target/debug/OpsxAPI' 2>/dev/null || true
pkill -f 'target/debug/opsxapi-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/OpsxAPI' >/dev/null 2>&1 \
|| pgrep -f 'target/debug/opsxapi-desktop' >/dev/null 2>&1; then
success "桌面 OpsxAPI 已启动(Dock 名应为 OpsxAPI)"
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/OpsxAPI' 2>/dev/null || true
pkill -f 'target/debug/opsxapi-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/OpsxAPI' >/dev/null 2>&1 \
|| pgrep -f 'target/debug/opsxapi-desktop' >/dev/null 2>&1; then
success "gui OpsxAPI UP"
else
warning "gui OpsxAPI DOWN"
fi
}
usage() {
cat <<USAGE
$PROJECT_NAME 启动脚本(场景 profile)
./run.sh # 同 web
./run.sh web # 浏览器:Gin + Vite
./run.sh gui # 桌面:Vite + Tauri(默认也起 Gin,OPSXAPI_GUI_WITH_SERVER=0 可关)
./run.sh all # Gin + Vite + Desktop
./run.sh server # 只 Gin
./run.sh web-only # 只 Vite
./run.sh desktop # 只 Desktop(会确保 Vite)
./run.sh stop | restart | status | help
端口: server $SERVER_PORT / web $WEB_PORT
日志: logs/{server,web,desktop}.log
USAGE
}
start_web_scene() {
start_server
start_web
echo
success "Web 场景就绪 → http://127.0.0.1:$WEB_PORT"
}
start_gui_scene() {
if [ "$GUI_WITH_SERVER" = "1" ]; then
start_server
else
info "跳过 Gin(OPSXAPI_GUI_WITH_SERVER=0);桌面 HTTP 走 tauri-native"
fi
start_desktop
echo
success "GUI 场景就绪 → 桌面窗口 OpsxAPI"
}
start_all_scene() {
start_server
start_web
start_desktop
echo
success "All 就绪:Web + Desktop + Gin"
}
stop_all() {
stop_desktop || true
stop_web || true
stop_server || true
}
cmd="${1:-web}"
case "$cmd" in
web|start) start_web_scene ;;
gui|desktop-scene) start_gui_scene ;;
all) start_all_scene ;;
server) start_server ;;
web-only) start_web ;;
desktop|gui-only) start_desktop ;;
stop) stop_all ;;
restart)
stop_all || true
start_web_scene
;;
status) status ;;
help|-h|--help) usage ;;
*) error "未知命令: $cmd"; usage; exit 1 ;;
esac