feat(gui): dockerized Windows cross-build (win-builder image + one-shot dist script) + GUI/backend scenario docs

This commit is contained in:
2026-08-16 12:49:55 +08:00
parent 47f3b44d92
commit 8843b8cca5
6 changed files with 213 additions and 8 deletions

View File

@ -1,4 +1,4 @@
.PHONY: all build test gui gui-dev gui-dist gui-deb gui-win help clean
.PHONY: all build test gui gui-dev gui-dist gui-deb gui-win gui-win-docker help clean
# ModelRouter - single-binary gateway + optional Electron desktop GUI (embedded core)
export BINARY
@ -33,10 +33,14 @@ gui-dist:
gui-deb:
@cd $(GUI_DIR) && npm run dist:debian
# windows nsis installer (needs apt install gcc-mingw-w64-x86-64 + wine for electron-builder)
# windows nsis installer: host-built core (needs apt install gcc-mingw-w64-x86-64 + wine)
gui-win:
@cd $(GUI_DIR) && npm run dist:win
# windows nsis installer: full cross-build in docker (self-sufficient, no host mingw)
gui-win-docker:
@cd $(GUI_DIR) && ./scripts/dist-win-docker.sh
gui-dist-dir:
@cd $(GUI_DIR) && npm run dist:dir
@ -53,5 +57,6 @@ help:
@echo " make gui-dev run GUI with devtools"
@echo " make gui-dist build deb + AppImage (linux)"
@echo " make gui-deb build deb only"
@echo " make gui-win build Windows nsis (needs mingw + wine)"
@echo " make gui-win build Windows nsis (needs host mingw + wine)"
@echo " make gui-win-docker build Windows nsis all-in-docker (no host mingw needed)"
@echo " make test run go tests"

View File

@ -307,6 +307,18 @@ zen 按 User-Agent 指纹识别官方客户端并分流——非官方 UA 的请
一键启停、系统托盘常驻、开机自启、静默启动,内容区一比一内嵌完整 WebUI
(状态/对话/密钥/优先级/源/适配器 六个页面,免登录)。服务器用户继续使用纯 Go 二进制。
### 选择 GUI 还是纯后端
| 场景 | 推荐 | 理由 |
| ------ | ------ | ------ |
| 服务器 / 内网网关 / Docker 部署 / 无人值守常驻 | **纯后端**(单二进制) | 极轻量(~10MB、~15MB RSS零 GUI 依赖,适合 systemd/容器托管、远程管理 |
| 个人桌面日常使用 / 多设备内网共享 | **GUI**Electron | 免登录内嵌 WebUI、系统托盘一键启停、开机自启、静默后台适合不懂命令行的使用者 |
| Windows 桌面 | **GUI** | 纯后端在 Windows 上需自行注册服务GUI 提供原生托盘/自启体验 |
| CI 一键出三平台安装包 | **GUI 打包脚本** | `make gui-*` 系列出 deb/AppImage/NSIS可进发行流水线 |
两者完全同源GUI 内嵌的就是纯后端的同一份 `llmsproxy` 核心LuaJIT 版),
配置/适配器格式完全一致,可随时互换。
### 构建与运行
```bash
@ -314,12 +326,32 @@ cd cmd/gui
npm install
make gui # 或 npm run dev —— 开发运行(需先 make build 生成 bin/llmsproxy
make gui-deb # 本机构建 deb分享给其它 Linux 用户)
make gui-dist # 构建 deb + AppImage
make gui-win # 构建 Windows nsis 安装包(需先 apt install gcc-mingw-w64-x86-64electron-builder 用 wine
make gui-dist # 构建 deb + AppImagelinux
make gui-win # 构建 Windows nsis 安装包(本机 mingw + wine
make gui-win-docker # 构建 Windows nsis 安装包(全 docker 自足,无需本机 mingw
make gui-dist-dir # 仅解包产物(调试用,不产安装包)
```
产物在 `cmd/build/gui-dist/``ModelRouter-1.0.0.AppImage``modelrouter-gui_1.0.0_amd64.deb`
`modelrouter-gui Setup 1.0.0.exe`win,需 mingw 交叉编译核心后。rpm 需系统 `rpmbuild`
产物在 `cmd/build/gui-dist/``ModelRouter-1.0.0.AppImage`
`modelrouter-gui_1.0.0_amd64.deb``ModelRouter Setup 1.0.0.exe`win。rpm 需系统 `rpmbuild`
#### Windows 打包docker 固化方案,推荐)
`make gui-win-docker` 一步完成:交叉编译核心 + NSIS 打包,宿主机**无需安装 mingw**。
- 构建镜像:`cmd/gui/docker/win-builder/Dockerfile``golang:1.25` + mingw-w64 +
预编译 LuaJIT windows 版 `lua51.dll` 与 import lib。LuaJIT 源码取自 gitcode
镜像国内可达github 兜底Go 依赖走 `https://goproxy.cn`
- 核心编译:`cmd/gui/scripts/win-core-docker.sh` —— 在容器内产出
`cmd/gui/bin/{llmsproxy.exe,lua51.dll}`
- 一键发布:`cmd/gui/scripts/dist-win-docker.sh` —— 核心编译后调用
electron-builder宿主需有 `wine` 用于 NSIS
- 首次运行自动构建 `modelrouter/win-builder` 镜像;已存在则直接复用。
```bash
# 全 Windows 发布(核心重编 + NSIS
make gui-win-docker
```
### 特性

