Add HTTP /v1 MVC, TCP send path, groups with server-side member-avatar collage, object-storage upload, and friends/favorites around the architecture-v3 layout. Co-authored-by: Cursor <cursoragent@cursor.com>
1155 lines
34 KiB
YAML
1155 lines
34 KiB
YAML
openapi: 3.0.3
|
||
info:
|
||
title: Spark IM API
|
||
version: 0.1.0
|
||
description: |
|
||
本页是 **HTTP REST**(Gin `127.0.0.1:8080`)。长连接 IM 是另一套 TCP,见文末。
|
||
|
||
## 响应
|
||
|
||
```json
|
||
{"code":"0","msg":"ok","data":{}}
|
||
```
|
||
|
||
`code` 是字符串。`"0"` 成功,其它失败。失败时 `data` 为 `{}`。看 `code`,不要只看 HTTP 状态(多数接口 HTTP 200;`/readyz` 未就绪才是 503)。
|
||
|
||
## 鉴权
|
||
|
||
1. `POST /v1/auth/login` 拿 `access_token` / `refresh_token`。测试号 `test01@spark.com`~`test10@spark.com`,`verify_code` 任意。
|
||
2. 其它接口:`Authorization: Bearer <access_token>`。
|
||
3. 可选 `X-Device-ID` 必须等于登录时的 `device_id`,否则 `40101`。
|
||
4. 续期 `POST /v1/auth/token/refresh`(用 refresh,不必带 Bearer)。退出 `POST /v1/auth/logout`。
|
||
|
||
语言:`X-Language` 优先,否则 `Accept-Language`。取值 `zh-CN`(默认)/ `en` / `zh-TW`。
|
||
|
||
## TCP(不是 Protobuf)
|
||
|
||
二进制 Frame 头 23 字节(Magic `SP` + Command + SequenceID + BodyLength),**Body 是上面同一套 JSON**。没有 `.proto`,不要按 Protobuf 解包。
|
||
公网 TLS 在 Nginx `:443` 终结;gnet 只收本机 PROXY 明文。心跳 / AUTH / GENERATE_MSG_ID 已通;发消息与同步还在接。
|
||
servers:
|
||
- url: http://127.0.0.1:8080
|
||
description: loopback HTTP (not public)
|
||
security:
|
||
- bearerAuth: []
|
||
tags:
|
||
- name: system
|
||
- name: auth
|
||
- name: users
|
||
- name: friends
|
||
- name: groups
|
||
- name: conversations
|
||
- name: messages
|
||
- name: files
|
||
- name: favorites
|
||
paths:
|
||
/healthz:
|
||
get:
|
||
tags: [system]
|
||
security: []
|
||
summary: 进程探活
|
||
operationId: healthz
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
example:
|
||
code: "0"
|
||
msg: ok
|
||
data:
|
||
status: ok
|
||
/readyz:
|
||
get:
|
||
tags: [system]
|
||
security: []
|
||
summary: 就绪探活(MySQL / Redis / 是否接流量)
|
||
description: |
|
||
进程存活用 `/healthz`(始终 200)。
|
||
`/readyz` 在未接流量、依赖不可用或关停中返回 HTTP 503,`code=50301`。
|
||
Nginx / 编排应摘流看本接口,不要看 `/healthz`。
|
||
operationId: readyz
|
||
responses:
|
||
"200":
|
||
description: 已就绪
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
example:
|
||
code: "0"
|
||
msg: ok
|
||
data:
|
||
status: ok
|
||
accepting: true
|
||
mysql: ok
|
||
redis: ok
|
||
taskpool: ok
|
||
"503":
|
||
description: 未就绪
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
example:
|
||
code: "50301"
|
||
msg: 服务不可用
|
||
data:
|
||
status: not_ready
|
||
accepting: false
|
||
mysql: skipped
|
||
redis: skipped
|
||
taskpool: ok
|
||
/docs:
|
||
get:
|
||
tags: [system]
|
||
security: []
|
||
summary: OpenAPI 文档页(Swagger UI)
|
||
operationId: docsUI
|
||
responses:
|
||
"200":
|
||
description: HTML
|
||
content:
|
||
text/html:
|
||
schema:
|
||
type: string
|
||
/openapi.yaml:
|
||
get:
|
||
tags: [system]
|
||
security: []
|
||
summary: OpenAPI 规范原文
|
||
operationId: openAPISpec
|
||
responses:
|
||
"200":
|
||
description: YAML
|
||
content:
|
||
application/yaml:
|
||
schema:
|
||
type: string
|
||
/v1/auth/email-codes:
|
||
post:
|
||
tags: [auth]
|
||
security: []
|
||
summary: 发送邮箱登录验证码
|
||
description: |
|
||
验证码 5 分钟有效,同一邮箱 60 秒内只能发一次。
|
||
测试账号 `test01@spark.com`~`test10@spark.com` 无需发码,登录时任意填写 `verify_code`。
|
||
当前不发送真实邮件,验证码写入 Redis(无 Redis 时为进程内存)。
|
||
operationId: sendEmailCode
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/SendEmailCodeRequest"
|
||
example:
|
||
email: ada@example.com
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
examples:
|
||
ok:
|
||
value:
|
||
code: "0"
|
||
msg: ok
|
||
data:
|
||
expires_in: 300
|
||
/v1/auth/login:
|
||
post:
|
||
tags: [auth]
|
||
security: []
|
||
summary: 邮箱验证码登录并签发 JWT
|
||
description: |
|
||
首次验证成功会创建用户(Snowflake uid、默认 spark_id、shortid 个人二维码票据,不含 uid)。
|
||
登录写入/更新 `user_devices`(device_type 存在 `platform` 列、device_name、app_version、language、last_ip),并追加 `user_login_logs`。
|
||
返回 access_token 与 refresh_token。测试账号任意验证码即可。
|
||
`device_type` 必填:ios / android / harmony / windows / macos / web(也接受旧字段 `platform`)。
|
||
operationId: login
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/LoginRequest"
|
||
example:
|
||
email: test01@spark.com
|
||
verify_code: "000000"
|
||
device_id: 7
|
||
device_type: ios
|
||
device_name: iPhone
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope。成功时 data 为 LoginData;失败时 data 为 {}
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/LoginEnvelope"
|
||
examples:
|
||
ok:
|
||
value:
|
||
code: "0"
|
||
msg: ok
|
||
data:
|
||
access_token: eyJhbGciOiJIUzI1NiJ9...
|
||
refresh_token: eyJhbGciOiJIUzI1NiJ9...
|
||
expires_in: 604800
|
||
refresh_expires_in: 2592000
|
||
user_id: 1940000000000000000
|
||
device_id: 7
|
||
spark_id: s1a2b3c
|
||
unauthorized:
|
||
value:
|
||
code: "40101"
|
||
msg: 未授权
|
||
data: {}
|
||
invalid:
|
||
value:
|
||
code: "40001"
|
||
msg: 请填写验证码
|
||
data: {}
|
||
/v1/auth/token/refresh:
|
||
post:
|
||
tags: [auth]
|
||
security: []
|
||
summary: 用 refresh_token 续期
|
||
description: |
|
||
校验 refresh_token 后签发新的 access/refresh,旧一对立即失效(jti denylist)。
|
||
不需要 access Bearer。按 RealIP 限流。
|
||
operationId: refreshToken
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/RefreshRequest"
|
||
example:
|
||
refresh_token: eyJhbGciOiJIUzI1NiJ9...
|
||
responses:
|
||
"200":
|
||
description: 与登录相同的 LoginData
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/LoginEnvelope"
|
||
/v1/auth/logout:
|
||
post:
|
||
tags: [auth]
|
||
summary: 退出当前设备
|
||
description: 吊销该设备的 access/refresh,并踢掉同 device_id 的 TCP 连接。
|
||
operationId: logout
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
/v1/users/me:
|
||
get:
|
||
tags: [users]
|
||
summary: 当前用户资料
|
||
operationId: getMe
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
example:
|
||
code: "0"
|
||
msg: ok
|
||
data:
|
||
uid: 1940000000000000000
|
||
spark_id: alice_ok
|
||
nickname: test01
|
||
avatar: perm/avatar/1940000000000000000/20260917/1940000000000000001.png
|
||
qrcode: "-NDveu-9Q"
|
||
birthday: ""
|
||
gender: 0
|
||
status: 1
|
||
last_login_time: 1726550000000
|
||
last_active_time: 1726550000000
|
||
create_time: 1726550000000
|
||
update_time: 1726550000000
|
||
patch:
|
||
tags: [users]
|
||
summary: 修改资料
|
||
description: 可改 spark_id、nickname、avatar、gender(0/1/2)、birthday(YYYY-MM-DD)。未出现的字段不改。
|
||
operationId: patchMe
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PatchMeRequest"
|
||
example:
|
||
nickname: Ada
|
||
gender: 2
|
||
birthday: "1994-05-01"
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
/v1/friends/lookup:
|
||
get:
|
||
tags: [friends]
|
||
summary: 按邮箱 / Spark账号 / 二维码查找用户
|
||
description: |
|
||
查询参数 `email`、`spark_id`、`qrcode`、`uid` 四选一。
|
||
二维码为 shortid 个人扫描票据,不含 uid。对方已拉黑当前用户时返回 40401。
|
||
operationId: lookupFriend
|
||
parameters:
|
||
- in: query
|
||
name: email
|
||
schema: { type: string, format: email }
|
||
- in: query
|
||
name: spark_id
|
||
schema: { type: string }
|
||
- in: query
|
||
name: qrcode
|
||
schema: { type: string, example: "-NDveu-9Q" }
|
||
- in: query
|
||
name: uid
|
||
schema: { type: integer, format: int64 }
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope,data 为好友卡片(含 relation:0无 1好友 2已拉黑)
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
/v1/friends/requests:
|
||
get:
|
||
tags: [friends]
|
||
summary: 收到的待处理好友申请
|
||
operationId: listFriendRequests
|
||
parameters:
|
||
- in: query
|
||
name: limit
|
||
schema: { type: integer, default: 20, maximum: 100 }
|
||
responses:
|
||
"200":
|
||
description: data.items
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
post:
|
||
tags: [friends]
|
||
summary: 发起好友申请
|
||
description: |
|
||
通过扫描二维码、搜索邮箱或 spark_id 发起。可填申请理由与备注。
|
||
同一对用户只保留一条申请,重复发起覆盖理由/备注,不新增行。
|
||
若对方已有待处理申请指向自己,则自动成为双向好友并写入一条系统消息。
|
||
不支持单向好友。
|
||
operationId: applyFriend
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/FriendApplyRequest"
|
||
example:
|
||
email: test02@spark.com
|
||
reason: 我是同事
|
||
remark: 同事
|
||
responses:
|
||
"200":
|
||
description: data.status 为 pending 或 accepted
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
/v1/friends:
|
||
get:
|
||
tags: [friends]
|
||
summary: 好友列表
|
||
operationId: listFriends
|
||
parameters:
|
||
- in: query
|
||
name: limit
|
||
schema: { type: integer, default: 20, maximum: 100 }
|
||
responses:
|
||
"200":
|
||
description: data.items
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
post:
|
||
tags: [friends]
|
||
summary: 同意好友申请
|
||
description: 成为双向好友后自动在单聊中插入一条 `kind=friend.became` 系统消息并入队 deliver。
|
||
operationId: acceptFriend
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/FriendAcceptRequest"
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
/v1/friends/{uid}:
|
||
get:
|
||
tags: [friends]
|
||
summary: 查看好友资料
|
||
operationId: getFriend
|
||
parameters:
|
||
- $ref: "#/components/parameters/FriendUID"
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
delete:
|
||
tags: [friends]
|
||
summary: 删除好友
|
||
description: 解除双向关系,双方都不再是好友。
|
||
operationId: deleteFriend
|
||
parameters:
|
||
- $ref: "#/components/parameters/FriendUID"
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
/v1/friends/{uid}/remark:
|
||
put:
|
||
tags: [friends]
|
||
summary: 设置好友备注
|
||
operationId: setFriendRemark
|
||
parameters:
|
||
- $ref: "#/components/parameters/FriendUID"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/FriendRemarkRequest"
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
/v1/friends/{uid}/block:
|
||
put:
|
||
tags: [friends]
|
||
summary: 拉黑
|
||
description: 单向拉黑并解除好友关系。被拉黑一方再申请时视为用户不存在。
|
||
operationId: blockFriend
|
||
parameters:
|
||
- $ref: "#/components/parameters/FriendUID"
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
delete:
|
||
tags: [friends]
|
||
summary: 取消拉黑
|
||
operationId: unblockFriend
|
||
parameters:
|
||
- $ref: "#/components/parameters/FriendUID"
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
/v1/friends/{uid}/reports:
|
||
post:
|
||
tags: [friends]
|
||
summary: 投诉好友
|
||
operationId: reportFriend
|
||
parameters:
|
||
- $ref: "#/components/parameters/FriendUID"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/FriendReportRequest"
|
||
example:
|
||
reason: 1
|
||
detail: spam
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
/v1/groups:
|
||
get:
|
||
tags: [groups]
|
||
summary: 我加入的群
|
||
operationId: listGroups
|
||
parameters:
|
||
- in: query
|
||
name: limit
|
||
schema: { type: integer, default: 20, maximum: 100 }
|
||
responses:
|
||
"200":
|
||
description: data.items,id 为自增群号,conversation_id 为 Snowflake 会话 id
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
post:
|
||
tags: [groups]
|
||
summary: 创建群
|
||
description: |
|
||
`id` 为表内自增群号。发消息、同步使用返回的 `conversation_id`(Snowflake)。
|
||
只能拉好友入群。群主无法直接退群,需先转让。
|
||
建群成功后服务端按实际人数拼接成员已有头像(注册默认或自定义),最多 9 人;两人就是并排两格,不会凑九宫格。
|
||
成员头像拉不到时用字母占位图,整张拼图失败则改用群名默认头像,`avatar` 不会为空。
|
||
写入 `perm/group/{群号}/`。之后可用 `purpose=group_avatar` 直传自定义头像再 PATCH。
|
||
operationId: createGroup
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/CreateGroupRequest"
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
/v1/groups/{id}:
|
||
get:
|
||
tags: [groups]
|
||
summary: 群资料
|
||
operationId: getGroup
|
||
parameters:
|
||
- $ref: "#/components/parameters/GroupID"
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
patch:
|
||
tags: [groups]
|
||
summary: 修改群名称 / 头像 / 全员禁言
|
||
operationId: updateGroup
|
||
parameters:
|
||
- $ref: "#/components/parameters/GroupID"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/UpdateGroupRequest"
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
delete:
|
||
tags: [groups]
|
||
summary: 解散群(仅群主)
|
||
operationId: dissolveGroup
|
||
parameters:
|
||
- $ref: "#/components/parameters/GroupID"
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
/v1/groups/{id}/members:
|
||
get:
|
||
tags: [groups]
|
||
summary: 群成员
|
||
operationId: listGroupMembers
|
||
parameters:
|
||
- $ref: "#/components/parameters/GroupID"
|
||
- in: query
|
||
name: limit
|
||
schema: { type: integer, default: 200, maximum: 200 }
|
||
responses:
|
||
"200":
|
||
description: data.items
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
post:
|
||
tags: [groups]
|
||
summary: 邀请好友入群
|
||
operationId: inviteGroupMembers
|
||
parameters:
|
||
- $ref: "#/components/parameters/GroupID"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/InviteGroupMembersRequest"
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
/v1/groups/{id}/members/{uid}:
|
||
patch:
|
||
tags: [groups]
|
||
summary: 改成员角色 / 禁言 / 群昵称
|
||
operationId: updateGroupMember
|
||
parameters:
|
||
- $ref: "#/components/parameters/GroupID"
|
||
- $ref: "#/components/parameters/FriendUID"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/UpdateGroupMemberRequest"
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
delete:
|
||
tags: [groups]
|
||
summary: 踢人,或 uid 为自己时退群
|
||
operationId: removeGroupMember
|
||
parameters:
|
||
- $ref: "#/components/parameters/GroupID"
|
||
- $ref: "#/components/parameters/FriendUID"
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
/v1/groups/{id}/transfer:
|
||
post:
|
||
tags: [groups]
|
||
summary: 转让群主
|
||
operationId: transferGroup
|
||
parameters:
|
||
- $ref: "#/components/parameters/GroupID"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/TransferGroupRequest"
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
/v1/files:
|
||
post:
|
||
tags: [files]
|
||
summary: 签发对象存储直传凭证
|
||
description: |
|
||
客户端只传 `purpose` / `mime` / `size`,服务端生成 object_key 并签发短时 Presigned PUT。
|
||
文件直传 RustFS,不经过 Gin。
|
||
`avatar` 写入 `perm/avatar/{uid}/…`(不过期);`group_avatar` 写入 `perm/group/{群号}/…`(需 `group_id`,群主/管理员);
|
||
聊天媒体写入 `chat/{yyyyMMdd}/{uid}/…`(桶生命周期过期)。
|
||
客户端 PUT 时必须带返回的 `headers`(至少 `Content-Type`)。消息与头像只存 `object_key`。
|
||
operationId: createUploadCredential
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/CreateUploadRequest"
|
||
example:
|
||
purpose: chat_image
|
||
mime: image/jpeg
|
||
size: 245760
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
example:
|
||
code: "0"
|
||
msg: ok
|
||
data:
|
||
object_key: chat/20260917/1/2.jpg
|
||
upload_url: http://47.236.66.9:9000/spark/chat/20260917/1/2.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256
|
||
method: PUT
|
||
headers:
|
||
Content-Type: image/jpeg
|
||
expires_in: 900
|
||
/v1/files/downloads:
|
||
post:
|
||
tags: [files]
|
||
summary: 签发对象存储下载凭证
|
||
description: 已登录用户用 object_key 换取短时 Presigned GET。key 必须是 `perm/` 或 `chat/` 前缀。
|
||
operationId: createDownloadCredential
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/CreateDownloadRequest"
|
||
example:
|
||
object_key: chat/20260917/1/2.jpg
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
/v1/favorites:
|
||
get:
|
||
tags: [favorites]
|
||
summary: 收藏列表
|
||
operationId: listFavorites
|
||
parameters:
|
||
- in: query
|
||
name: limit
|
||
schema:
|
||
type: integer
|
||
default: 20
|
||
maximum: 100
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
post:
|
||
tags: [favorites]
|
||
summary: 收藏一条消息
|
||
description: |
|
||
会话成员可将消息收入收藏。图片/语音/视频/文件会 CopyObject 到 `perm/fav/{uid}/…`。
|
||
源对象已从 `chat/` 过期删除则失败(`code=40402`),不提供本地补传。
|
||
同一 `msg_id` 重复收藏返回已有记录。撤回不影响已收藏快照。
|
||
operationId: createFavorite
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/CreateFavoriteRequest"
|
||
example:
|
||
msg_id: 1
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
/v1/favorites/{id}:
|
||
delete:
|
||
tags: [favorites]
|
||
summary: 取消收藏
|
||
operationId: deleteFavorite
|
||
parameters:
|
||
- $ref: "#/components/parameters/FavoriteID"
|
||
responses:
|
||
"200":
|
||
description: 统一 envelope
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Envelope"
|
||
components:
|
||
securitySchemes:
|
||
bearerAuth:
|
||
type: http
|
||
scheme: bearer
|
||
bearerFormat: JWT
|
||
description: Access JWT from /v1/auth/login. Same token is used for TCP AUTH.
|
||
schemas:
|
||
Envelope:
|
||
type: object
|
||
required: [code, msg, data]
|
||
properties:
|
||
code:
|
||
type: string
|
||
description: 业务码,成功为 "0"
|
||
example: "0"
|
||
msg:
|
||
type: string
|
||
example: ok
|
||
data:
|
||
type: object
|
||
additionalProperties: true
|
||
SendEmailCodeRequest:
|
||
type: object
|
||
required: [email]
|
||
properties:
|
||
email:
|
||
type: string
|
||
format: email
|
||
LoginRequest:
|
||
type: object
|
||
required: [email, verify_code, device_id]
|
||
properties:
|
||
email:
|
||
type: string
|
||
format: email
|
||
verify_code:
|
||
type: string
|
||
description: 邮箱验证码。测试账号 test01@spark.com~test10@spark.com 可任意填写。
|
||
device_id:
|
||
type: integer
|
||
format: int64
|
||
minimum: 1
|
||
device_type:
|
||
type: string
|
||
enum: [ios, android, harmony, windows, macos, web]
|
||
platform:
|
||
type: string
|
||
description: device_type 的兼容字段
|
||
enum: [ios, android, harmony, windows, macos, web]
|
||
device_name:
|
||
type: string
|
||
maxLength: 128
|
||
LoginData:
|
||
type: object
|
||
required: [access_token, refresh_token, expires_in, refresh_expires_in, user_id, device_id, spark_id]
|
||
properties:
|
||
access_token:
|
||
type: string
|
||
refresh_token:
|
||
type: string
|
||
expires_in:
|
||
type: integer
|
||
format: int64
|
||
description: access TTL in seconds (default 604800 = 7 days)
|
||
example: 604800
|
||
refresh_expires_in:
|
||
type: integer
|
||
format: int64
|
||
description: refresh TTL in seconds (default 2592000 = 30 days)
|
||
example: 2592000
|
||
user_id:
|
||
type: integer
|
||
format: int64
|
||
description: Snowflake user id
|
||
device_id:
|
||
type: integer
|
||
format: int64
|
||
spark_id:
|
||
type: string
|
||
description: Public handle, unique and user-changeable
|
||
RefreshRequest:
|
||
type: object
|
||
required: [refresh_token]
|
||
properties:
|
||
refresh_token:
|
||
type: string
|
||
PatchMeRequest:
|
||
type: object
|
||
properties:
|
||
spark_id:
|
||
type: string
|
||
minLength: 6
|
||
maxLength: 32
|
||
pattern: "^[a-zA-Z][a-zA-Z0-9_]{5,31}$"
|
||
nickname:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 64
|
||
avatar:
|
||
type: string
|
||
maxLength: 512
|
||
gender:
|
||
type: integer
|
||
enum: [0, 1, 2]
|
||
birthday:
|
||
type: string
|
||
description: YYYY-MM-DD;空字符串表示清除
|
||
example: "1994-05-01"
|
||
FriendApplyRequest:
|
||
type: object
|
||
properties:
|
||
uid:
|
||
type: integer
|
||
format: int64
|
||
email:
|
||
type: string
|
||
format: email
|
||
spark_id:
|
||
type: string
|
||
qrcode:
|
||
type: string
|
||
description: 个人二维码扫描票据(shortid),不含 uid
|
||
reason:
|
||
type: string
|
||
maxLength: 128
|
||
remark:
|
||
type: string
|
||
maxLength: 64
|
||
FriendAcceptRequest:
|
||
type: object
|
||
required: [request_id]
|
||
properties:
|
||
request_id:
|
||
type: integer
|
||
format: int64
|
||
remark:
|
||
type: string
|
||
maxLength: 64
|
||
FriendRemarkRequest:
|
||
type: object
|
||
properties:
|
||
remark:
|
||
type: string
|
||
maxLength: 64
|
||
FriendReportRequest:
|
||
type: object
|
||
required: [reason]
|
||
properties:
|
||
reason:
|
||
type: integer
|
||
enum: [1, 2, 3, 4]
|
||
description: 1=垃圾 2=骚扰 3=不实身份 4=其他
|
||
detail:
|
||
type: string
|
||
maxLength: 512
|
||
CreateUploadRequest:
|
||
type: object
|
||
required: [purpose, mime, size]
|
||
properties:
|
||
purpose:
|
||
type: string
|
||
enum: [avatar, group_avatar, chat_image, chat_voice, chat_video, chat_gif, chat_file]
|
||
mime:
|
||
type: string
|
||
example: image/jpeg
|
||
size:
|
||
type: integer
|
||
format: int64
|
||
minimum: 1
|
||
group_id:
|
||
type: integer
|
||
format: int64
|
||
minimum: 1
|
||
description: purpose=group_avatar 时必填,为自增群号
|
||
CreateDownloadRequest:
|
||
type: object
|
||
required: [object_key]
|
||
properties:
|
||
object_key:
|
||
type: string
|
||
maxLength: 512
|
||
CreateFavoriteRequest:
|
||
type: object
|
||
required: [msg_id]
|
||
properties:
|
||
msg_id:
|
||
type: integer
|
||
format: int64
|
||
minimum: 1
|
||
CreateGroupRequest:
|
||
type: object
|
||
required: [name, member_uids]
|
||
properties:
|
||
name:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 32
|
||
member_uids:
|
||
type: array
|
||
minItems: 1
|
||
maxItems: 199
|
||
items:
|
||
type: integer
|
||
format: int64
|
||
UpdateGroupRequest:
|
||
type: object
|
||
properties:
|
||
name:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 32
|
||
avatar:
|
||
type: string
|
||
muted_all:
|
||
type: boolean
|
||
InviteGroupMembersRequest:
|
||
type: object
|
||
required: [uids]
|
||
properties:
|
||
uids:
|
||
type: array
|
||
minItems: 1
|
||
items:
|
||
type: integer
|
||
format: int64
|
||
UpdateGroupMemberRequest:
|
||
type: object
|
||
properties:
|
||
role:
|
||
type: integer
|
||
enum: [2, 3]
|
||
description: 2=管理员 3=普通成员;群主只能通过转让变更
|
||
speak_muted:
|
||
type: boolean
|
||
nickname:
|
||
type: string
|
||
maxLength: 32
|
||
TransferGroupRequest:
|
||
type: object
|
||
required: [uid]
|
||
properties:
|
||
uid:
|
||
type: integer
|
||
format: int64
|
||
LoginEnvelope:
|
||
type: object
|
||
required: [code, msg, data]
|
||
properties:
|
||
code:
|
||
type: string
|
||
msg:
|
||
type: string
|
||
data:
|
||
$ref: "#/components/schemas/LoginData"
|
||
AuthData:
|
||
type: object
|
||
properties:
|
||
user_id:
|
||
type: integer
|
||
format: int64
|
||
device_id:
|
||
type: integer
|
||
format: int64
|
||
MsgIDData:
|
||
type: object
|
||
properties:
|
||
msg_id:
|
||
type: integer
|
||
format: int64
|
||
headers:
|
||
X-Request-ID:
|
||
schema:
|
||
type: string
|
||
X-Trace-ID:
|
||
schema:
|
||
type: string
|
||
X-Device-ID:
|
||
schema:
|
||
type: string
|
||
description: Optional. Must match JWT did when present.
|
||
X-App-Version:
|
||
schema:
|
||
type: string
|
||
Accept-Language:
|
||
schema:
|
||
type: string
|
||
enum: [zh-CN, en, zh-TW]
|
||
example: zh-CN
|
||
description: 用户语言。也可用 X-Language。缺省 zh-CN。
|
||
X-Language:
|
||
schema:
|
||
type: string
|
||
enum: [zh-CN, en, zh-TW]
|
||
description: 覆盖 Accept-Language 的简洁语言标签。
|
||
X-Real-IP:
|
||
schema:
|
||
type: string
|
||
description: Nginx PROXY / SNI 写入的客户端真实 IP,进入 reqctx.RealIP。
|
||
parameters:
|
||
Authorization:
|
||
in: header
|
||
name: Authorization
|
||
required: true
|
||
schema:
|
||
type: string
|
||
description: "Bearer <jwt>"
|
||
X-Device-ID:
|
||
in: header
|
||
name: X-Device-ID
|
||
required: false
|
||
schema:
|
||
type: string
|
||
X-App-Version:
|
||
in: header
|
||
name: X-App-Version
|
||
required: false
|
||
schema:
|
||
type: string
|
||
Accept-Language:
|
||
in: header
|
||
name: Accept-Language
|
||
required: false
|
||
schema:
|
||
type: string
|
||
enum: [zh-CN, en, zh-TW]
|
||
description: 用户语言。缺省 zh-CN。
|
||
X-Language:
|
||
in: header
|
||
name: X-Language
|
||
required: false
|
||
schema:
|
||
type: string
|
||
enum: [zh-CN, en, zh-TW]
|
||
description: 覆盖 Accept-Language。
|
||
FriendUID:
|
||
in: path
|
||
name: uid
|
||
required: true
|
||
schema:
|
||
type: integer
|
||
format: int64
|
||
description: 对方 Snowflake uid。
|
||
FavoriteID:
|
||
in: path
|
||
name: id
|
||
required: true
|
||
schema:
|
||
type: integer
|
||
format: int64
|
||
description: 收藏 Snowflake id。
|
||
GroupID:
|
||
in: path
|
||
name: id
|
||
required: true
|
||
schema:
|
||
type: integer
|
||
format: int64
|
||
description: 自增群号(不是 conversation_id)。
|