Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 15m39s
- 修复 AuthContext 硬编码 localhost:8080,改用 api.ts 统一的 API_BASE_URL(走环境变量/代理,生产可部署) - 修复注册页发送验证码绕过 api.ts 直接 raw fetch 的问题,改用统一的 sendVerificationCode - 修复 SliderCaptcha 验证成功后 captchaId 未透传导致注册人机验证失效的断链 - 修复头像上传契约:预签名URL方式(/user/avatar/upload-url)改为直传(/user/avatar/upload) - 移除后端不支持的'设置活跃角色'功能(/profile/:uuid/activate)及相关死代码 - 修复分页字段不一致:新增 normalizePaginatedData 兼容后端 per_page 响应 - 强化邮箱正则校验(RFC 标准,禁止连续点/首尾点,TLD>=2字母) - 移除首页指向无效 /api 页面的'查看API文档'按钮 - 新增 /register /login /signup 重定向路由,统一指向 /auth?mode= - 同步 API文档.md
332 lines
9.2 KiB
Markdown
332 lines
9.2 KiB
Markdown
# CarrotSkin API 文档(基于后端代码,2026-07 修订)
|
||
|
||
> 本文档由后端 `internal/handler/*.go` + `internal/types/common.go` 直接整理而来,
|
||
> 是前后端契约的唯一事实来源。所有路径以 `/api/v1` 为前缀(Yggdrasil / CSL 除外)。
|
||
|
||
## 通用约定
|
||
|
||
### 统一响应格式
|
||
|
||
成功:
|
||
```json
|
||
{ "code": 200, "message": "操作成功", "data": <任意> }
|
||
```
|
||
|
||
错误:
|
||
```json
|
||
{ "code": <非200>, "message": "<错误描述>", "error": "<仅非生产环境返回>" }
|
||
```
|
||
|
||
常用状态码:`200 成功` / `201 创建成功` / `400 参数错误` / `401 未授权` / `403 禁止` / `404 不存在` / `500 服务器错误`。
|
||
|
||
### 认证
|
||
|
||
- 除"无需认证"标注的接口外,所有 `/user/*`、`/profile` 写操作、`/texture` 写操作均需在请求头携带:
|
||
`Authorization: Bearer <jwt_token>`
|
||
- JWT 通过登录或注册接口获取。
|
||
|
||
### 分页
|
||
|
||
- 请求参数:`page`(页码,默认 1)、`page_size`(每页数量,默认 20)。
|
||
- 响应数据形态(注意:后端返回的字段名是 **`per_page`**,且 **不含 `total_pages`**):
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "操作成功",
|
||
"data": {
|
||
"list": [ ... ],
|
||
"total": 100,
|
||
"page": 1,
|
||
"per_page": 20
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 一、认证 `/auth`(无需 JWT)
|
||
|
||
### 1. 用户注册
|
||
|
||
- `POST /api/v1/auth/register`
|
||
- Body:
|
||
```json
|
||
{
|
||
"username": "newuser",
|
||
"email": "user@example.com",
|
||
"password": "password123",
|
||
"verification_code": "123456",
|
||
"captcha_id": "uuid-xxxx",
|
||
"avatar": "https://example.com/a.png"
|
||
}
|
||
```
|
||
- `username` 3–50 字符;`password` 6–128;`verification_code` 必填 6 位;
|
||
- `captcha_id` 必填(来自 `POST /captcha/verify` 后透传的 `captchaId`);
|
||
- `avatar` 可选,必须是合法 URL。
|
||
- 响应 `data`:
|
||
```json
|
||
{
|
||
"token": "<jwt>",
|
||
"user_info": {
|
||
"id": 1, "username": "newuser", "email": "user@example.com",
|
||
"avatar": "", "points": 0, "role": "user", "status": 1,
|
||
"last_login_at": null,
|
||
"created_at": "2026-07-08T10:00:00Z",
|
||
"updated_at": "2026-07-08T10:00:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2. 用户登录
|
||
|
||
- `POST /api/v1/auth/login`
|
||
- Body:
|
||
```json
|
||
{ "username": "newuser 或 user@example.com", "password": "password123" }
|
||
```
|
||
- 响应 `data`:同注册(`{ token, user_info }`)。
|
||
|
||
### 3. 发送邮箱验证码
|
||
|
||
- `POST /api/v1/auth/send-code`
|
||
- Body:
|
||
```json
|
||
{ "email": "user@example.com", "type": "register | reset_password | change_email" }
|
||
```
|
||
- 响应 `data`:`{ "message": "验证码已发送,请查收邮件" }`。
|
||
|
||
### 4. 重置密码
|
||
|
||
- `POST /api/v1/auth/reset-password`
|
||
- Body:
|
||
```json
|
||
{
|
||
"email": "user@example.com",
|
||
"verification_code": "123456",
|
||
"new_password": "newpassword123"
|
||
}
|
||
```
|
||
- 响应 `data`:`{ "message": "密码重置成功" }`。
|
||
|
||
---
|
||
|
||
## 二、用户 `/user`(需 JWT)
|
||
|
||
### 5. 获取当前用户信息
|
||
|
||
- `GET /api/v1/user/profile`
|
||
- 响应 `data`:`UserInfo`(同注册返回的 `user_info`)。
|
||
|
||
### 6. 更新用户信息(密码 / 头像 URL)
|
||
|
||
- `PUT /api/v1/user/profile`
|
||
- Body:
|
||
```json
|
||
{
|
||
"avatar": "https://example.com/a.png", // 可选,更新头像 URL
|
||
"old_password": "oldpassword123", // 修改密码时必填
|
||
"new_password": "newpassword123" // 6–128
|
||
}
|
||
```
|
||
- 响应 `data`:更新后的 `UserInfo`。
|
||
|
||
### 7. 上传头像文件
|
||
|
||
- `POST /api/v1/user/avatar/upload`(multipart/form-data)
|
||
- 字段:`file`(图片文件,≤10MB)
|
||
- 响应 `data`:
|
||
```json
|
||
{ "avatar_url": "https://.../avatar.png", "user": { ...UserInfo } }
|
||
```
|
||
|
||
### 8. 更新头像 URL(外部 URL)
|
||
|
||
- `PUT /api/v1/user/avatar?avatar_url=<url>`
|
||
- 响应 `data`:`UserInfo`。
|
||
|
||
### 9. 更换邮箱
|
||
|
||
- `POST /api/v1/user/change-email`
|
||
- Body:
|
||
```json
|
||
{ "new_email": "new@example.com", "verification_code": "123456" }
|
||
```
|
||
- 响应 `data`:更换后的 `UserInfo`(新邮箱已生效)。
|
||
|
||
### 10. 重置 Yggdrasil 密码
|
||
|
||
- `POST /api/v1/user/yggdrasil-password/reset`
|
||
- 响应 `data`:`{ "password": "<新的Yggdrasil密码>" }`。
|
||
|
||
### 11. 获取用户公开信息
|
||
|
||
- `GET /api/v1/users/public?username=<name>` 或 `?id=<id>`(无需 JWT)
|
||
- 响应 `data`:
|
||
```json
|
||
{
|
||
"id": 1, "username": "testuser", "avatar": "",
|
||
"points": 100, "role": "user", "status": 1,
|
||
"created_at": "2026-07-01T10:00:00Z"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 三、材质 `/texture`
|
||
|
||
### 12. 搜索材质(公开,无需 JWT)
|
||
|
||
- `GET /api/v1/texture`
|
||
- Query:`keyword` / `type=SKIN|CAPE` / `public_only=true` / `page` / `page_size`
|
||
- 响应 `data`:分页结构 `{ list: TextureInfo[], total, page, per_page }`。
|
||
|
||
### 13. 获取材质详情(公开)
|
||
|
||
- `GET /api/v1/texture/:id`
|
||
- 响应 `data`:`TextureInfo`。
|
||
|
||
### 14. 上传材质(需 JWT,multipart)
|
||
|
||
- `POST /api/v1/texture/upload`
|
||
- formData:`file`(PNG, ≤32MB)、`name`(必填)、`description`、`type=SKIN|CAPE`、`is_public`、`is_slim`
|
||
- 响应 `data`:`TextureInfo`。
|
||
|
||
### 15. 更新材质(需 JWT)
|
||
|
||
- `PUT /api/v1/texture/:id`
|
||
- Body:
|
||
```json
|
||
{ "name": "新名称", "description": "新描述", "is_public": true }
|
||
```
|
||
- `is_public` 后端为 `*bool`,传 `true/false` 会更新;不传保持不变。
|
||
- 响应 `data`:`TextureInfo`。
|
||
|
||
### 16. 删除材质(需 JWT)
|
||
|
||
- `DELETE /api/v1/texture/:id`
|
||
- 响应 `data`:`null`。
|
||
|
||
### 17. 切换收藏(需 JWT)
|
||
|
||
- `POST /api/v1/texture/:id/favorite`
|
||
- 响应 `data`:`{ "is_favorited": true }`。
|
||
|
||
### 18. 我的上传(需 JWT)
|
||
|
||
- `GET /api/v1/texture/my?page=1&page_size=20`
|
||
- 响应 `data`:分页结构。
|
||
|
||
### 19. 我的收藏(需 JWT)
|
||
|
||
- `GET /api/v1/texture/favorites?page=1&page_size=20`
|
||
- 响应 `data`:分页结构。
|
||
|
||
#### TextureInfo 字段
|
||
`id, uploader_id, uploader_username, name, description(omitempty), type(SKIN|CAPE), url, hash, size, is_public, download_count, favorite_count, is_slim, status, created_at, updated_at`
|
||
|
||
---
|
||
|
||
## 四、档案 `/profile`
|
||
|
||
> ⚠️ **后端当前 `PUT /profile/:uuid` 不支持解除皮肤/披风绑定**:
|
||
> `UpdateProfileRequest.SkinID / CapeID` 为 `*int64`,仅当非 nil 时才更新;
|
||
> 传 `null` 与字段缺失都会被 Go 反序列化为 `nil`,二者无法区分,均被忽略。
|
||
> 因此前端要"移除角色皮肤",需要后端先支持(详见下方《待补齐》)。
|
||
|
||
### 20. 获取档案列表(需 JWT)
|
||
|
||
- `GET /api/v1/profile` 或 `GET /api/v1/profile/`
|
||
- 响应 `data`:`ProfileInfo[]`。
|
||
|
||
### 21. 创建档案(需 JWT)
|
||
|
||
- `POST /api/v1/profile` 或 `POST /api/v1/profile/`
|
||
- Body:`{ "name": "PlayerName" }`(1–16 字符)
|
||
- 响应 `data`:`ProfileInfo`。
|
||
|
||
### 22. 获取档案详情(公开)
|
||
|
||
- `GET /api/v1/profile/:uuid`
|
||
- 响应 `data`:`ProfileInfo`。
|
||
|
||
### 23. 更新档案(需 JWT)
|
||
|
||
- `PUT /api/v1/profile/:uuid`
|
||
- Body:
|
||
```json
|
||
{ "name": "NewName", "skin_id": 1, "cape_id": 2 }
|
||
```
|
||
- 当前实现:`skin_id/cape_id` 传数字会设置;传 null / 缺失均被忽略(无法清空)。
|
||
- 响应 `data`:`ProfileInfo`。
|
||
|
||
### 24. 删除档案(需 JWT)
|
||
|
||
- `DELETE /api/v1/profile/:uuid`
|
||
- 响应 `data`:`{ "message": "删除成功" }`。
|
||
|
||
#### ProfileInfo 字段
|
||
`uuid, user_id, name, skin_id(omitempty,可空), cape_id(omitempty,可空), last_used_at(omitempty), created_at, updated_at`
|
||
|
||
---
|
||
|
||
## 五、滑动验证码 `/captcha`(无需 JWT)
|
||
|
||
### 25. 生成滑动验证码
|
||
|
||
- `GET /api/v1/captcha/generate`
|
||
- 响应(注意:此接口的响应是直接对象,未包裹 `data`):
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {
|
||
"masterImage": "<base64 主图>",
|
||
"tileImage": "<base64 滑块图>",
|
||
"captchaId": "<进程ID,注册时需要>",
|
||
"y": <缺口纵坐标>
|
||
}
|
||
}
|
||
```
|
||
|
||
### 26. 校验滑动验证码
|
||
|
||
- `POST /api/v1/captcha/verify`
|
||
- Body:`{ "captchaId": "<id>", "dx": <横坐标> }`
|
||
- 响应:`{ "code": 200, "msg": "验证成功" }` 或 `{ "code": 400, "msg": "验证失败,请重试" }`
|
||
|
||
---
|
||
|
||
## 六、Yggdrasil API `/api/yggdrasil`(独立前缀,供 MC 客户端使用)
|
||
|
||
- `GET /api/yggdrasil` —— 元数据
|
||
- `POST /api/yggdrasil/minecraftservices/player/certificates`
|
||
- `POST /api/yggdrasil/authserver/authenticate`
|
||
- `POST /api/yggdrasil/authserver/validate`
|
||
- `POST /api/yggdrasil/authserver/refresh`
|
||
- `POST /api/yggdrasil/authserver/invalidate`
|
||
- `POST /api/yggdrasil/authserver/signout`
|
||
- `GET /api/yggdrasil/sessionserver/session/minecraft/profile/:uuid`
|
||
- `POST /api/yggdrasil/sessionserver/session/minecraft/join`
|
||
- `GET /api/yggdrasil/sessionserver/session/minecraft/hasJoined`
|
||
- `POST /api/yggdrasil/api/profiles/minecraft`
|
||
|
||
## 七、CustomSkinAPI `/api/v1/csl`(公开)
|
||
|
||
- `GET /api/v1/csl/:username` —— 获取玩家信息 JSON
|
||
- `GET /api/v1/csl/textures/:hash` —— 获取资源文件
|
||
|
||
## 八、管理员 `/api/v1/admin`(需 JWT + 管理员)
|
||
|
||
- `GET /admin/users`、`GET /admin/users/:id`
|
||
- `PUT /admin/users/role`、`PUT /admin/users/status`
|
||
- `GET /admin/textures`、`DELETE /admin/textures/:id`
|
||
- `GET /admin/permissions`
|
||
|
||
---
|
||
|
||
## 待补齐(后端缺陷,影响前端可用性)
|
||
|
||
1. **`PUT /profile/:uuid` 不支持清空 skin/cape**
|
||
现状:`SkinID/CapeID *int64` 无法区分"未传"与"显式 null"。
|
||
建议:改用 `json.RawMessage` 区分三种语义(缺失=不修改 / null=清空 / 数字=设置),
|
||
或新增 `remove_skin` / `remove_cape` 布尔字段。前端"移除角色皮肤"依赖此能力。
|