Files
backend/README.md
Wu XiangYu cea22951b9
Some checks failed
Build / build (push) Failing after 39m5s
Build / build-docker (push) Has been skipped
docs: 更新 README,新增对象存储桶策略与 CORS 配置说明
- 新增'对象存储配置'章节:存储桶创建、匿名只读策略、CORS 配置(前端3D渲染必需)
- 明确后端不自动创建存储桶,需手动配置
- 按当前 dev 代码更新:Go 1.25+、Casbin v3、fx 子配置 Provider
- 补充默认管理员账号、RUSTFS_PUBLIC_URL 说明、Docker 部署细节
- 修正启动命令、目录结构、Swagger 产物说明
2026-07-08 17:37:24 +08:00

13 KiB
Raw Blame History

CarrotSkin Backend

一个功能完善的 Minecraft 皮肤站后端,基于 Go + Gin 构建,覆盖用户认证、材质管理、角色档案、审计日志等核心能力,并提供完整的 Swagger 文档与容器友好的环境变量配置。

功能亮点

  • 账号体系:注册 / 登录 / JWT 鉴权 / Yggdrasil 密码同步 / 用户积分
  • 邮箱与验证码:验证码发送频率控制、邮箱绑定与变更
  • 材质中心:皮肤/披风上传、搜索、收藏、下载统计、Hash 去重
  • 角色档案Minecraft Profile 管理、RSA 密钥对生成、活跃档案切换
  • 存储与上传RustFS/MinIOS3 兼容直接上传Hash 分片存储
  • 任务与日志:登录日志、操作审计、材质下载记录、定时任务
  • 权限体系Casbin RBAC支持细粒度路线授权
  • 配置管理100% 依赖环境变量,SERVER_SWAGGER_ENABLED 控制 Swagger
  • 可观测性Zap 结构化日志、统一 API 响应模型

🛠 技术栈

类型 选型
语言 / 运行时 Go 1.25+
Web 框架 Gin
ORM GORM (PostgreSQL 驱动)
数据库 PostgreSQL 15+
缓存 / 消息 Redis 6+
对象存储 RustFS / MinIOS3 兼容)
权限控制 Casbin v3
依赖注入 uber-go/fx
配置 Viper + godotenv + .env
API 文档 swaggo / Swagger UI
滑动验证码 wenlng/go-captcha
日志 Uber Zap

📁 目录结构

backend/
├── cmd/server/                # 应用入口main.go极简 fx 装配)
├── internal/
│   ├── app/                   # uber-go/fx 装配层(唯一依赖注入入口)
│   │   ├── module.go          # 聚合根 Module
│   │   ├── infra_module.go    # Logger/DB/Redis/Storage/Email/JWT/Casbin + 子配置 Provider + Lifecycle
│   │   ├── container_module.go # Container 聚合 + 迁移/路由/HTTP/任务生命周期
│   │   ├── repository_module.go # Repository Provider占位Container 模式暂未启用)
│   │   ├── service_module.go   # Service Provider占位
│   │   ├── handler_module.go   # Handler Provider占位
│   │   ├── server_module.go    # HTTP 服务器模块(占位)
│   │   └── task_module.go      # 后台任务模块(占位)
│   ├── container/             # 过渡期手动 DI 容器fx 迁移期的聚合层)
│   ├── handler/              # HTTP Handler 与 Swagger 注解
│   ├── service/              # 业务逻辑(接口 + 实现)
│   ├── repository/           # 数据访问(接口 + GORM 实现)
│   ├── model/                # GORM 数据模型与响应结构
│   ├── types/                # 请求/响应 DTO
│   ├── errors/               # 统一错误定义apperrors
│   ├── middleware/           # Gin 中间件
│   └── task/                 # 定时任务与后台作业
├── pkg/                      # 可复用组件config、database、auth、logger、redis、storage、email、utils
├── configs/casbin/           # Casbin RBAC 模型定义rbac_model.conf
├── start.sh                  # 启动脚本(自动 swag init
├── docker-compose.yml        # 本地容器编排(含 rustfs-init 自动建桶)
├── .env.example              # 环境变量示例
└── go.mod                    # Go Module 定义

前置要求

  • Go 1.25+
  • PostgreSQL 15+
  • Redis 6+
  • RustFS / MinIO或其他兼容 S3 的对象存储,用于皮肤与头像)
  • swag生成 Swagger 文档,见 https://github.com/swaggo/swag

