Merge branch 'main' into dev
This commit is contained in:
commit
91e1ed456a
5 files changed
+97
-59
No files matched your search
@@ -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 .
|
||||
|
||||
# 指定版本号
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user