# SproutGate 后端文档

## 技术栈

- **语言**：Go 1.20+
- **Web 框架**：Gin；跨域中间件 **`github.com/gin-contrib/cors`**
- **数据存储**：MySQL（通过 GORM + `gorm.io/driver/mysql`）
- **JWT**：`github.com/golang-jwt/jwt/v5`
- **密码哈希**：`golang.org/x/crypto/bcrypt`

业务数据与配置均保存在 MySQL 中，**不再使用** `data/users/*.json` 与 `data/config/*.json` 作为运行时数据源。仓库中的 `data/` 仅可作迁移脚本输入或本地备份参考。

## 目录结构（要点）

| 路径 | 说明 |
|------|------|
| `main.go` | 入口：连接数据库、初始化 `Store`、注册路由 |
| `internal/database/` | MySQL 连接与环境选择（`DB_DSN` / `APP_ENV`） |
| `internal/handlers/` | HTTP 处理：认证、资料、签到、公开页、管理端等 |
| `internal/models/` | 领域模型（用户、待激活、重置密码、辅助邮箱等） |
| `internal/storage/` | 持久化：`Store` + GORM 模型与业务方法 |
| `internal/auth/` | JWT 签发与校验 |
| `internal/email/` | 发信 |
| `cmd/migrate/` | 一次性工具：将旧版 JSON `data/` 导入 MySQL |

## 环境与数据库

连接字符串优先级：**`DB_DSN` > 内置规则**。

1. **若设置 `DB_DSN`**：直接使用该完整 DSN（适合 CI、容器或临时切换）。
2. **否则**：根据 `APP_ENV` 选择内置库：
   - `APP_ENV=production` 或 `prod` → 生产库：`192.168.1.100:3306` / 库名 `sproutgate` / 用户 `sproutgate`
   - 未设置或其它值 → 开发/测试库：`10.1.1.100:3306` / 库名 `sproutgate-test` / 用户 `sproutgate-test`

其它常用环境变量：

| 变量 | 说明 | 默认 |
|------|------|------|
| `PORT` | HTTP 监听端口 | `8080` |
| `APP_ENV` | 影响默认 MySQL 选择与 `/api/health` 返回的 `env` | 空则 health 中为 `development` |
| `GIN_MODE` | `release` 时 GORM 日志级别更安静（警告级） | debug 模式 |
| `DB_DSN` | 完整 MySQL DSN，覆盖上述内置地址 | 未设置 |
| `GEO_LOOKUP_URL` | `GET /api/auth/me` 未带 `X-Visit-Location` 时，服务端按 IP 请求该基址反查展示地理位置（实现见 `internal/clientgeo`，会拼接 `?ip=`） | 默认 `https://cf-ip-geo.smyhub.com/api` |

**建议**：生产部署时设置 `APP_ENV=production`，并确保 MySQL 防火墙与账号权限正确；敏感连接信息也可只通过 `DB_DSN` 注入，无需改代码。

## 数据库表（AutoMigrate）

应用启动与 `migrate` 工具均会同步下列表结构（GORM `AutoMigrate`）：

| 表名 | 用途 |
|------|------|
| `users` | 用户主数据；签到/访问时间列表、`auth_clients` 等以 JSON 文本列存储 |
| `pending_users` | 注册邮箱验证流程中的待激活用户 |
| `password_resets` | 忘记密码验证码 |
| `secondary_email_verifications` | 绑定辅助邮箱验证码 |
| `app_configs` | 键值配置：`admin`、`auth`、`email`、`checkin`、`registration`（JSON） |
| `invite_codes` | 注册邀请码 |
| `profile_likes` | 公开用户主页点赞明细（点赞者、被赞者、自然日等） |
| `profile_like_daily_quota` | 点赞者当日已用额度（与代码常量 `MaxProfileLikesPerDay` 配合） |

管理员令牌、JWT Secret、邮件 SMTP、签到奖励、是否强制邀请码等，均从 `app_configs` / `invite_codes` 读写，首次无记录时由 `Store` 按逻辑补全默认项。

## 从旧 JSON 迁移到 MySQL

在 **`sproutgate-backend`** 目录下：

```bash
# 导入到当前环境对应的库（默认开发库，除非设置 APP_ENV 或 DB_DSN）
go run ./cmd/migrate --data-dir ./data
```

Windows PowerShell 示例（写入生产库）：

```powershell
$env:APP_ENV = "production"
go run ./cmd/migrate --data-dir ./data
Remove-Item Env:APP_ENV   # 可选：清除变量，避免影响本机其它命令
```

迁移内容：`data/config/*.json`（写入 `app_configs` 与 `invite_codes`）、`data/users/*.json`（写入 `users`）。对已存在主键执行 **UPSERT**：同账号、同配置键会更新为迁移文件中的值。

## HTTP 路由速览

- **`GET /`**、**`GET /api`**：API 简要说明 JSON（`main.go` 内嵌字段，含 `version`、`routePrefixes` 等）。**当前未注册**单独返回 Markdown 的 `/api/docs` 路由；对外长文档见仓库根目录 **`萌芽账户认证中心-第三方应用API接入文档.md`**。
- **`GET /api/health`**：`{ "status": "ok", "env": "<APP_ENV 或 development>" }`。
- **`/api/auth/*`**：登录、注册、邮箱验证、忘记/重置密码、辅助邮箱、令牌校验、`/me`、签到、更新资料等；可选请求头 `X-Auth-Client`、`X-Auth-Client-Name`（记录第三方应用接入）。
- **`/api/public/*`**：
  - **`GET /api/public/users`**：公开用户目录（未封禁；默认按 `createdAt` 升序）。
  - **`GET /api/public/users/:account`**：公开资料 + 累计赞数；若带合法 Bearer，可附加「是否已赞今日」「当日剩余可点赞人数」等浏览者上下文。
  - **`POST /api/public/users/:account/like`**：登录用户给指定主页点赞（不能赞自己；每人每主页每日一次；每自然日每名用户最多给 **5** 位不同用户点赞，常量见 `internal/storage/profile_likes.go`）。
  - **`GET /api/public/registration-policy`**：是否强制邀请码等。
- **`/api/admin/*`**：需 **`X-Admin-Token`**（或 Query `token`）：用户 CRUD、签到配置、注册策略与邀请码管理。

允许的 CORS 自定义头包含：`Authorization`、`X-Admin-Token`、`X-Visit-Ip`、`X-Visit-Location`、`X-Auth-Client`、`X-Auth-Client-Name` 等。

## 本地开发命令

```bash
cd sproutgate-backend
go mod tidy
go run .
```

默认监听 `:8080`。前端开发时请将 `VITE_API_BASE` 指向本服务（详见仓库根目录下 `sproutgate-frontend/前端文档.md`）。

## 安全提示

- 勿将生产 `DB_DSN`、SMTP 密码、真实 `admin` 令牌提交到版本库。
- 旧版 `data/config/email.json` 等若含真实口令，迁移后应以环境或运维密钥管理为准，并限制数据库与 SMTP 账号权限。
