Files
carrotskin/API文档.md
Wu XiangYu 268344c357
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 15m39s
fix: 修复前端多项问题并完善认证流程
- 修复 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
2026-07-08 17:36:09 +08:00

332 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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` 350 字符;`password` 6128`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" // 6128
}
```
- 响应 `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. 上传材质(需 JWTmultipart
- `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" }`116 字符)
- 响应 `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` 布尔字段。前端"移除角色皮肤"依赖此能力。