桌面 GUI 外链修复 + 主题顺序调整(含结构扁平化进行中的工作区基线)

- 集成 tauri-plugin-opener:Cargo/lib.rs/capabilities/npm 四处接线,新增 src/openExternal.ts(桌面走 opener,浏览器回退 window.open,相对路径绝对化)
- 关于页 4 个链接、活动栏官网菜单、代理错误横幅 2 处健康检查链接全部改走 openExternal,桌面端可正常跳转
- 设置→外观 主题顺序调整为 深色/浅色/跟随系统
- release 二进制重编并验证(浏览器实测关于页链接可开文档页)
- 附带工作区中未提交的扁平化重构现状(另一会话进行中)
- homepage 落地页与文档终检通过(8787 / 与 /docs 双 200)
This commit is contained in:
logan committed 2026-10-16 03:03:36 +08:00
1 parent 6452026fd6
commit 2bb4ef7a95
299 files changed
+6059 -3322

No files matched your search

+92 -47
View File
@@ -1,21 +1,22 @@
#!/usr/bin/env bash
# OpsxAPI launcher — scene profiles, not random process soup
#
# ./run.sh # = web
# ./run.sh web # Gin 代理 + Vite(浏览器)
# ./run.sh gui # Vite + 临时 .app 桌面(Dock 用当前 icon.icns)
# ./run.sh all # Gin + Vite + Desktop
# ./run.sh # = web:仅 Gin 官网 + docs(:8787,homepage/dist)
# ./run.sh web # 同上(浏览器不当客户端)
# ./run.sh gui # 桌面唯一操作入口:临时 .app(Dock 名 OpsxAPI)+ 默认 Gin
# ./run.sh all # 同 gui
# ./run.sh server|web-only|desktop # 单组件
# ./run.sh stop|restart|status|help
#
# 设计:一份 Vite UI,Web/GUI 共用;Gin 对 Web 刚需,对 GUI 可选。
# 产品形态:浏览器 = 官网/docs(homepage/);操作 = 桌面 Tauri(src/ + src-tauri/)。
# Vite :5173 仅作桌面 WebView 源,不要用浏览器当工作台。
set -euo pipefail
PROJECT_NAME="OpsxAPI"
SERVER_PORT="${OPSXAPI_SERVER_PORT:-8787}"
WEB_PORT="${OPSXAPI_WEB_PORT:-5173}"
SERVER_DIR="apps/server"
SERVER_DIR="server"
SERVER_BIN="opsxapi-server"
SERVER_LABEL="com.opsx.api.server"
WEB_LABEL="com.opsx.api.web"
@@ -45,6 +46,35 @@ export PATH="/Users/logan/.nvm/versions/node/v24.19.0/bin:/opt/homebrew/bin:/usr
check_port() { lsof -ti:"$1" 2>/dev/null | head -1 || true; }
mock_api_ready() {
# /health and the original Mock route exist on older builds; require the access-control shape.
curl -fsS --max-time 2 "http://127.0.0.1:$SERVER_PORT/api/mock/local" 2>/dev/null \
| grep -q '"bearerRequired"'
}
stop_owned_server() {
stop_detached "$SERVER_LABEL"
local pid cwd command_line process_name
pid="$(check_port "$SERVER_PORT")"
[ -z "$pid" ] && return 0
cwd="$(lsof -a -p "$pid" -d cwd -Fn 2>/dev/null | sed -n 's/^n//p' | head -1)"
command_line="$(ps -p "$pid" -o command= 2>/dev/null || true)"
process_name="$(lsof -nP -a -p "$pid" -iTCP:"$SERVER_PORT" -sTCP:LISTEN -Fc 2>/dev/null | sed -n 's/^c//p' | head -1)"
if [ "$process_name" = "opsxapi-server" ] && { [ "$cwd" = "$SCRIPT_DIR/server" ] || [ "$cwd" = "$SCRIPT_DIR/apps/server" ]; }; then
info "停止本项目旧后端 (PID $pid)"
kill "$pid" 2>/dev/null || { error "无法停止本项目后端 (PID $pid)"; return 1; }
return 0
fi
case "$command_line:$cwd" in
*"$SCRIPT_DIR/server/bin/opsxapi-server"*|*"$SCRIPT_DIR/apps/server/bin/opsxapi-server"*|*"opsxapi-server:$SCRIPT_DIR/server"|*"opsxapi-server:$SCRIPT_DIR/apps/server")
info "停止本项目旧后端 (PID $pid)"
kill "$pid" 2>/dev/null || { error "无法停止本项目后端 (PID $pid)"; return 1; }
return 0 ;;
esac
error "端口 $SERVER_PORT 被其他进程占用 (PID $pid),未停止该进程"
return 1
}
kill_port() {
local pids
pids="$(lsof -ti:"$1" 2>/dev/null || true)"
@@ -74,13 +104,9 @@ stop_detached() {
}
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
if [ ! -d "$SCRIPT_DIR/node_modules" ] || ! npm ls --depth=0 >/dev/null 2>&1; then
warning "桌面依赖缺失或与 package.json 不一致,正在 npm install..."
npm install || { error "桌面依赖安装失败,请检查 npm registry 与网络后运行 npm install"; return 1; }
fi
}
@@ -103,13 +129,25 @@ wait_http() {
}
start_server() {
if curl -fsS "http://127.0.0.1:$SERVER_PORT/health" >/dev/null 2>&1; then
if mock_api_ready; then
success "后端已在运行 :$SERVER_PORT"
return 0
fi
if [ -n "$(check_port "$SERVER_PORT")" ]; then
warning "现有后端缺少 Mock 管理接口,准备更新本项目服务"
stop_owned_server
local retries=0
while [ -n "$(check_port "$SERVER_PORT")" ]; do
retries=$((retries + 1))
if [ "$retries" -ge 20 ]; then
error "旧后端未能释放端口 ${SERVER_PORT},请检查占用进程"
return 1
fi
sleep 0.25
done
fi
info "启动后端 :$SERVER_PORT ..."
: >"$SERVER_LOG"
kill_port "$SERVER_PORT"
build_server
# 默认仅本机;对外需 OPSXAPI_BIND_PUBLIC=1(或显式 OPSXAPI_ADDR)
local bind_env=""
@@ -121,41 +159,44 @@ start_server() {
bind_env="OPSXAPI_ADDR=127.0.0.1:$SERVER_PORT"
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\""
"export PATH=\"/opt/homebrew/bin:/usr/local/bin:\$PATH\"; cd \"$SCRIPT_DIR/$SERVER_DIR\" && exec env $bind_env OPSXAPI_MOCK_ADDR=\"${OPSXAPI_MOCK_ADDR:-0.0.0.0:8788}\" OPSXAPI_MOCK_ALLOW_PUBLIC=\"${OPSXAPI_MOCK_ALLOW_PUBLIC:-0}\" GIN_MODE=debug \"./bin/$SERVER_BIN\""
wait_http "http://127.0.0.1:$SERVER_PORT/health" "后端"
if ! mock_api_ready; then
error "后端已启动,但 Mock 管理接口仍不可用,请检查 $SERVER_LOG"
return 1
fi
success "后端 UP http://127.0.0.1:$SERVER_PORT/health"
}
stop_server() {
stop_detached "$SERVER_LABEL"
kill_port "$SERVER_PORT"
stop_owned_server || return 1
success "后端已停止"
}
start_web() {
start_vite_for_desktop() {
ensure_npm
if curl -fsS "http://127.0.0.1:$WEB_PORT/" >/dev/null 2>&1; then
success "Web 已在运行 :$WEB_PORT"
success "桌面 UI(Vite) 已在运行 :$WEB_PORT"
return 0
fi
info "启动 Web UI :$WEB_PORT ..."
info "启动桌面 UI 源 :${WEB_PORT}(仅本机给 Tauri,勿当浏览器客户端)..."
: >"$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"
nohup /bin/bash -lc "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" >>"$WEB_LOG" 2>&1 </dev/null &
wait_http "http://127.0.0.1:$WEB_PORT/" "Vite" 80 || { tail -n 20 "$WEB_LOG"; return 1; }
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"
kill_port "$WEB_PORT"
success "Web 已停止"
success "Vite 已停止"
}
start_desktop() {
# Vite 照常(浏览器联调);Dock 桌面走临时 .app(每次刷当前 icons/icon.icns)
start_web
start_vite_for_desktop
info "启动桌面 OpsxAPI(临时 .app + 当前图标)..."
: >"$DESKTOP_LOG"
ensure_npm
@@ -168,7 +209,7 @@ start_desktop() {
local i=0
while true; do
if pgrep -x OpsxAPI >/dev/null 2>&1; then
if gui_is_running; then
success "桌面 OpsxAPI 已启动(临时 .app,Dock 用当前 icon.icns)"
info "路径: $GUI_APP"
return 0
@@ -196,11 +237,15 @@ status() {
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"
success "vite :$WEB_PORT UP(桌面源,非浏览器产品)"
else
warning "web :$WEB_PORT DOWN"
warning "vite :$WEB_PORT DOWN"
fi
if pgrep -x OpsxAPI >/dev/null 2>&1; then
if curl -fsS "http://127.0.0.1:$SERVER_PORT/" >/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 OpsxAPI UP"
else
warning "gui OpsxAPI DOWN"
@@ -212,12 +257,12 @@ usage() {
$PROJECT_NAME 启动脚本(场景 profile)
./run.sh # 同 web
./run.sh web # 浏览器:Gin + Vite
./run.sh gui # 桌面:Vite + 临时 .app(Dock=icons/icon.icns)
./run.sh all # Gin + Vite + Desktop
./run.sh web # 仅 Gin:官网 + docs → http://127.0.0.1:$SERVER_PORT/
./run.sh gui # 桌面:临时 .app(Dock=OpsxAPI / icon.icns;默认也起 Gin)
./run.sh all # 同 gui
./run.sh server # 只 Gin
./run.sh web-only # 只 Vite
./run.sh server # 只 Gin(官网/docs + 代理 API)
./run.sh web-only # 只 Vite(桌面源,不是浏览器工作台)
./run.sh desktop # 只 Desktop(会确保 Vite)
./run.sh stop | restart | status | help
@@ -229,9 +274,10 @@ USAGE
start_web_scene() {
start_server
start_web
stop_web || true
echo
success "Web 场景就绪 → http://127.0.0.1:$WEB_PORT"
success "官网/文档就绪 → http://127.0.0.1:$SERVER_PORT/ · docs → http://127.0.0.1:$SERVER_PORT/docs"
info "操作请用 ./run.sh gui(桌面)。:$WEB_PORT 不是产品入口。"
}
start_gui_scene() {
@@ -246,17 +292,13 @@ start_gui_scene() {
}
start_all_scene() {
start_server
start_web
start_desktop
echo
success "All 就绪:Web + Desktop + Gin"
start_gui_scene
}
stop_all() {
stop_desktop || true
stop_web || true
stop_server || true
stop_server
}
cmd="${1:-web}"
@@ -265,7 +307,10 @@ case "$cmd" in
gui|desktop-scene) start_gui_scene ;;
all) start_all_scene ;;
server) start_server ;;
web-only) start_web ;;
web-only|vite)
warning "Vite 仅桌面源,不是浏览器工作台;请用: ./run.sh gui"
start_vite_for_desktop
;;
desktop|gui-only) start_desktop ;;
stop) stop_all ;;
restart)