diff --git a/README.md b/README.md index 6942d3e..af6e688 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,9 @@ redis: jwt: secret: "your-secret-key-change-in-production" + +crypto: + encryptionKey: "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2" ``` ### 4. 执行数据库迁移 @@ -267,31 +270,47 @@ curl http://localhost:18888/v1/chat/completions \ ## Docker 部署 -项目提供多阶段 Dockerfile(前端构建 → Go 编译 → 精简运行镜像)和完整的 Docker Compose 编排,包含 PostgreSQL、Redis 和应用服务。 +### 快速部署(推荐) -### 快速启动(推荐) +只需两个文件,无需克隆整个仓库: ```bash -# 1. 复制示例配置文件并重命名 -cp manifest/docker/config.example.yaml manifest/docker/config.yaml +# 1. 创建部署目录 +mkdir team-api && cd team-api -# 2. 编辑配置文件(修改数据库密码、Redis 密码、JWT 密钥等) -vim manifest/docker/config.yaml +# 2. 下载 docker-compose.yaml 和示例配置 +curl -O https://raw.githubusercontent.com/qianfree/team-api/main/manifest/docker/docker-compose.yaml +curl -o config.yaml https://raw.githubusercontent.com/qianfree/team-api/main/manifest/docker/config.example.yaml -# 3. 修改 docker-compose.yaml 中的密码(与 config.yaml 保持一致) +# 3. 编辑配置文件(修改数据库密码、Redis 密码、JWT 密钥、加密密钥等) +vim config.yaml + +# 4. 修改 docker-compose.yaml 中的密码(与 config.yaml 保持一致) # - POSTGRES_PASSWORD # - Redis --requirepass +vim docker-compose.yaml -# 4. 一键启动所有服务 -docker compose -f manifest/docker/docker-compose.yaml up -d +# 5. 一键启动所有服务 +docker compose up -d ``` 服务启动后访问 `http://localhost:18888`,首次部署会进入系统初始化向导(`/api/setup`)。 -### 仅构建镜像 +### 从源码构建 ```bash -# 构建镜像(多阶段构建,自动编译前端和后端) +# 克隆仓库 +git clone https://github.com/qianfree/team-api.git +cd team-api + +# 构建并启动(多阶段构建:前端编译 → Go 编译 → 精简运行镜像) +docker compose --profile build up -d --build +``` + +也可以单独构建镜像推送到自己的 Registry: + +```bash +# 构建镜像 docker build -t team-api:latest -f manifest/docker/Dockerfile . # 指定版本号 diff --git a/internal/consts/consts.go b/internal/consts/consts.go index c8043a0..0413d3f 100644 --- a/internal/consts/consts.go +++ b/internal/consts/consts.go @@ -2,6 +2,7 @@ package consts import ( "os" + "path/filepath" "strings" "github.com/gogf/gf/v2/errors/gcode" @@ -9,25 +10,47 @@ import ( ) // Version can be set at build time via -ldflags "-X github.com/qianfree/team-api/internal/consts.Version=x.y.z". -// Falls back to reading the VERSION file in the project root, or "dev" if unavailable. +// Falls back to reading the VERSION file, or "dev" if unavailable. var Version = "" func init() { if Version != "" { return } - data, err := os.ReadFile("VERSION") - if err != nil { - Version = "dev" - return - } - if v := strings.TrimSpace(string(data)); v != "" { + v := readVersionFile() + if v != "" { Version = v } else { Version = "dev" } } +// readVersionFile tries multiple locations to find the VERSION file: +// 1. Current working directory (development mode) +// 2. Same directory as the running binary (deployment mode) +func readVersionFile() string { + // Candidate paths to search + candidates := []string{"VERSION"} + + // Add binary's directory + if exe, err := os.Executable(); err == nil { + if exe, err = filepath.EvalSymlinks(exe); err == nil { + candidates = append(candidates, filepath.Join(filepath.Dir(exe), "VERSION")) + } + } + + for _, path := range candidates { + data, err := os.ReadFile(path) + if err != nil { + continue + } + if v := strings.TrimSpace(string(data)); v != "" { + return v + } + } + return "" +} + // Business error variables (using gerror for full stack traces) var ( ErrUnauthorized = gerror.NewCode(gcode.New(CodeUnauthorized, MsgUnauthorized, nil), MsgUnauthorized) diff --git a/internal/logic/update/checker.go b/internal/logic/update/checker.go index 6297150..c851088 100644 --- a/internal/logic/update/checker.go +++ b/internal/logic/update/checker.go @@ -33,17 +33,6 @@ func CheckForUpdate(ctx context.Context, force bool) (*CheckResult, error) { } } - // Dev builds skip update check - if consts.Version == "dev" { - return &CheckResult{ - CurrentVersion: consts.Version, - LatestVersion: consts.Version, - HasUpdate: false, - CheckedAt: gtime.Now(), - DeploymentMode: GetDeploymentMode(), - }, nil - } - // Call GitHub API release, etag, err := fetchLatestRelease(ctx) if err != nil { @@ -65,16 +54,23 @@ func CheckForUpdate(ctx context.Context, force bool) (*CheckResult, error) { }, nil } - // Parse versions - currentVer, err := semver.NewVersion(strings.TrimPrefix(consts.Version, "v")) - if err != nil { - return nil, fmt.Errorf("invalid current version %q: %w", consts.Version, err) - } - latestStr := strings.TrimPrefix(release.TagName, "v") - latestVer, err := semver.NewVersion(latestStr) - if err != nil { - return nil, fmt.Errorf("invalid latest version %q: %w", release.TagName, err) + + // Determine if update is available + var hasUpdate bool + if consts.Version == "dev" { + // Dev builds: always report an update if a remote release exists + hasUpdate = true + } else { + currentVer, err := semver.NewVersion(strings.TrimPrefix(consts.Version, "v")) + if err != nil { + return nil, fmt.Errorf("invalid current version %q: %w", consts.Version, err) + } + latestVer, err := semver.NewVersion(latestStr) + if err != nil { + return nil, fmt.Errorf("invalid latest version %q: %w", release.TagName, err) + } + hasUpdate = latestVer.GreaterThan(currentVer) } // Find matching asset for current platform @@ -95,7 +91,7 @@ func CheckForUpdate(ctx context.Context, force bool) (*CheckResult, error) { result := &CheckResult{ CurrentVersion: consts.Version, LatestVersion: latestStr, - HasUpdate: latestVer.GreaterThan(currentVer), + HasUpdate: hasUpdate, ReleaseNotes: release.Body, ReleaseURL: release.HTMLURL, PublishedAt: release.PublishedAt, diff --git a/manifest/docker/Dockerfile b/manifest/docker/Dockerfile index 5b40301..6b061c3 100644 --- a/manifest/docker/Dockerfile +++ b/manifest/docker/Dockerfile @@ -7,7 +7,7 @@ # Override example: docker compose build --build-arg GOPROXY=https://proxy.golang.org,direct # Stage 1: Frontend build -FROM oven/bun:1-alpine AS frontend +FROM --platform=$BUILDPLATFORM oven/bun:1-alpine AS frontend ARG BUN_CONFIG_REGISTRY=https://registry.npmmirror.com ARG VERSION=dev @@ -34,7 +34,7 @@ RUN if [ "$VERSION" = "dev" ]; then \ cd ../tenant && VITE_APP_VERSION=$VERSION bun run build # Stage 2: Go build -FROM golang:1.25-alpine AS builder +FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS builder ARG GOPROXY=https://goproxy.cn,direct ENV GOPROXY=${GOPROXY} @@ -87,6 +87,7 @@ COPY --from=builder /build/manifest/i18n /app/i18n COPY --from=builder /build/migrations /app/migrations RUN mkdir -p /app/config /app/logs && \ + touch /app/config/config.yaml && \ chown -R team-api:team-api /app USER team-api diff --git a/manifest/docker/docker-compose.yaml b/manifest/docker/docker-compose.yaml index c4a713e..14e14f6 100644 --- a/manifest/docker/docker-compose.yaml +++ b/manifest/docker/docker-compose.yaml @@ -1,20 +1,16 @@ # ============================================================================= # Team-API Docker Compose 配置文件 # ============================================================================= -# 使用说明: -# 1. 修改下方 "数据库配置" 中的 POSTGRES_PASSWORD -# 2. 修改下方 "Redis 配置" 中的 --requirepass 密码 -# 3. 将 manifest/docker/config.yaml 修改为实际配置 -# (确保数据库密码和 Redis 密码与上方设置一致) -# 4. (可选)复制 .env.example 为 .env 并按需调整镜像加速配置 -# 5. 运行: docker compose up -d +# 快速部署(仅拉取镜像,只需本文件 + config.yaml): +# docker compose up -d # -# 镜像加速说明(大陆用户): -# 构建时默认使用 goproxy.cn 和 npmmirror 作为 Go/Bun 的下载源。 -# 拉取基础镜像缓慢时,可配置 Docker daemon 镜像加速器: -# 编辑 /etc/docker/daemon.json,添加: -# {"registry-mirrors": ["https://mirror.ccs.tencentyun.com", "https://docker.1ms.run"]} -# 然后重启 Docker: systemctl restart docker +# 本地构建(需要完整项目源码): +# docker compose --profile build up -d --build +# +# 配置步骤: +# 1. 复制 config.example.yaml 为 config.yaml 并修改数据库密码、Redis 密码等 +# 2. 修改下方 POSTGRES_PASSWORD 和 Redis --requirepass(与 config.yaml 保持一致) +# 3. 运行: docker compose up -d # ============================================================================= services: @@ -22,17 +18,20 @@ services: # PostgreSQL 数据库 # ----------------------------------------------------------------------------- postgres: - image: postgres:17-alpine + image: postgres:18-alpine container_name: team-api-postgres restart: unless-stopped environment: TZ: Asia/Shanghai POSTGRES_USER: team_api - POSTGRES_PASSWORD: team_api_secret # 请修改为强密码 + POSTGRES_PASSWORD: team_api_secret # 请修改为强密码(PG18 默认 SCRAM-SHA-256 加密) POSTGRES_DB: team_api - PGDATA: /var/lib/postgresql/data/pgdata + # PG18 默认 PGDATA 为 /var/lib/postgresql/18/docker,VOLUME 为 /var/lib/postgresql + # 不再需要手动设置 PGDATA,镜像已按版本号自动管理子目录 + # PG18 默认启用 data checksums,如从旧版升级且旧集群未启用校验和, + # 需添加: POSTGRES_INITDB_ARGS: "--no-data-checksums" volumes: - - postgres_data:/var/lib/postgresql/data + - postgres_data:/var/lib/postgresql healthcheck: test: ["CMD-SHELL", "pg_isready -U team_api -d team_api"] interval: 10s @@ -65,7 +64,7 @@ services: # Team-API 应用 # ----------------------------------------------------------------------------- app: - image: team-api:${VERSION:-latest} + image: qian5/team-api:${VERSION:-latest} build: context: ../.. dockerfile: manifest/docker/Dockerfile