From 8843b8cca5638d111a7ffcf518c205114a68a07d Mon Sep 17 00:00:00 2001 From: jianf <2198972886@qq.com> Date: Sun, 16 Aug 2026 12:49:55 +0800 Subject: [PATCH] feat(gui): dockerized Windows cross-build (win-builder image + one-shot dist script) + GUI/backend scenario docs --- Makefile | 11 ++-- README.md | 40 +++++++++++++-- README_EN.md | 74 ++++++++++++++++++++++++++- cmd/gui/docker/win-builder/Dockerfile | 35 +++++++++++++ cmd/gui/scripts/dist-win-docker.sh | 23 +++++++++ cmd/gui/scripts/win-core-docker.sh | 38 ++++++++++++++ 6 files changed, 213 insertions(+), 8 deletions(-) create mode 100644 cmd/gui/docker/win-builder/Dockerfile create mode 100755 cmd/gui/scripts/dist-win-docker.sh create mode 100755 cmd/gui/scripts/win-core-docker.sh diff --git a/Makefile b/Makefile index 6d30256..df3b2e9 100644 --- a/Makefile +++ b/Makefile @@ -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" diff --git a/README.md b/README.md index 6030218..77bd0b3 100644 --- a/README.md +++ b/README.md @@ -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-64;electron-builder 用 wine) +make gui-dist # 构建 deb + AppImage(linux) +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 +``` ### 特性 diff --git a/README_EN.md b/README_EN.md index a176cda..47d8f77 100644 --- a/README_EN.md +++ b/README_EN.md @@ -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 `/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 ``` @@ -324,4 +396,4 @@ go test -tags luajit ./... Covers: config validation, adapter load/transform, signature hooks, gateway auth, SDK round trip, SSE streaming, model routing, multimodal passthrough and -disable_thinking. \ No newline at end of file +disable_thinking. diff --git a/cmd/gui/docker/win-builder/Dockerfile b/cmd/gui/docker/win-builder/Dockerfile new file mode 100644 index 0000000..03b17d0 --- /dev/null +++ b/cmd/gui/docker/win-builder/Dockerfile @@ -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"] diff --git a/cmd/gui/scripts/dist-win-docker.sh b/cmd/gui/scripts/dist-win-docker.sh new file mode 100755 index 0000000..b7609ca --- /dev/null +++ b/cmd/gui/scripts/dist-win-docker.sh @@ -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 .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" diff --git a/cmd/gui/scripts/win-core-docker.sh b/cmd/gui/scripts/win-core-docker.sh new file mode 100755 index 0000000..c68a461 --- /dev/null +++ b/cmd/gui/scripts/win-core-docker.sh @@ -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"