Files
logan 2c0bfafd8d 同步完整源码至远程:扁平化重构现状 + 未入库源文件
- 补入库:server/internal/db/dump.go、redis 分页/扫描预算测试、opsxdb-mcp main_test、
  packages/opsxdb-mcp package-lock、docs 审计与对比文档等 37 个新文件
- 含结构扁平化进行中的 80 处修改与 155 处旧结构删除(server/ src/ src-tauri/ 为当前有效源)
- 构建产物(bin/target/dist)与签名私钥仍按规则排除
2026-10-16 03:47:33 +08:00

391 lines
12 KiB
Bash
Executable File
Raw Permalink 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
# OpsxDB launcher — scene profiles, not random process soup
#
# ./run.sh # = web:仅 Gin 官网 + docs(:8788)
# ./run.sh web # 同上(浏览器不当客户端)
# ./run.sh gui # 桌面唯一操作入口:临时 .app(Dock 名 OpsxDB)+ 默认 Gin
# ./run.sh all # 同 gui(兼容旧命令;不再起「浏览器工作台产品」)
# ./run.sh server|desktop # 单组件
# ./run.sh package # Tauri 安装包 + sidecar
# ./run.sh stop|restart|status|help
#
# 产品形态:浏览器 = 官网/docs;操作 = 桌面 Tauri。
# src 的 Vite 仅作桌面打包/devUrl 源,不要用浏览器当工作台。
set -euo pipefail
PROJECT_NAME="OpsxDB"
SERVER_PORT="${OPSXDB_SERVER_PORT:-8788}"
WEB_PORT="${OPSXDB_WEB_PORT:-5174}"
SERVER_DIR="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"
# shellcheck source=scripts/gui-dev-app.sh
source "$SCRIPT_DIR/scripts/gui-dev-app.sh"
# node/npm 不在此强制注入,由 detect_node 按需探测(start_web/start_desktop 前调用)。
export PATH="/opt/homebrew/bin:/usr/local/bin:${HOME}/.cargo/bin:$PATH"
# detect_node 探测 node/npm:PATH 已有直接用;否则依次探测
# $NVM_DIR/versions/node、~/.nvm/versions/node(取最高版本)、homebrew、/usr/local。
# 找不到给出清晰报错并退出。结果写入 NODE_BIN_DIR 供 launchctl/nohup 子 shell 使用。
NODE_BIN_DIR=""
detect_node() {
if command -v node >/dev/null 2>&1 && command -v npm >/dev/null 2>&1; then
NODE_BIN_DIR="$(cd "$(dirname "$(command -v node)")" && pwd)"
return 0
fi
local roots=() d v best=""
[ -n "${NVM_DIR:-}" ] && roots+=("$NVM_DIR/versions/node")
roots+=("${HOME}/.nvm/versions/node")
for d in "${roots[@]}"; do
[ -d "$d" ] || continue
v="$(ls -1 "$d" 2>/dev/null | sort -V | tail -n 1)"
if [ -n "$v" ] && [ -x "$d/$v/bin/node" ]; then
best="$d/$v/bin"
break
fi
done
if [ -z "$best" ]; then
for d in /opt/homebrew/bin /usr/local/bin; do
if [ -x "$d/node" ]; then
best="$d"
break
fi
done
fi
if [ -z "$best" ]; then
error "未找到 node/npm。请先安装 Node.js(https://nodejs.org)或 nvm,或设置 NVM_DIR 后重试。"
exit 1
fi
NODE_BIN_DIR="$best"
export PATH="$NODE_BIN_DIR:$PATH"
info "使用 Node.js: $NODE_BIN_DIR"
}
check_port() { lsof -ti:"$1" 2>/dev/null | head -1 || true; }
opsxdb_health() {
curl -fsS --max-time 2 "http://127.0.0.1:$SERVER_PORT/health" 2>/dev/null \
| rg -q '"service"[[:space:]]*:[[:space:]]*"opsxdb"'
}
opsxdb_vite() {
local html
html="$(curl -fsS --max-time 2 "http://127.0.0.1:$WEB_PORT/" 2>/dev/null)" || return 1
[[ "$html" == *'<title>OpsxDB</title>'* && "$html" == *'/src/main.tsx'* ]]
}
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() {
detect_node
if [ ! -d "$SCRIPT_DIR/node_modules" ]; then
warning "缺少 node_modules,正在 npm install..."
npm install
fi
}
build_server() {
info "编译 Gin 服务..."
# -trimpath 去本地路径;-ldflags "-s -w" 去符号表/调试信息;CGO_ENABLED=0 静态化减体积
(cd "$SCRIPT_DIR/$SERVER_DIR" && CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -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 opsxdb_health; then
success "后端已在运行 :$SERVER_PORT"
return 0
fi
if [ -n "$(check_port "$SERVER_PORT")" ]; then
error ":$SERVER_PORT 已被其他进程占用,不能启动 OpsxDB Gin;不会结束该进程"
return 1
fi
info "启动后端 :$SERVER_PORT ..."
: >"$SERVER_LOG"
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"
if opsxdb_health; then
kill_port "$SERVER_PORT"
elif [ -n "$(check_port "$SERVER_PORT")" ]; then
warning ":$SERVER_PORT 由其他服务占用,未停止该进程"
fi
success "后端已停止"
}
# 仅供桌面 Tauri devUrl / 打包源;不是浏览器产品入口
start_vite_for_desktop() {
if opsxdb_vite; then
success "桌面 UI(Vite) 已在运行 :$WEB_PORT"
return 0
fi
if [ -n "$(check_port "$WEB_PORT")" ]; then
error ":$WEB_PORT 已被其他服务占用,无法启动 OpsxDB 桌面 UI;不会结束该进程"
return 1
fi
info "启动桌面 UI 源 :${WEB_PORT}(仅本机给 Tauri,勿当浏览器客户端)..."
: >"$WEB_LOG"
ensure_npm
# Vite 用 nohup:launchctl submit 在本机对 npm -w 经常交不出进程(web.log 空、40s 超时)
nohup /bin/bash -lc "export PATH=\"$NODE_BIN_DIR:/opt/homebrew/bin:/usr/local/bin:\$PATH\"; cd \"$SCRIPT_DIR\" && exec npm run dev:ui" >>"$WEB_LOG" 2>&1 </dev/null &
wait_http "http://127.0.0.1:$WEB_PORT/" "Web UI" 80
success "桌面 UI(Vite) UP http://127.0.0.1:${WEB_PORT}(请用 ./run.sh gui,勿当工作台)"
}
# 兼容旧名
start_web() { start_vite_for_desktop; }
stop_web() {
stop_detached "$WEB_LABEL"
if opsxdb_vite; then
kill_port "$WEB_PORT"
elif [ -n "$(check_port "$WEB_PORT")" ]; then
warning ":$WEB_PORT 由其他服务占用,未停止该进程"
fi
success "Web 已停止"
}
start_desktop() {
info "启动桌面 OpsxDB(临时 .app + 当前图标)..."
: >"$DESKTOP_LOG"
ensure_npm
kill_opsxdb_gui
build_server
prepare_sidecar
npm run tauri -- build --debug --no-bundle
ensure_gui_dev_app || return 1
if [ "${GUI_DEV_NEEDS_VITE:-0}" = "1" ]; then
start_vite_for_desktop
fi
open "$GUI_APP" 2>>"$DESKTOP_LOG" || { error "open 失败,见 $DESKTOP_LOG"; return 1; }
local i=0
while true; do
if gui_is_running; then
success "桌面 OpsxDB 已启动(临时 .app,Dock 用 icon.icns / 名称 OpsxDB)"
info "路径: $GUI_APP"
return 0
fi
i=$((i + 1))
if [ "$i" -ge 30 ]; then
error "桌面启动超时,见 $DESKTOP_LOG"
tail -n 40 "$DESKTOP_LOG" || true
return 1
fi
sleep 1
done
}
stop_desktop() {
kill_opsxdb_gui
success "桌面已停止"
}
status() {
echo "$PROJECT_NAME 状态"
if opsxdb_health; then
success "server :$SERVER_PORT UP $(curl -fsS "http://127.0.0.1:$SERVER_PORT/health")"
else
warning "server :$SERVER_PORT DOWN"
if [ -n "$(check_port "$SERVER_PORT")" ]; then
warning ":$SERVER_PORT 被其他进程占用;桌面可使用独立动态端口"
fi
fi
if opsxdb_vite; then
success "vite :${WEB_PORT} UP(桌面源,非浏览器产品)"
else
warning "vite :$WEB_PORT DOWN"
if [ -n "$(check_port "$WEB_PORT")" ]; then
warning ":$WEB_PORT 被其他服务占用"
fi
fi
if curl -fsS "http://127.0.0.1:$SERVER_PORT/home" >/dev/null 2>&1 \
|| curl -fsS "http://127.0.0.1:$SERVER_PORT/docs" >/dev/null 2>&1; then
success "site :$SERVER_PORT 官网/docs 可达"
fi
if gui_is_running; then
success "gui OpsxDB UP"
else
warning "gui OpsxDB DOWN"
fi
}
prepare_sidecar() {
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/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"
}
package_desktop() {
info "构建桌面可安装包(含本机 sidecar)..."
ensure_npm
build_server
prepare_sidecar
(
cd "$SCRIPT_DIR"
export VITE_OPSXDB_API="http://127.0.0.1:$SERVER_PORT"
npm run tauri -- build
)
success "桌面包构建完成(见 src-tauri/target/release/bundle)"
}
usage() {
cat <<USAGE
$PROJECT_NAME 启动脚本(产品形态:浏览器=官网/docs · 操作=桌面)
./run.sh # 同 web
./run.sh web # 仅 Gin:官网 + docs → http://127.0.0.1:$SERVER_PORT/
./run.sh gui # 桌面:临时 .app(Dock=OpsxDB / icon.icns;默认也起 Gin)
./run.sh all # 同 gui(兼容旧名)
./run.sh server # 只 Gin(官网/docs + API)
./run.sh desktop # 只 Desktop(会起本机 Vite 源)
./run.sh package # 打桌面安装包
./run.sh stop | restart | status | help
端口: Gin/官网 $SERVER_PORT · 桌面 Vite 源 ${WEB_PORT}(不对外当客户端)
日志: logs/{server,web,desktop}.log
USAGE
}
start_web_scene() {
# 产品:浏览器只开官网/docs,不起 Vite 工作台
start_server
stop_web || true
echo
success "官网/文档就绪 → http://127.0.0.1:$SERVER_PORT/ · docs → http://127.0.0.1:$SERVER_PORT/docs"
info "操作请用 ./run.sh gui(桌面)。:5174 不是产品入口。"
}
start_gui_scene() {
if [ "$GUI_WITH_SERVER" = "1" ]; then
if [ -n "$(check_port "$SERVER_PORT")" ] && ! opsxdb_health; then
warning ":$SERVER_PORT 已被其他服务占用;桌面将自起独立后端"
else
start_server
fi
else
info "跳过 Gin(OPSXDB_GUI_WITH_SERVER=0);桌面 HTTP 走 tauri-native"
fi
start_desktop
echo
success "GUI 场景就绪 → 桌面窗口 OpsxDB"
}
start_all_scene() {
# 与 gui 同:桌面操作 + Gin;不起「浏览器工作台产品」语义
start_gui_scene
}
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 ;;
prepare) build_server; prepare_sidecar ;;
package|dist) package_desktop ;;
web-only|vite)
warning "已弃用浏览器工作台。Vite 仅桌面源;请用: ./run.sh gui"
start_vite_for_desktop
;;
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