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:
@@ -14,7 +14,6 @@ tmp-cookie.txt
|
|||||||
dist/
|
dist/
|
||||||
frontend/node_modules/
|
frontend/node_modules/
|
||||||
frontend/dist/
|
frontend/dist/
|
||||||
embed_static/static/spa/
|
|
||||||
node_modules/
|
node_modules/
|
||||||
.vite/
|
.vite/
|
||||||
|
|
||||||
|
|||||||
39
Dockerfile
39
Dockerfile
@@ -1,15 +1,20 @@
|
|||||||
# 姜十三论坛 — 多阶段构建:Node 前端 → Go 单二进制 → Alpine 运行镜像
|
# 姜十三论坛 — 多阶段构建:Node 前端(Vike SSR)+ Go API,双服务共用镜像
|
||||||
# 不使用 # syntax=docker/dockerfile:1,避免构建前额外拉取 docker.io/docker/dockerfile
|
# 不使用 # syntax=docker/dockerfile:1,避免构建前额外拉取 docker.io/docker/dockerfile
|
||||||
#
|
#
|
||||||
# 国内网络:默认经 DaoCloud 拉取基础镜像,npm/go 走国内代理
|
# 国内网络:默认经 DaoCloud 拉取基础镜像,npm/go 走国内代理
|
||||||
# 海外或已配置 Docker registry-mirrors 时可传空前缀:
|
# 海外或已配置 Docker registry-mirrors 时可传空前缀:
|
||||||
# docker build --build-arg IMAGE_PREFIX= ...
|
# docker build --build-arg IMAGE_PREFIX= ...
|
||||||
|
#
|
||||||
|
# 运行形态(docker compose 双容器,同一镜像两种角色):
|
||||||
|
# api —— Go 二进制,纯 API(:3001 内部,/data 数据卷)
|
||||||
|
# web —— Node 渲染服务器(对外 :3000:SSR + 静态资源 + API 反代)
|
||||||
|
# 镜像默认入口为 api 角色;web 角色在 compose 中以 node 用户覆盖 entrypoint。
|
||||||
|
|
||||||
ARG IMAGE_PREFIX=docker.m.daocloud.io/library/
|
ARG IMAGE_PREFIX=docker.m.daocloud.io/library/
|
||||||
ARG VERSION=dev
|
ARG VERSION=dev
|
||||||
|
|
||||||
# ── Stage 1: 前端构建(Vite → embed_static/static/spa)────────────────────
|
# ── Stage 1: 前端构建(Vike → dist/client 静态资源 + dist/server SSR 入口)──
|
||||||
FROM ${IMAGE_PREFIX}node:22-bookworm-slim AS frontend
|
FROM ${IMAGE_PREFIX}node:24-bookworm-slim AS frontend
|
||||||
WORKDIR /src/frontend
|
WORKDIR /src/frontend
|
||||||
COPY frontend/package.json frontend/package-lock.json ./
|
COPY frontend/package.json frontend/package-lock.json ./
|
||||||
RUN npm config set registry https://registry.npmmirror.com \
|
RUN npm config set registry https://registry.npmmirror.com \
|
||||||
@@ -17,7 +22,7 @@ RUN npm config set registry https://registry.npmmirror.com \
|
|||||||
COPY frontend/ ./
|
COPY frontend/ ./
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# ── Stage 2: Go 编译(纯 Go SQLite,CGO_ENABLED=0)────────────────────────
|
# ── Stage 2: Go 编译(纯 Go SQLite,CGO_ENABLED=0;仅 API,无内嵌前端)─────
|
||||||
FROM ${IMAGE_PREFIX}golang:1.26-bookworm AS builder
|
FROM ${IMAGE_PREFIX}golang:1.26-bookworm AS builder
|
||||||
ARG VERSION
|
ARG VERSION
|
||||||
ENV GOPROXY=https://goproxy.cn,direct
|
ENV GOPROXY=https://goproxy.cn,direct
|
||||||
@@ -25,31 +30,31 @@ WORKDIR /src
|
|||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
COPY . .
|
COPY . .
|
||||||
COPY --from=frontend /src/embed_static/static/spa ./embed_static/static/spa
|
|
||||||
RUN CGO_ENABLED=0 go build -trimpath \
|
RUN CGO_ENABLED=0 go build -trimpath \
|
||||||
-ldflags "-s -w -X main.version=${VERSION}" \
|
-ldflags "-s -w -X main.version=${VERSION}" \
|
||||||
-o /out/jiang13 ./cmd/jiang13
|
-o /out/jiang13 ./cmd/jiang13
|
||||||
|
|
||||||
# ── Stage 3: 运行镜像 ───────────────────────────────────────────────────────
|
# ── Stage 3: 运行镜像(Node 运行时承载 SSR 渲染与静态资源,附带 Go API 二进制)
|
||||||
FROM ${IMAGE_PREFIX}alpine:3.21
|
FROM ${IMAGE_PREFIX}node:24-bookworm-slim
|
||||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \
|
RUN apt-get update \
|
||||||
&& apk add --no-cache ca-certificates tzdata wget su-exec \
|
&& apt-get install -y --no-install-recommends gosu ca-certificates tzdata \
|
||||||
&& adduser -D -u 1000 -h /app jiang13
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
ENV TZ=Asia/Shanghai
|
ENV TZ=Asia/Shanghai \
|
||||||
|
NODE_ENV=production
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=builder /out/jiang13 /app/jiang13
|
COPY --from=builder /out/jiang13 /app/jiang13
|
||||||
|
COPY --from=frontend /src/frontend /app/frontend
|
||||||
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||||
RUN chown jiang13:jiang13 /app/jiang13 \
|
RUN chmod +x /app/jiang13 /docker-entrypoint.sh
|
||||||
&& chmod +x /app/jiang13 /docker-entrypoint.sh
|
|
||||||
|
|
||||||
# 以 root 启动,entrypoint 修正 /data 卷权限后 su-exec 降权为 jiang13
|
# 以 root 启动,entrypoint 修正 /data 卷权限后 gosu 降权为 uid 1000(node 用户)
|
||||||
VOLUME ["/data"]
|
VOLUME ["/data"]
|
||||||
EXPOSE 3000
|
EXPOSE 3000 3001
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
|
||||||
CMD wget -qO- http://127.0.0.1:3000/health || exit 1
|
CMD node -e "fetch('http://127.0.0.1:3001/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
|
||||||
|
|
||||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||||
CMD ["--work-path", "/app", "--data", "/data", "--port", "3000"]
|
CMD ["--work-path", "/app", "--data", "/data", "--port", "3001"]
|
||||||
|
|||||||
46
Makefile
46
Makefile
@@ -12,39 +12,40 @@ REGISTRY_IMAGE := hangzhang714128/jiang13-forum
|
|||||||
GO := go
|
GO := go
|
||||||
GOFLAGS := -trimpath
|
GOFLAGS := -trimpath
|
||||||
|
|
||||||
.PHONY: all build build-windows build-linux build-darwin clean run dev tidy help frontend frontend-build docker compose-up compose-down
|
.PHONY: all build web-build build-windows build-linux build-darwin build-all clean run serve dev tidy help docker compose-up compose-down
|
||||||
|
|
||||||
all: build
|
all: build
|
||||||
|
|
||||||
frontend-build:
|
## 前端构建(Vike:产出 frontend/dist/client 静态资源 + dist/server SSR 入口)
|
||||||
cd frontend && npm install && npm run build
|
web-build:
|
||||||
|
cd frontend && (test -d node_modules || npm install) && npm run build
|
||||||
|
|
||||||
## 编译当前平台二进制(纯 Go SQLite,无需 CGO)
|
## 编译当前平台二进制(纯 Go SQLite,无需 CGO;Go 仅含 API,前端由 Node 渲染)
|
||||||
build: frontend-build
|
build:
|
||||||
@mkdir -p $(BUILD_DIR)
|
@mkdir -p $(BUILD_DIR)
|
||||||
CGO_ENABLED=0 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME) $(MAIN_PKG)
|
CGO_ENABLED=0 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME) $(MAIN_PKG)
|
||||||
@echo "✓ 编译完成: $(BUILD_DIR)/$(APP_NAME)"
|
@echo "✓ 编译完成: $(BUILD_DIR)/$(APP_NAME)"
|
||||||
|
|
||||||
## Windows amd64(先打包前端再 embed)
|
## Windows amd64
|
||||||
build-windows: frontend-build
|
build-windows:
|
||||||
@mkdir -p $(BUILD_DIR)
|
@mkdir -p $(BUILD_DIR)
|
||||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME).exe $(MAIN_PKG)
|
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME).exe $(MAIN_PKG)
|
||||||
@echo "✓ Windows: $(BUILD_DIR)/$(APP_NAME).exe"
|
@echo "✓ Windows: $(BUILD_DIR)/$(APP_NAME).exe"
|
||||||
|
|
||||||
## Linux amd64(先打包前端再 embed)
|
## Linux amd64
|
||||||
build-linux: frontend-build
|
build-linux:
|
||||||
@mkdir -p $(BUILD_DIR)
|
@mkdir -p $(BUILD_DIR)
|
||||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-linux-amd64 $(MAIN_PKG)
|
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-linux-amd64 $(MAIN_PKG)
|
||||||
@echo "✓ Linux: $(BUILD_DIR)/$(APP_NAME)-linux-amd64"
|
@echo "✓ Linux: $(BUILD_DIR)/$(APP_NAME)-linux-amd64"
|
||||||
|
|
||||||
## macOS arm64 (Apple Silicon)(先打包前端再 embed)
|
## macOS arm64 (Apple Silicon)
|
||||||
build-darwin: frontend-build
|
build-darwin:
|
||||||
@mkdir -p $(BUILD_DIR)
|
@mkdir -p $(BUILD_DIR)
|
||||||
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-darwin-arm64 $(MAIN_PKG)
|
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-darwin-arm64 $(MAIN_PKG)
|
||||||
@echo "✓ macOS: $(BUILD_DIR)/$(APP_NAME)-darwin-arm64"
|
@echo "✓ macOS: $(BUILD_DIR)/$(APP_NAME)-darwin-arm64"
|
||||||
|
|
||||||
## 跨平台全量编译(frontend-build 只跑一次)
|
## 跨平台全量编译
|
||||||
build-all: frontend-build
|
build-all:
|
||||||
@mkdir -p $(BUILD_DIR)
|
@mkdir -p $(BUILD_DIR)
|
||||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME).exe $(MAIN_PKG)
|
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME).exe $(MAIN_PKG)
|
||||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-linux-amd64 $(MAIN_PKG)
|
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/$(APP_NAME)-linux-amd64 $(MAIN_PKG)
|
||||||
@@ -56,12 +57,19 @@ build-all: frontend-build
|
|||||||
tidy:
|
tidy:
|
||||||
$(GO) mod tidy
|
$(GO) mod tidy
|
||||||
|
|
||||||
## 本地运行(仅后端,使用已 embed 的前端;数据目录与 dist 二进制一致)
|
## 本地运行(仅 Go API :3000,无前端页面;数据目录与 dist 二进制一致)
|
||||||
run:
|
run:
|
||||||
@mkdir -p $(DEV_DATA_DIR)
|
@mkdir -p $(DEV_DATA_DIR)
|
||||||
$(GO) run $(MAIN_PKG) --data $(DEV_DATA_DIR)
|
$(GO) run $(MAIN_PKG) --data $(DEV_DATA_DIR)
|
||||||
|
|
||||||
## 前端热更新开发(后端 :3000 + Vite :5173,Ctrl+C 同时退出;数据目录与 dist 二进制一致)
|
## 生产模式本地运行(Node :3000 对外 SSR + Go API :3001)
|
||||||
|
serve: build web-build
|
||||||
|
@mkdir -p $(DEV_DATA_DIR)
|
||||||
|
@trap 'kill 0' INT; \
|
||||||
|
$(BUILD_DIR)/$(APP_NAME) --data $(DEV_DATA_DIR) --port 3001 & \
|
||||||
|
cd frontend && PORT=3000 GO_API_URL=http://127.0.0.1:3001 npm start
|
||||||
|
|
||||||
|
## 前端热更新开发(Vike dev :5173 + Go API :3000,Ctrl+C 同时退出;数据目录与 dist 二进制一致)
|
||||||
dev:
|
dev:
|
||||||
@echo "前端热更新: http://localhost:5173"
|
@echo "前端热更新: http://localhost:5173"
|
||||||
@echo "后端 API : http://localhost:3000"
|
@echo "后端 API : http://localhost:3000"
|
||||||
@@ -79,7 +87,7 @@ clean:
|
|||||||
docker:
|
docker:
|
||||||
docker build --build-arg VERSION=$(VERSION) -t $(REGISTRY_IMAGE):$(VERSION) -t $(REGISTRY_IMAGE):latest .
|
docker build --build-arg VERSION=$(VERSION) -t $(REGISTRY_IMAGE):$(VERSION) -t $(REGISTRY_IMAGE):latest .
|
||||||
|
|
||||||
## Docker Compose 启动
|
## Docker Compose 启动(web :3000 + api :3001 双容器)
|
||||||
compose-up:
|
compose-up:
|
||||||
docker compose up -d --build
|
docker compose up -d --build
|
||||||
|
|
||||||
@@ -89,12 +97,14 @@ compose-down:
|
|||||||
|
|
||||||
help:
|
help:
|
||||||
@echo "姜十三论坛编译命令:"
|
@echo "姜十三论坛编译命令:"
|
||||||
@echo " make build - 编译当前平台"
|
@echo " make build - 编译 Go API 二进制(当前平台)"
|
||||||
|
@echo " make web-build - 构建前端(Vike:client + server 产物)"
|
||||||
|
@echo " make serve - 生产模式本地运行(Node :3000 + Go API :3001)"
|
||||||
@echo " make build-windows - 编译 Windows"
|
@echo " make build-windows - 编译 Windows"
|
||||||
@echo " make build-linux - 编译 Linux"
|
@echo " make build-linux - 编译 Linux"
|
||||||
@echo " make build-darwin - 编译 macOS"
|
@echo " make build-darwin - 编译 macOS"
|
||||||
@echo " make build-all - 编译全部平台"
|
@echo " make build-all - 编译全部平台"
|
||||||
@echo " make run - 仅启动后端(:3000)"
|
@echo " make run - 仅启动 Go API(:3000)"
|
||||||
@echo " make dev - 前端热更新开发(:5173 + :3000)"
|
@echo " make dev - 前端热更新开发(:5173 + :3000)"
|
||||||
@echo " make docker - 构建 Docker 镜像"
|
@echo " make docker - 构建 Docker 镜像"
|
||||||
@echo " make compose-up - Docker Compose 启动"
|
@echo " make compose-up - Docker Compose 启动"
|
||||||
|
|||||||
140
README.md
140
README.md
@@ -13,7 +13,7 @@
|
|||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
[](https://hub.docker.com/r/hangzhang714128/jiang13-forum)
|
[](https://hub.docker.com/r/hangzhang714128/jiang13-forum)
|
||||||
[](go.mod)
|
[](go.mod)
|
||||||
[](frontend/package.json)
|
[](frontend/package.json)
|
||||||
[](#)
|
[](#)
|
||||||
|
|
||||||
[在线演示](https://bbs.iioio.com/) ·
|
[在线演示](https://bbs.iioio.com/) ·
|
||||||
@@ -114,11 +114,12 @@
|
|||||||
|
|
||||||
### 部署体验
|
### 部署体验
|
||||||
|
|
||||||
- **单二进制** — `go:embed` 打包前端,无需再单独部署静态资源
|
- **双服务架构** — Node(:3000 对外:SSR + 静态资源 + API 反代)+ Go(:3001 内部:纯 API),职责单一、可独立扩缩
|
||||||
|
- **服务端渲染** — 首页 / 板块 / 帖子详情由 Vike SSR 真渲染,SEO 友好、首屏直出;其余页面 SPA 接管
|
||||||
- **零依赖数据库** — SQLite 内建,数据目录由 `app.ini` 统一管理
|
- **零依赖数据库** — SQLite 内建,数据目录由 `app.ini` 统一管理
|
||||||
- **跨平台** — Windows / Linux / macOS 一键编译
|
- **跨平台** — Windows / Linux / macOS 一键编译 Go API
|
||||||
- **系统服务** — 内置 Linux systemd / Windows Service 注册
|
- **系统服务** — 内置 Linux systemd / Windows Service 注册
|
||||||
- **Docker 单容器** — 多阶段镜像,挂载 `data/` 即可持久化
|
- **Docker 双容器** — 同一镜像两种角色,`docker compose up` 一键起
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -126,7 +127,7 @@
|
|||||||
|
|
||||||
### 1. 编译
|
### 1. 编译
|
||||||
|
|
||||||
**Windows(推荐):**
|
Go API 二进制(不含前端,Windows):
|
||||||
|
|
||||||
```bat
|
```bat
|
||||||
build.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
|
```bash
|
||||||
cd frontend && npm install && npm run build
|
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 Hub):**
|
||||||
|
|
||||||
|
同一镜像两种角色,建议直接用 `docker-compose.yml`(已含完整配置):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker pull hangzhang714128/jiang13-forum:latest
|
docker pull hangzhang714128/jiang13-forum:latest
|
||||||
docker run -d --name jiang13 \
|
docker compose up -d
|
||||||
-p 3000:3000 \
|
|
||||||
-v jiang13-data:/data \
|
|
||||||
--restart unless-stopped \
|
|
||||||
hangzhang714128/jiang13-forum:latest
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**数据持久化:** 容器内 `/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 /你的数据目录`
|
**若使用旧版镜像仍报 permission denied**,可在宿主机执行:`chown -R 1000:1000 /你的数据目录`
|
||||||
|
|
||||||
**可选环境变量(容器编排):**
|
**可选环境变量(容器编排):**
|
||||||
|
|
||||||
| 变量 | 说明 |
|
| 变量 | 容器 | 说明 |
|
||||||
|------|------|
|
|------|------|------|
|
||||||
| `JIANG13_HTTP_PORT` | HTTP 端口(默认 `3000`) |
|
| `GO_API_URL` | web | Go API 地址(默认 `http://127.0.0.1:3001`,Compose 中为 `http://api:3001`) |
|
||||||
| `JIANG13_DATA` | 数据目录(默认 `/data`) |
|
| `PORT` | web | Node 渲染服务器监听端口(默认 `3000`) |
|
||||||
| `JIANG13_JWT_SECRET` | JWT 密钥(留空则自动生成并写入 `/data/.jwt_secret`) |
|
| `JIANG13_HTTP_PORT` | api | HTTP 端口(默认 `3000`,Compose 中为 `3001`) |
|
||||||
| `JIANG13_CONFIG` | 配置文件路径 |
|
| `JIANG13_DATA` | api | 数据目录(默认 `/data`) |
|
||||||
| `JIANG13_WORK_PATH` | 工作目录 |
|
| `JIANG13_JWT_SECRET` | api | JWT 密钥(留空则自动生成并写入 `/data/.jwt_secret`) |
|
||||||
| `JIANG13_COMMUNITY_HUB` | 维护者选项:设为 `1` 时本站作为社区枢纽收报。官方站 `bbs.iioio.com` 会按域名自动成为枢纽,无需此变量;仅非官网的枢纽镜像 / 预发需要显式开启(见 `app.ini.example`) |
|
| `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(手动):**
|
**发布镜像到 Docker Hub(手动):**
|
||||||
|
|
||||||
@@ -238,14 +258,16 @@ docker push hangzhang714128/jiang13-forum:latest
|
|||||||
|
|
||||||
**1Panel 部署提示:**
|
**1Panel 部署提示:**
|
||||||
|
|
||||||
1. 容器镜像填 `hangzhang714128/jiang13-forum:latest`
|
1. 用 Compose 编排(`docker-compose.yml`)或手动创建两个容器:`api`(Go,内部 `3001`,挂载 `/data`)与 `web`(Node,映射 `3000:3000`)
|
||||||
2. 端口映射 `3000:3000`
|
2. `web` 容器环境变量 `GO_API_URL` 指向 `api` 容器(如 `http://api:3001`)
|
||||||
3. 挂载数据卷到容器内 `/data`(镜像会自动修正目录权限)
|
3. 数据卷只需挂在 `api` 容器的 `/data`(镜像会自动修正目录权限)
|
||||||
4. 首次访问 `http://服务器IP:3000/register` 注册管理员
|
4. 首次访问 `http://服务器IP:3000/register` 注册管理员
|
||||||
|
|
||||||
### 3. 直接启动(二进制)
|
### 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)
|
当前 **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
|
```bash
|
||||||
# Windows
|
# 1) 启动 Go API
|
||||||
.\jiang13-1.1.7-windows-amd64.exe
|
./jiang13 --port 3001 --data ./data
|
||||||
# 或改名为 jiang13.exe 后:
|
|
||||||
.\jiang13.exe
|
|
||||||
|
|
||||||
# Linux
|
# 2) 另开终端,构建并启动 Node 渲染服务器
|
||||||
chmod +x jiang13-1.1.7-linux-amd64
|
cd frontend && npm install && npm run build
|
||||||
./jiang13-1.1.7-linux-amd64
|
PORT=3000 GO_API_URL=http://127.0.0.1:3001 npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
本地刚编译的产物在 `dist/`:
|
Windows:
|
||||||
|
|
||||||
```bash
|
```powershell
|
||||||
# Windows
|
.\jiang13.exe --port 3001 --data .\data
|
||||||
.\dist\jiang13.exe
|
# 另开 PowerShell:
|
||||||
|
cd frontend; npm install; npm run build
|
||||||
# Linux / macOS
|
$env:PORT = '3000'; $env:GO_API_URL = 'http://127.0.0.1:3001'; npm start
|
||||||
./dist/jiang13
|
|
||||||
```
|
|
||||||
|
|
||||||
也可先复制示例配置再改端口 / 数据目录:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cp app.ini.example /opt/jiang13/app.ini
|
|
||||||
# 编辑 app.ini 后:
|
|
||||||
./jiang13
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. 首次使用
|
### 4. 首次使用
|
||||||
@@ -354,16 +365,17 @@ C:\jiang13\jiang13.exe --service start
|
|||||||
|
|
||||||
| 层级 | 技术 |
|
| 层级 | 技术 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| **后端** | Go 1.26 · Gin · GORM · SQLite |
|
| **后端** | Go 1.26 · Gin · GORM · SQLite(纯 API 服务) |
|
||||||
| **前端** | React 18 · TipTap · Radix UI · Tailwind CSS · TanStack Virtual |
|
| **前端** | React 19 · Vike SSR · react-router · TipTap · Radix UI · Tailwind CSS · TanStack Virtual |
|
||||||
| **构建** | Vite → `go:embed` 内嵌 SPA,单二进制发布 |
|
| **构建** | Vite 8(Rolldown)双构建:`dist/client` 静态资源 + `dist/server` SSR 入口 |
|
||||||
|
| **渲染** | Node 24 · Express + Vike `renderPage`(:3000 对外,API 反代至 Go :3001) |
|
||||||
| **认证** | bcrypt · JWT Cookie · 可选 OIDC Provider |
|
| **认证** | bcrypt · JWT Cookie · 可选 OIDC Provider |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 前端开发
|
## 前端开发
|
||||||
|
|
||||||
日常改前端不需要重新完整构建,Vite 支持秒级热更新(HMR,热模块替换):
|
日常改前端不需要重新完整构建,Vike dev 支持秒级热更新(HMR,热模块替换):
|
||||||
|
|
||||||
```bat
|
```bat
|
||||||
build.bat -Target dev
|
build.bat -Target dev
|
||||||
@@ -373,16 +385,17 @@ build.bat -Target dev
|
|||||||
make 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 运行数据不一致。
|
开发后端与 `dist/jiang13` 共用数据目录 `dist/data`(SQLite、上传、JWT 密钥等),避免 dev 与 dist 运行数据不一致。
|
||||||
|
|
||||||
**何时需要完整构建:**
|
**何时需要完整构建:**
|
||||||
|
|
||||||
- 修改 Go 代码或要发布单二进制 → `build.bat` / `make build`
|
- 修改 Go 代码 → `build.bat` / `make build`(仅编译 Go API)
|
||||||
|
- 验证生产 SSR / 发布部署 → `make serve`(构建前端 + 双进程起本地产)
|
||||||
- 更新 README 界面截图 → 见下方「更新截图」
|
- 更新 README 界面截图 → 见下方「更新截图」
|
||||||
|
|
||||||
> 直接访问 `:3000` 看到的是上次 build 嵌入的前端;开发时请用 `:5173`。
|
> 开发时请访问 `:5173`;`:3000` 只有在生产模式(Node 渲染服务器)下才有页面。
|
||||||
|
|
||||||
### 更新截图
|
### 更新截图
|
||||||
|
|
||||||
@@ -412,17 +425,20 @@ jiang13-forum/
|
|||||||
├── cmd/jiang13/ # 程序入口(含系统服务注册)
|
├── cmd/jiang13/ # 程序入口(含系统服务注册)
|
||||||
├── config/ # app.ini 与命令行配置
|
├── config/ # app.ini 与命令行配置
|
||||||
├── app.ini.example # 配置文件示例
|
├── app.ini.example # 配置文件示例
|
||||||
├── Dockerfile # 多阶段 Docker 构建
|
├── Dockerfile # 多阶段构建(前端 + Go API 共用镜像,双容器运行)
|
||||||
├── docker-compose.yml # 单容器 Compose 部署
|
├── docker-compose.yml # 双容器 Compose 部署(web :3000 + api :3001)
|
||||||
├── docker-entrypoint.sh # 容器启动脚本(修正 /data 卷权限)
|
├── docker-entrypoint.sh # api 容器启动脚本(修正 /data 卷权限)
|
||||||
├── .dockerignore
|
├── .dockerignore
|
||||||
├── model/ # GORM 模型与数据库迁移
|
├── model/ # GORM 模型与数据库迁移
|
||||||
├── service/ # 业务逻辑
|
├── service/ # 业务逻辑
|
||||||
├── handler/ # HTTP 处理器(前台 + 后台)
|
├── handler/ # HTTP 处理器(API + /api/home-boot SSR 聚合端点)
|
||||||
├── middleware/ # JWT 鉴权等
|
├── middleware/ # JWT 鉴权等
|
||||||
├── router/ # 路由注册
|
├── router/ # 路由注册(仅 API)
|
||||||
├── embed_static/ # go:embed 内嵌的 SPA
|
├── frontend/
|
||||||
├── frontend/ # React 源码(Vite 构建)
|
│ ├── pages/ # Vike 页面(feed=SSR 白名单 / spa=catch-all 壳 / _error)
|
||||||
|
│ ├── renderer/ # Vike 双端渲染入口(onRenderHtml / onRenderClient)
|
||||||
|
│ ├── server/ # Node 渲染服务器(express + renderPage + API 反代)
|
||||||
|
│ └── src/ # React 源码(含 src/ssr 服务端取数与作用域管理)
|
||||||
├── docs/screenshots/ # README 界面截图
|
├── docs/screenshots/ # README 界面截图
|
||||||
├── ROADMAP.md # 路线图与已知问题
|
├── ROADMAP.md # 路线图与已知问题
|
||||||
└── scripts/ # 开发辅助脚本(含截图)
|
└── scripts/ # 开发辅助脚本(含截图)
|
||||||
|
|||||||
46
build.ps1
46
build.ps1
@@ -3,7 +3,7 @@
|
|||||||
# .\build.ps1 -Target build-windows
|
# .\build.ps1 -Target build-windows
|
||||||
|
|
||||||
param(
|
param(
|
||||||
[ValidateSet('build', 'build-windows', 'build-linux', 'build-darwin', 'build-all', 'frontend', 'tidy', 'run', 'dev', 'clean', 'docker', 'compose-up', 'compose-down', 'help')]
|
[ValidateSet('build', 'web-build', 'frontend', 'build-windows', 'build-linux', 'build-darwin', 'build-all', 'tidy', 'run', 'serve', 'dev', 'clean', 'docker', 'compose-up', 'compose-down', 'help')]
|
||||||
[string]$Target = 'build'
|
[string]$Target = 'build'
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ function Ensure-Dir($path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Build-Frontend {
|
function Build-Frontend {
|
||||||
Write-Host '[frontend] npm run build...' -ForegroundColor Cyan
|
Write-Host '[frontend] npm run build (Vike: client + server)...' -ForegroundColor Cyan
|
||||||
Push-Location frontend
|
Push-Location frontend
|
||||||
try {
|
try {
|
||||||
if (-not (Test-Path node_modules)) {
|
if (-not (Test-Path node_modules)) {
|
||||||
@@ -69,13 +69,14 @@ function Build-Go([string]$OutFile, [string]$GoOS = '', [string]$GoArch = '') {
|
|||||||
|
|
||||||
switch ($Target) {
|
switch ($Target) {
|
||||||
'help' {
|
'help' {
|
||||||
Write-Host '.\build.ps1 build current platform'
|
Write-Host '.\build.ps1 build Go API binary (current platform)'
|
||||||
Write-Host '.\build.ps1 -Target frontend frontend only'
|
Write-Host '.\build.ps1 -Target web-build build frontend (Vike client + server)'
|
||||||
|
Write-Host '.\build.ps1 -Target serve production run: Node :3000 + Go API :3001'
|
||||||
Write-Host '.\build.ps1 -Target build-windows'
|
Write-Host '.\build.ps1 -Target build-windows'
|
||||||
Write-Host '.\build.ps1 -Target build-linux'
|
Write-Host '.\build.ps1 -Target build-linux'
|
||||||
Write-Host '.\build.ps1 -Target build-all'
|
Write-Host '.\build.ps1 -Target build-all'
|
||||||
Write-Host '.\build.ps1 -Target run backend only (port 3000)'
|
Write-Host '.\build.ps1 -Target run Go API only (port 3000, no pages)'
|
||||||
Write-Host '.\build.ps1 -Target dev backend + Vite HMR (recommended for frontend dev)'
|
Write-Host '.\build.ps1 -Target dev Vike HMR :5173 + Go API :3000 (recommended for frontend dev)'
|
||||||
Write-Host '.\build.ps1 -Target tidy'
|
Write-Host '.\build.ps1 -Target tidy'
|
||||||
Write-Host '.\build.ps1 -Target clean'
|
Write-Host '.\build.ps1 -Target clean'
|
||||||
Write-Host '.\build.ps1 -Target docker build Docker image'
|
Write-Host '.\build.ps1 -Target docker build Docker image'
|
||||||
@@ -84,6 +85,7 @@ switch ($Target) {
|
|||||||
Write-Host ''
|
Write-Host ''
|
||||||
Write-Host 'Note: Windows "make" is often Embarcadero MAKE, not GNU Make.'
|
Write-Host 'Note: Windows "make" is often Embarcadero MAKE, not GNU Make.'
|
||||||
}
|
}
|
||||||
|
'web-build' { Build-Frontend }
|
||||||
'frontend' { Build-Frontend }
|
'frontend' { Build-Frontend }
|
||||||
'tidy' { go mod tidy }
|
'tidy' { go mod tidy }
|
||||||
'clean' {
|
'clean' {
|
||||||
@@ -94,11 +96,35 @@ switch ($Target) {
|
|||||||
Ensure-Dir $DevDataDir
|
Ensure-Dir $DevDataDir
|
||||||
go run $MainPkg --data $DevDataDir
|
go run $MainPkg --data $DevDataDir
|
||||||
}
|
}
|
||||||
|
'serve' {
|
||||||
|
Build-Frontend
|
||||||
|
Build-Go -OutFile $AppName
|
||||||
|
Ensure-Dir $DevDataDir
|
||||||
|
$root = (Get-Location).Path
|
||||||
|
$goExe = Join-Path $root "$BuildDir\$AppName.exe"
|
||||||
|
Write-Host '[serve] 正在新窗口启动 Go API (:3001)...' -ForegroundColor Cyan
|
||||||
|
Start-Process powershell -ArgumentList @(
|
||||||
|
'-NoExit', '-Command',
|
||||||
|
"Set-Location '$root'; & '$goExe' --data '$DevDataDir' --port 3001"
|
||||||
|
) | Out-Null
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
Write-Host '[serve] 启动 Node 渲染服务器 (:3000)...' -ForegroundColor Cyan
|
||||||
|
Push-Location frontend
|
||||||
|
try {
|
||||||
|
$env:PORT = '3000'
|
||||||
|
$env:GO_API_URL = 'http://127.0.0.1:3001'
|
||||||
|
npm start
|
||||||
|
} finally {
|
||||||
|
Remove-Item Env:PORT -ErrorAction SilentlyContinue
|
||||||
|
Remove-Item Env:GO_API_URL -ErrorAction SilentlyContinue
|
||||||
|
Pop-Location
|
||||||
|
}
|
||||||
|
}
|
||||||
'dev' {
|
'dev' {
|
||||||
$root = (Get-Location).Path
|
$root = (Get-Location).Path
|
||||||
Ensure-Dir $DevDataDir
|
Ensure-Dir $DevDataDir
|
||||||
Write-Host ''
|
Write-Host ''
|
||||||
Write-Host '[dev] 前端开发 : http://localhost:5173 (Vite HMR)' -ForegroundColor Green
|
Write-Host '[dev] 前端开发 : http://localhost:5173 (Vike HMR)' -ForegroundColor Green
|
||||||
Write-Host '[dev] 后端 API : http://localhost:3000 (Go)' -ForegroundColor Green
|
Write-Host '[dev] 后端 API : http://localhost:3000 (Go)' -ForegroundColor Green
|
||||||
Write-Host "[dev] 数据目录 : $DevDataDir (与 dist 二进制一致)" -ForegroundColor Green
|
Write-Host "[dev] 数据目录 : $DevDataDir (与 dist 二进制一致)" -ForegroundColor Green
|
||||||
Write-Host '[dev] 提示 : 请访问 5173 端口,Vite 会自动代理 API 到 3000' -ForegroundColor Yellow
|
Write-Host '[dev] 提示 : 请访问 5173 端口,Vite 会自动代理 API 到 3000' -ForegroundColor Yellow
|
||||||
@@ -117,24 +143,18 @@ switch ($Target) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
'build' {
|
'build' {
|
||||||
Build-Frontend
|
|
||||||
Build-Go -OutFile $AppName
|
Build-Go -OutFile $AppName
|
||||||
}
|
}
|
||||||
'build-windows' {
|
'build-windows' {
|
||||||
Build-Frontend
|
|
||||||
Build-Go -OutFile $AppName -GoOS 'windows' -GoArch 'amd64'
|
Build-Go -OutFile $AppName -GoOS 'windows' -GoArch 'amd64'
|
||||||
}
|
}
|
||||||
'build-linux' {
|
'build-linux' {
|
||||||
Write-Host '[build-linux] will npm run build then go:embed SPA' -ForegroundColor Yellow
|
|
||||||
Build-Frontend
|
|
||||||
Build-Go -OutFile "$AppName-linux-amd64" -GoOS 'linux' -GoArch 'amd64'
|
Build-Go -OutFile "$AppName-linux-amd64" -GoOS 'linux' -GoArch 'amd64'
|
||||||
}
|
}
|
||||||
'build-darwin' {
|
'build-darwin' {
|
||||||
Build-Frontend
|
|
||||||
Build-Go -OutFile "$AppName-darwin-arm64" -GoOS 'darwin' -GoArch 'arm64'
|
Build-Go -OutFile "$AppName-darwin-arm64" -GoOS 'darwin' -GoArch 'arm64'
|
||||||
}
|
}
|
||||||
'build-all' {
|
'build-all' {
|
||||||
Build-Frontend
|
|
||||||
Build-Go -OutFile $AppName -GoOS 'windows' -GoArch 'amd64'
|
Build-Go -OutFile $AppName -GoOS 'windows' -GoArch 'amd64'
|
||||||
Build-Go -OutFile "$AppName-linux-amd64" -GoOS 'linux' -GoArch 'amd64'
|
Build-Go -OutFile "$AppName-linux-amd64" -GoOS 'linux' -GoArch 'amd64'
|
||||||
Build-Go -OutFile "$AppName-darwin-arm64" -GoOS 'darwin' -GoArch 'arm64'
|
Build-Go -OutFile "$AppName-darwin-arm64" -GoOS 'darwin' -GoArch 'arm64'
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
# 姜十三论坛 — Docker Compose 单服务部署
|
# 姜十三论坛 — Docker Compose 双服务部署
|
||||||
|
# 架构:web(Node :3000 对外:Vike SSR + 静态资源 + API 反代) → api(Go :3001 内部:纯 API + /data 数据卷)
|
||||||
# 启动:docker compose up -d --build
|
# 启动:docker compose up -d --build
|
||||||
# 或:make compose-up / build.bat -Target compose-up
|
# 或:make compose-up / build.bat -Target compose-up
|
||||||
|
|
||||||
services:
|
services:
|
||||||
jiang13:
|
api:
|
||||||
image: hangzhang714128/jiang13-forum:latest
|
image: hangzhang714128/jiang13-forum:latest
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
args:
|
args:
|
||||||
VERSION: dev
|
VERSION: dev
|
||||||
ports:
|
command: ["--work-path", "/app", "--data", "/data", "--port", "3001"]
|
||||||
- "3000:3000"
|
|
||||||
volumes:
|
volumes:
|
||||||
- jiang13-data:/data
|
- jiang13-data:/data
|
||||||
# 可选:挂载自定义 app.ini(只读)
|
# 可选:挂载自定义 app.ini(只读)
|
||||||
@@ -20,10 +20,34 @@ services:
|
|||||||
TZ: Asia/Shanghai
|
TZ: Asia/Shanghai
|
||||||
# 可选:固定 JWT 密钥(留空则自动生成并持久化到 /data/.jwt_secret)
|
# 可选:固定 JWT 密钥(留空则自动生成并持久化到 /data/.jwt_secret)
|
||||||
# JIANG13_JWT_SECRET: your-secret-here
|
# JIANG13_JWT_SECRET: your-secret-here
|
||||||
|
expose:
|
||||||
|
- "3001"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
stop_grace_period: 15s
|
stop_grace_period: 15s
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/health"]
|
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3001/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 15s
|
||||||
|
|
||||||
|
web:
|
||||||
|
image: hangzhang714128/jiang13-forum:latest
|
||||||
|
entrypoint: ["node", "server/index.mjs"]
|
||||||
|
working_dir: /app/frontend
|
||||||
|
user: "1000:1000"
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
TZ: Asia/Shanghai
|
||||||
|
PORT: "3000"
|
||||||
|
GO_API_URL: http://api:3001
|
||||||
|
depends_on:
|
||||||
|
api:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/healthz').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 3
|
retries: 3
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
# api 容器入口:默认运行 Go API(web 容器在 compose 中以 node 用户直接覆盖 entrypoint)。
|
||||||
# 宿主机 / 1Panel 挂载的 volume 常为 root 所有(uid 0),
|
# 宿主机 / 1Panel 挂载的 volume 常为 root 所有(uid 0),
|
||||||
# 而应用以 jiang13(uid 1000)运行,需在启动时修正 /data 权限后降权。
|
# 而应用以 uid 1000(node 镜像内置用户)运行,需在启动时修正 /data 权限后降权。
|
||||||
|
|
||||||
DATA_DIR="/data"
|
DATA_DIR="/data"
|
||||||
|
|
||||||
if [ "$(id -u)" = '0' ]; then
|
if [ "$(id -u)" = '0' ]; then
|
||||||
mkdir -p "$DATA_DIR"
|
mkdir -p "$DATA_DIR"
|
||||||
chown -R jiang13:jiang13 "$DATA_DIR"
|
chown -R 1000:1000 "$DATA_DIR"
|
||||||
exec su-exec jiang13 /app/jiang13 "$@"
|
exec gosu 1000:1000 /app/jiang13 "$@"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exec /app/jiang13 "$@"
|
exec /app/jiang13 "$@"
|
||||||
|
|||||||
Reference in New Issue
Block a user