View File

@ -305,6 +305,78 @@ app, so you HMAC-sign timestamp+URL+body with `meta.app_secret` and add
`X-App-Sign`-style headers. Configure `sources[].meta.{app_id, app_secret,
app_agent}`.
## Desktop GUI (Electron)
Optional standalone product for desktop users on your internal network: an
**embedded ModelRouter core** (Clash Verge-style): one-click start/stop, system
tray, autostart, silent launch, and a pixel-for-pixel embedded full WebUI
(Status/Chat/Keys/Priority/Sources/Adapters — no login needed). Server users
keep using the plain Go binary.
### GUI vs. plain backend
| Scenario | Pick | Why |
| ---- | ---- | ---- |
| Server / intranet gateway / Docker / unattended long-running | **plain backend** (single binary) | ~10 MB, ~15 MB RSS, zero GUI deps; systemd/container friendly, remote admin |
| Personal desktop daily use / multi-device intranet sharing | **GUI** (Electron) | no-login embedded WebUI, tray one-click, autostart, silent background — for non-CLI users |
| Windows desktop | **GUI** | plain backend needs manual service registration; GUI ships native tray/autostart |
| CI one-shot 3-platform installers | **GUI packaging scripts** | `make gui-*` emits deb / AppImage / NSIS, drops straight into a release pipeline |
Both share the exact same `llmsproxy` core (LuaJIT build) — configs and
adapters are fully compatible, freely interchangeable.
### Build & run
```bash
cd cmd/gui
npm install
make gui # or npm run dev — dev run (needs make build first for bin/llmsproxy)
make gui-deb # local deb (share with other Linux users)
make gui-dist # deb + AppImage (linux)
make gui-win # Windows NSIS (host mingw + wine)
make gui-win-docker # Windows NSIS, fully dockerized (no host mingw needed)
```
Artifacts land in `cmd/build/gui-dist/`: `ModelRouter-1.0.0.AppImage`,
`modelrouter-gui_1.0.0_amd64.deb`, `ModelRouter Setup 1.0.0.exe` (win). rpm
needs system `rpmbuild`.
#### Windows packaging (dockerized, recommended)
`make gui-win-docker` does cross-compile + NSIS in one step; the host needs
**no mingw**.
- Image: `cmd/gui/docker/win-builder/Dockerfile` (`golang:1.25` + mingw-w64 +
prebuilt LuaJIT for Windows: `lua51.dll` + import lib). LuaJIT source comes
from the gitcode mirror (reachable in CN) with github fallback; Go modules go
through `https://goproxy.cn`.
- Core build: `cmd/gui/scripts/win-core-docker.sh` — emits
`cmd/gui/bin/{llmsproxy.exe,lua51.dll}` inside the container.
- One-shot release: `cmd/gui/scripts/dist-win-docker.sh` — after the core,
runs electron-builder (host needs `wine` for NSIS).
- The `modelrouter/win-builder` image is auto-built on first run and reused
afterwards.
```bash
# Full Windows release (rebuild core + NSIS):
make gui-win-docker
```
### Features
- **Embedded core**: auto-spawns the bundled `llmsproxy` (LuaJIT build);
config/keys/adapters live in `<userData>/profile/`. First run generates a
random admin key written into `keys` (not seed) and self-injects it — WebUI
needs no login and never nags about changing the initial key. Port changeable
in settings (default 8787).
- **System tray**: core status dot, autostart/silent toggles, start/stop/restart
core, quit.
- **Silent launch**: `--silent` flag or setting — starts to the tray only;
first run always shows the window.
- **Autostart**: `setLoginItemSettings` on Windows/macOS; writes
`~/.config/autostart/modelrouter-gui.desktop` on Linux, appending `--silent`
when silent mode is on.
## Layout
```

View File

@ -0,0 +1,35 @@
# ModelRouter GUI Windows builder
# Self-sufficient cross-compilation environment for the embedded llmsproxy core.
# Produces llmsproxy.exe (Go, windows/amd64, luajit tag) + lua51.dll (LuaJIT mingw build)
# inside a mounted workspace: cmd/gui/bin/{llmsproxy.exe,lua51.dll}
#
# Build: docker build -t modelrouter/win-builder cmd/gui/docker/win-builder
# Usage: make gui-win-docker (or cmd/gui/scripts/dist-win-docker.sh)
#
# LuaJIT source is fetched from gitcode.com (CN-reachable mirror) with github
# fallback; pass --build-arg LUAJIT_REPO to override.
FROM golang:1.25
ARG LUAJIT_REPO=https://gitcode.com/openresty/luajit2.git
RUN apt-get update -qq \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
gcc-mingw-w64-x86-64 make ca-certificates git \
&& rm -rf /var/lib/apt/lists/*
# LuaJIT cross-compiled for Windows; keeps lua51.dll and the cgo import lib
# (libluajit-5.1.dll.a -> libluajit-5.1.a in the mingw lib dir).
# NOTE: $LUAJIT_REPO deliberately unquoted in "sh -c" — quoting the ARG would
# embed literal quotes into the URL.
RUN git clone --depth 1 $LUAJIT_REPO /opt/luajit2 \
|| git clone --depth 1 https://github.com/openresty/luajit2 /opt/luajit2 \
&& cd /opt/luajit2 \
&& make HOST_CC=gcc CROSS=x86_64-w64-mingw32- TARGET_SYS=Windows TARGET_XCFLAGS=-DLUAJIT_ENABLE_LUA52COMPAT \
&& cp src/libluajit-5.1.dll.a /usr/x86_64-w64-mingw32/lib/libluajit-5.1.a
# Go proxy mirror for builds behind GFW-ish networks; overridable at runtime.
ENV GOPROXY=https://goproxy.cn,direct
WORKDIR /workspace
CMD ["/bin/bash"]

View File

@ -0,0 +1,23 @@
#!/usr/bin/env bash
# One-shot Windows release build for the GUI:
# 1) cross-compile the embedded core in docker (llmsproxy.exe + lua51.dll)
# 2) host-side electron-builder NSIS packaging (wine required on host)
# Outputs: cmd/build/gui-dist/"ModelRouter Setup <version>.exe"
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
cd "$ROOT/cmd/gui"
# 1) core (docker)
"$ROOT/cmd/gui/scripts/win-core-docker.sh"
# 2) packaging (host, needs wine for electron-builder's signtool/nsis)
command -v wine >/dev/null || { echo "wine required on host for NSIS packaging"; exit 1; }
if ! command -v npx >/dev/null; then echo "node/npm required"; exit 1; fi
echo "[dist-win] electron-builder --win nsis ..."
# bin artifacts already refreshed by win-core-docker.sh; skip the prepare step
npx electron-builder --win nsis
ls -la "$ROOT/cmd/build/gui-dist/"*.exe 2>/dev/null
echo "[dist-win] done"

View File

@ -0,0 +1,38 @@
#!/usr/bin/env bash
# Build embedded Windows core inside the self-sufficient builder image.
# - image has LuaJIT pre-built: /opt/luajit2/src/{lua51.dll, libluajit-5.1.dll.a}
# - import lib already installed at image build (libluajit-5.1.a, mingw lib dir)
# Outputs: cmd/gui/bin/{llmsproxy.exe, lua51.dll}
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
BIN_DIR="$ROOT/cmd/gui/bin"
IMAGE="${WIN_BUILDER_IMAGE:-modelrouter/win-builder}"
PROXY="${GOPROXY:-https://goproxy.cn,direct}"
command -v docker >/dev/null || {
echo "docker required"
exit 1
}
if ! docker image inspect "$IMAGE" >/dev/null 2>&1; then
echo "[win-core] building image $IMAGE ..."
docker build -t "$IMAGE" "$ROOT/cmd/gui/docker/win-builder"
fi
mkdir -p "$BIN_DIR"
echo "[win-core] cross-building llmsproxy.exe (windows/amd64, luajit) + lua51.dll ..."
docker run --rm \
-v "$ROOT":/workspace \
-e GOPROXY="$PROXY" \
-w /workspace \
"$IMAGE" bash -c '
set -euo pipefail
cp /opt/luajit2/src/lua51.dll /workspace/cmd/gui/bin/lua51.dll
GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc \
go build -buildvcs=false -tags luajit -trimpath \
-o /workspace/cmd/gui/bin/llmsproxy.exe ./cmd/llmsproxy
echo " -> core built"
'
echo "[win-core] done: $BIN_DIR/llmsproxy.exe"