🚀 快速开始

  1. 克隆仓库

    git clone <repo>
    cd backend
    
  2. 安装依赖

    go mod download
    
  3. 配置环境变量

    cp .env.example .env
    # 根据实际环境填写数据库、Redis、对象存储、邮件等信息
    
  4. 初始化数据库

    createdb carrotskin
    # 或 psql -c "CREATE DATABASE carrotskin;"
    

    应用启动时会执行 AutoMigrate,自动创建 / 更新表结构,并写入种子数据(默认管理员 + Casbin 规则)。

  5. 准备对象存储(创建存储桶 + 配置策略与跨域,见下方 🪣 对象存储配置 章节

  6. 启动服务

    • 推荐./start.sh(自动 swag init,随后 go run ./cmd/server
    • 手动启动
      swag init -g cmd/server/main.go -o docs
      go run ./cmd/server
      
  7. 访问接口

    • API Root: http://localhost:8080
    • Swagger: http://localhost:8080/swagger/index.html(需 SERVER_SWAGGER_ENABLED=true
    • 健康检查: http://localhost:8080/health

默认管理员

首次启动会自动 seed 一个管理员账号,首次登录后请立即修改密码

用户名 密码 邮箱
admin admin123456 admin@example.com

🪣 对象存储配置

后端使用 S3 兼容的对象存储RustFS / MinIO保存皮肤与头像需要两个存储桶

桶名(由环境变量指定) 默认名 用途
RUSTFS_BUCKET_TEXTURES carrot-skin-textures 皮肤 / 披风文件
RUSTFS_BUCKET_AVATARS carrot-skin-avatars 用户头像文件

⚠️ 后端不会自动创建存储桶。请在启动后端前,手动创建这两个桶并配置好访问策略与跨域,否则上传会失败、前端 3D 皮肤将无法渲染。

步骤 1创建存储桶

在 RustFS / MinIO 控制台(默认 http://<host>:9001)创建上述两个桶,或使用 mc 客户端:

mc alias set myrustfs http://<host>:9000 <access-key> <secret-key>
mc mb myrustfs/carrot-skin-textures --ignore-existing
mc mb myrustfs/carrot-skin-avatars  --ignore-existing

步骤 2配置桶访问策略匿名只读

皮肤和头像需要被浏览器与 Minecraft 启动器匿名下载(后端用密钥上传即可)。给每个桶配置如下 S3 Bucket Policycarrot-skin-textures 为例,avatars 桶把 Resource 里的桶名换掉即可):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::carrot-skin-textures/*"
    }
  ]
}

等价的 mc 命令:

mc anonymous set download myrustfs/carrot-skin-textures
mc anonymous set download myrustfs/carrot-skin-avatars

步骤 3配置 CORS跨域前端 3D 渲染必需

🔴 这一步极其关键。普通的 <img> 标签加载图片不要求 CORS但前端用 skinview3d / three.js 通过 WebGL 纹理渲染 3D 皮肤时,浏览器强制要求图片响应带 Access-Control-Allow-Origin 头,否则 canvas 会被判定为 tainted3D 皮肤将加载失败/显示空白。

在 RustFS / MinIO 控制台为两个桶各配置一条 CORS 规则,允许前端来源以 GET 方法跨域访问。CORS 规则 JSON贴入桶的 CORS 配置编辑器):

[
  {
    "AllowedOrigins": ["*"],
    "AllowedMethods": ["GET", "HEAD"],
    "AllowedHeaders": ["*"],
    "ExposeHeaders": ["ETag"],
    "MaxAgeSeconds": 86400
  }
]

生产环境建议把 AllowedOrigins 收紧为你的前端实际域名(如 ["https://skins.example.com"]),而不是 *

等价的 mc 命令(将 JSON 存为 cors.json 后应用):

mc cors add myrustfs/carrot-skin-textures < cors.json
mc cors add myrustfs/carrot-skin-avatars  < cors.json

验证存储配置是否生效

匿名 GET 任意一个桶内对象,期望返回 HTTP 200 且响应头包含 access-control-allow-origin

curl -I -H "Origin: http://localhost:3000" http://<host>:9000/carrot-skin-textures/<某对象路径>
# 期望看到:
#   HTTP/1.1 200 OK
#   access-control-allow-origin: *

access-control-allow-origin 缺失,说明 CORS 未生效,前端 3D 皮肤将无法渲染。

关于 RUSTFS_PUBLIC_URL

RUSTFS_PUBLIC_URL 决定后端生成的文件访问 URL 前缀(<public_url>/<bucket>/<object>)。

  • 留空时:用 RUSTFS_ENDPOINT + 协议拼接(适合 endpoint 本身就是浏览器可达地址的场景)
  • 生产环境:务必填写浏览器可访问的公网地址,例如 https://rustfs.example.com,否则后端返回给前端的 URL 浏览器打不开

🐳 Docker 部署

docker-compose.yml 提供了完整的本地编排,包含 PostgreSQL、Redis、RustFS以及一个一次性的 rustfs-init 服务自动创建存储桶并设置匿名下载策略:

# 启动全部基础设施(含存储)
docker compose --profile storage up -d

# 仅启动应用
docker compose up -d

rustfs-init 使用 mc 完成建桶与策略配置。CORS 仍需按上文 步骤 3 手动配置。

⚙️ 关键环境变量

变量 说明 示例
SERVER_PORT 服务监听端口 :8080
SERVER_MODE Gin 模式debug/release debug
SERVER_SWAGGER_ENABLED 是否暴露 Swagger UI true
DATABASE_HOST / DATABASE_PORT PostgreSQL 地址 localhost / 5432
DATABASE_USERNAME / DATABASE_PASSWORD 数据库凭据 postgres
DATABASE_NAME 数据库名称 carrotskin
REDIS_HOST / REDIS_PORT Redis 地址 localhost / 6379
REDIS_PASSWORD Redis 密码(无可为空) ``
RUSTFS_ENDPOINT RustFS/MinIO 访问地址(后端内网连接用) 127.0.0.1:9000
RUSTFS_PUBLIC_URL 生成文件 URL 的公开前缀(浏览器可达) https://rustfs.example.com
RUSTFS_ACCESS_KEY / RUSTFS_SECRET_KEY 对象存储凭据 minioadmin
RUSTFS_BUCKET_TEXTURES / RUSTFS_BUCKET_AVATARS 存储桶名称 carrot-skin-textures
JWT_SECRET JWT 签名密钥 change-me
EMAIL_ENABLED 是否开启邮件服务 true
EMAIL_SMTP_HOST / EMAIL_SMTP_PORT SMTP 配置 smtp.example.com / 587
ENVIRONMENT 应用环境(用于 service 层 IsTestEnvironment() 短路) production
SECURITY_ALLOWED_ORIGINS API CORS 允许的来源(逗号分隔) *
SECURITY_ALLOWED_DOMAINS 头像/材质 URL 允许的域名(逗号分隔) localhost,127.0.0.1

更多变量请参考 .env.example.env.docker.example

🧪 常用命令

# 运行单元测试
go test ./...

# 重新生成 swagger
swag init -g cmd/server/main.go -o docs

# 代码格式化 / 静态检查
gofmt -w .
golangci-lint run (若已安装)

🧱 架构说明

  • 分层设计Handler → Service → Repository → Model层次清晰、职责单一。
    • Handler 不得直连 *gorm.DB*Repository,必须通过 Service。
    • Service 的数据访问通过 Repository 接口(个别需要跨表事务的场景除外)。
  • 依赖注入uber-go/fx
    • internal/app/ 是唯一的依赖装配入口。每个基础设施 / 仓储 / 服务作为 fx.Provider 注册。
    • 子配置 Providerinfra_module.go*config.Config 提取 DatabaseConfig / RedisConfig / RustFSConfig / LogConfig / EmailConfig 等子结构,按值注入到各 pkg/* 包的构造函数。
    • 生命周期管理DB / Redis / Storage / Logger / HTTP Server / Task Runner 通过 fx.LifecycleOnStart / OnStop Hook 统一管理,彻底替代散落在 main()defer Close() 链。
    • 优雅关闭fx.New(app.Module(cfg)).Run() 监听 SIGINT / SIGTERM,按依赖逆序触发 OnStop,先关 HTTP Server30s 超时),再关 Task Runner最后关 DB/Redis。
    • Dev/Test 自动回退Redis 连接失败且环境为 development / test / dev / USE_MINIREDIS=true 时自动启用 miniredis
    • 启动时错误检查:循环依赖、缺失 Provider、Provider 返回 error 全部在 fx.New() 阶段立即检测。
  • 错误与响应统一
    • 错误统一在 internal/errors/(别名 apperrors),通过 errors.Is/As 匹配。
    • 响应统一使用 model.Response / model.PaginationResponse / model.NewErrorResponse
    • Middleware 错误响应也走 model.NewErrorResponse,不再使用裸 gin.H
  • 配置优先级config.Load() 读取 .env → 环境变量 → viper 默认值;*config.Configfx.Supply 注入。
  • Swagger 注解:所有 Handler、模型、DTO 均补齐 @Summary / @Description / @Success,可直接生成 OpenAPI 文档。

📝 Swagger 说明

  • start.sh 会在启动前执行 swag init -g cmd/server/main.go -o docs,生成 docs/ 目录(docs.go / swagger.json / swagger.yaml)。
  • docs/ 为生成产物,不在仓库内跟踪,首次启动会自动生成。
  • 通过 SERVER_SWAGGER_ENABLED=false 可在生产环境关闭 Swagger UI 暴露。

🤝 贡献指南

  1. Fork & Clone
  2. 创建特性分支:git checkout -b feature/xxx
  3. 编写代码并补全测试 / Swagger 注释
  4. 提交时附上变更说明

📄 许可证

该项目未附带开源许可证,默认保留所有权利。若需对外使用,请先与作者确认协议。


如需了解业务细节或 API 调用示例,请参考 docs/swagger.yaml 或运行服务后访问 Swagger UI。祝编码愉快🍀