chore: Docker 双服务 + Makefile/build.ps1 双进程目标 + README 架构更新

- Dockerfile 改为 Node 24 运行时:Vike 产物 + Go API 二进制共用镜像
- docker-compose 双容器:web(:3000 对外 SSR) + api(:3001 内部,持 /data 卷)
- entrypoint 改用 gosu 降权(Debian 基础镜像),健康检查用 node fetch
- Makefile/build.ps1:build 不再打包前端,新增 serve(本地双进程)与 web-build
- README 同步双服务架构、端口/环境变量、部署与开发说明
This commit is contained in:
2026-09-09 02:48:28 +08:00
parent ba39376a0e
commit 230006a905
7 changed files with 194 additions and 119 deletions

140
README.md
View File

@@ -13,7 +13,7 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-18a058?style=flat-square)](LICENSE)
[![Docker](https://img.shields.io/badge/Docker-hangzhang714128%2Fjiang13--forum-2496ED?style=flat-square&logo=docker&logoColor=white)](https://hub.docker.com/r/hangzhang714128/jiang13-forum)
[![Go](https://img.shields.io/badge/Go-1.26-00ADD8?style=flat-square&logo=go&logoColor=white)](go.mod)
[![React](https://img.shields.io/badge/React-18-61DAFB?style=flat-square&logo=react&logoColor=white)](frontend/package.json)
[![React](https://img.shields.io/badge/React-19-61DAFB?style=flat-square&logo=react&logoColor=white)](frontend/package.json)
[![SQLite](https://img.shields.io/badge/SQLite-内置-003B57?style=flat-square&logo=sqlite&logoColor=white)](#)
[在线演示](https://bbs.iioio.com/) ·
@@ -114,11 +114,12 @@
### 部署体验
- **单二进制** — `go:embed` 打包前端,无需再单独部署静态资源
- **双服务架构** — Node(:3000 对外:SSR + 静态资源 + API 反代)+ Go(:3001 内部:纯 API),职责单一、可独立扩缩
- **服务端渲染** — 首页 / 板块 / 帖子详情由 Vike SSR 真渲染,SEO 友好、首屏直出;其余页面 SPA 接管
- **零依赖数据库** — SQLite 内建,数据目录由 `app.ini` 统一管理
- **跨平台** — Windows / Linux / macOS 一键编译
- **跨平台** — Windows / Linux / macOS 一键编译 Go API
- **系统服务** — 内置 Linux systemd / Windows Service 注册
- **Docker 单容器** — 多阶段镜像,挂载 `data/` 即可持久化
- **Docker 双容器** — 同一镜像两种角色,`docker compose up` 一键起
---
@@ -126,7 +127,7 @@
### 1. 编译
**Windows(推荐):**
Go API 二进制(不含前端,Windows):
```bat
build.bat
@@ -142,9 +143,21 @@ make build
**手动分步(全平台):**
```bash
go build -trimpath -ldflags "-s -w" -o dist/jiang13 ./cmd/jiang13
```
前端构建(Vike,产出 `frontend/dist/client` + `dist/server`):
```bash
cd frontend && npm install && npm run build
cd .. && go build -trimpath -ldflags "-s -w" -o dist/jiang13 ./cmd/jiang13
```
本地生产模式一键起双服务(Node :3000 对外 + Go API :3001):
```bash
make serve # Linux / macOS
build.bat -Target serve # Windows
```
跨平台编译:
@@ -180,31 +193,38 @@ make compose-up
**拉取已构建镜像(Docker Hub):**
同一镜像两种角色,建议直接用 `docker-compose.yml`(已含完整配置):
```bash
docker pull hangzhang714128/jiang13-forum:latest
docker run -d --name jiang13 \
-p 3000:3000 \
-v jiang13-data:/data \
--restart unless-stopped \
hangzhang714128/jiang13-forum:latest
docker compose up -d
```
**数据持久化:** 容器内 `/data` 对应 SQLite、上传、日志与 JWT 密钥,与下方「数据目录」结构一致。可用 Docker volume 或绑定宿主机目录。镜像启动时会自动将 `/data` 卷属主修正为 uid `1000`(`jiang13` 用户),适配 1Panel 等面板挂载的目录。
Compose 会启动两个容器:
| 容器 | 角色 | 端口 | 说明 |
|------|------|------|------|
| `web` | Node 渲染服务器 | `3000` 对外 | Vike SSR + 静态资源 + API 反代(`GO_API_URL=http://api:3001`) |
| `api` | Go API | `3001` 内部 | 纯 API 服务,持有 `/data` 数据卷 |
**数据持久化:** `api` 容器内 `/data` 对应 SQLite、上传、日志与 JWT 密钥,与下方「数据目录」结构一致。可用 Docker volume 或绑定宿主机目录。镜像启动时会自动将 `/data` 卷属主修正为 uid `1000`,适配 1Panel 等面板挂载的目录。
**若使用旧版镜像仍报 permission denied**,可在宿主机执行:`chown -R 1000:1000 /你的数据目录`
**可选环境变量(容器编排):**
| 变量 | 说明 |
|------|------|
| `JIANG13_HTTP_PORT` | HTTP 端口(默认 `3000`) |
| `JIANG13_DATA` | 数据目录(默认 `/data`) |
| `JIANG13_JWT_SECRET` | JWT 密钥(留空则自动生成并写入 `/data/.jwt_secret`) |
| `JIANG13_CONFIG` | 配置文件路径 |
| `JIANG13_WORK_PATH` | 工作目录 |
| `JIANG13_COMMUNITY_HUB` | 维护者选项:设为 `1` 时本站作为社区枢纽收报。官方站 `bbs.iioio.com` 会按域名自动成为枢纽,无需此变量;仅非官网的枢纽镜像 / 预发需要显式开启(见 `app.ini.example`) |
| 变量 | 容器 | 说明 |
|------|------|------|
| `GO_API_URL` | web | Go API 地址(默认 `http://127.0.0.1:3001`,Compose 中为 `http://api:3001`) |
| `PORT` | web | Node 渲染服务器监听端口(默认 `3000`) |
| `JIANG13_HTTP_PORT` | api | HTTP 端口(默认 `3000`,Compose 中为 `3001`) |
| `JIANG13_DATA` | api | 数据目录(默认 `/data`) |
| `JIANG13_JWT_SECRET` | api | JWT 密钥(留空则自动生成并写入 `/data/.jwt_secret`) |
| `JIANG13_CONFIG` | api | 配置文件路径 |
| `JIANG13_WORK_PATH` | api | 工作目录 |
| `JIANG13_COMMUNITY_HUB` | api | 维护者选项:设为 `1` 时本站作为社区枢纽收报。官方站 `bbs.iioio.com` 会按域名自动成为枢纽,无需此变量;仅非官网的枢纽镜像 / 预发需要显式开启(见 `app.ini.example`) |
**健康检查:** `GET /health` 返回 `{"status":"ok"}`,供 Docker / 负载均衡探活。
**健康检查:** Go API `GET /health`、Node 渲染服务器 `GET /healthz`,均返回 200,供 Docker / 负载均衡探活。
**发布镜像到 Docker Hub(手动):**
@@ -238,14 +258,16 @@ docker push hangzhang714128/jiang13-forum:latest
**1Panel 部署提示:**
1. 容器镜像填 `hangzhang714128/jiang13-forum:latest`
2. 端口映射 `3000:3000`
3. 挂载数据卷到容器内 `/data`(镜像会自动修正目录权限)
1. 用 Compose 编排(`docker-compose.yml`)或手动创建两个容器:`api`(Go,内部 `3001`,挂载 `/data`)与 `web`(Node,映射 `3000:3000`)
2. `web` 容器环境变量 `GO_API_URL` 指向 `api` 容器(如 `http://api:3001`)
3. 数据卷只需挂在 `api` 容器的 `/data`(镜像会自动修正目录权限)
4. 首次访问 `http://服务器IP:3000/register` 注册管理员
### 3. 直接启动(二进制)
不想自己编译时,从 [Gitea Releases](https://git.iioio.com/freefire/jiang13-forum/releases) 下载与 Docker 同版本号的文件即可:
Go 二进制仅提供 API,完整站点需同时运行 Node 渲染服务器(构建前端后 `frontend/server/index.mjs`)。最简单的方式是用上面的 Docker Compose,或本地 `make serve` / `build.bat -Target serve` 一键起双进程。
如需手动部署二进制,从 [Gitea Releases](https://git.iioio.com/freefire/jiang13-forum/releases) 下载与 Docker 同版本号的文件:
| 文件 | 平台 |
|------|------|
@@ -254,35 +276,24 @@ docker push hangzhang714128/jiang13-forum:latest
当前 **1.1.7:** [Windows](https://git.iioio.com/freefire/jiang13-forum/releases/download/v1.1.7/jiang13-1.1.7-windows-amd64.exe) · [Linux](https://git.iioio.com/freefire/jiang13-forum/releases/download/v1.1.7/jiang13-1.1.7-linux-amd64)
把文件放到目标目录后直接运行(首次会在同目录生成 `app.ini`):
两个进程一起跑(Go API :3001 + Node :3000 对外):
```bash
# Windows
.\jiang13-1.1.7-windows-amd64.exe
# 或改名为 jiang13.exe 后:
.\jiang13.exe
# 1) 启动 Go API
./jiang13 --port 3001 --data ./data
# Linux
chmod +x jiang13-1.1.7-linux-amd64
./jiang13-1.1.7-linux-amd64
# 2) 另开终端,构建并启动 Node 渲染服务器
cd frontend && npm install && npm run build
PORT=3000 GO_API_URL=http://127.0.0.1:3001 npm start
```
本地刚编译的产物在 `dist/`:
Windows:
```bash
# Windows
.\dist\jiang13.exe
# Linux / macOS
./dist/jiang13
```
也可先复制示例配置再改端口 / 数据目录:
```bash
cp app.ini.example /opt/jiang13/app.ini
# 编辑 app.ini 后:
./jiang13
```powershell
.\jiang13.exe --port 3001 --data .\data
# 另开 PowerShell:
cd frontend; npm install; npm run build
$env:PORT = '3000'; $env:GO_API_URL = 'http://127.0.0.1:3001'; npm start
```
### 4. 首次使用
@@ -354,16 +365,17 @@ C:\jiang13\jiang13.exe --service start
| 层级 | 技术 |
|------|------|
| **后端** | Go 1.26 · Gin · GORM · SQLite |
| **前端** | React 18 · TipTap · Radix UI · Tailwind CSS · TanStack Virtual |
| **构建** | Vite → `go:embed` 内嵌 SPA,单二进制发布 |
| **后端** | Go 1.26 · Gin · GORM · SQLite(纯 API 服务) |
| **前端** | React 19 · Vike SSR · react-router · TipTap · Radix UI · Tailwind CSS · TanStack Virtual |
| **构建** | Vite 8(Rolldown)双构建:`dist/client` 静态资源 + `dist/server` SSR 入口 |
| **渲染** | Node 24 · Express + Vike `renderPage`(:3000 对外,API 反代至 Go :3001) |
| **认证** | bcrypt · JWT Cookie · 可选 OIDC Provider |
---
## 前端开发
日常改前端不需要重新完整构建,Vite 支持秒级热更新(HMR,热模块替换):
日常改前端不需要重新完整构建,Vike dev 支持秒级热更新(HMR,热模块替换):
```bat
build.bat -Target dev
@@ -373,16 +385,17 @@ build.bat -Target dev
make dev
```
浏览器访问 `http://localhost:5173`,API 自动代理到 `http://localhost:3000`。
浏览器访问 `http://localhost:5173`,API 自动代理到 `http://localhost:3000`。首页 / 板块 / 帖子详情在 dev 下同样走 SSR(`vike dev`),与生产行为一致;其余页面为 SPA 壳。
开发后端与 `dist/jiang13` 共用数据目录 `dist/data`(SQLite、上传、JWT 密钥等),避免 dev 与 dist 运行数据不一致。
**何时需要完整构建:**
- 修改 Go 代码或要发布单二进制 → `build.bat` / `make build`
- 修改 Go 代码 → `build.bat` / `make build`(仅编译 Go API)
- 验证生产 SSR / 发布部署 → `make serve`(构建前端 + 双进程起本地产)
- 更新 README 界面截图 → 见下方「更新截图」
> 直接访问 `:3000` 看到的是上次 build 嵌入的前端;开发时请用 `:5173`。
> 开发时请访问 `:5173`;`:3000` 只有在生产模式(Node 渲染服务器)下才有页面。
### 更新截图
@@ -412,17 +425,20 @@ jiang13-forum/
├── cmd/jiang13/ # 程序入口(含系统服务注册)
├── config/ # app.ini 与命令行配置
├── app.ini.example # 配置文件示例
├── Dockerfile # 多阶段 Docker 构建
├── docker-compose.yml # 单容器 Compose 部署
├── docker-entrypoint.sh # 容器启动脚本(修正 /data 卷权限)
├── Dockerfile # 多阶段构建(前端 + Go API 共用镜像,双容器运行)
├── docker-compose.yml # 双容器 Compose 部署(web :3000 + api :3001)
├── docker-entrypoint.sh # api 容器启动脚本(修正 /data 卷权限)
├── .dockerignore
├── model/ # GORM 模型与数据库迁移
├── service/ # 业务逻辑
├── handler/ # HTTP 处理器(前台 + 后台)
├── handler/ # HTTP 处理器(API + /api/home-boot SSR 聚合端点)
├── middleware/ # JWT 鉴权等
├── router/ # 路由注册
├── embed_static/ # go:embed 内嵌的 SPA
├── frontend/ # React 源码(Vite 构建)
├── router/ # 路由注册(仅 API)
├── frontend/
│ ├── pages/ # Vike 页面(feed=SSR 白名单 / spa=catch-all 壳 / _error)
│ ├── renderer/ # Vike 双端渲染入口(onRenderHtml / onRenderClient)
│ ├── server/ # Node 渲染服务器(express + renderPage + API 反代)
│ └── src/ # React 源码(含 src/ssr 服务端取数与作用域管理)
├── docs/screenshots/ # README 界面截图
├── ROADMAP.md # 路线图与已知问题
└── scripts/ # 开发辅助脚本(含截图)