SAMA 0.37 API 详解
SAMA 0.37.2 双通道 API 详解:WebSocket JSON 包协议(60+ 路由按用户/会话/消息/文件/联系人/状态/加密/推送分域)与 HTTP 端点(/login、/health、/admin/*),全部给出 2026-10-30 实测请求与响应回显,覆盖认证链路、错误格式、预签名文件传输与包路由总表,注册/登录/频道/消息/文件 12 步全链路实测通过。
SAMA 0.37 API 详解
目标:面向要把 SAMA 集成进自己产品的开发者,讲清 0.37.2 的两套 API:WebSocket JSON 包协议(业务主体)与 HTTP 端点(认证 + 管理)。本文所有请求/响应均为 2026-10-30 实测回显(环境见 《SAMA 0.37 Docker Compose 部署指南》),路由定义与
APIs/JSON/routes/routes.js逐条核对。
1. 协议总览
| 通道 | 地址(实测映射) | 用途 |
|---|---|---|
| WebSocket JSON | ws://<host>:8162(nginx → server 9001/9002 负载均衡) | 业务全量:用户/会话/消息/文件/联系人等 60+ 路由 |
| HTTP | http://<host>:8162 | GET /health、POST /login、POST /logout、GET /health-stats、/admin/* 管理域 |
| TCP | APP_TCP_PORT 启用时(默认关) | 非 WS 客户端的透传通道 |
包格式:一条 WS 文本消息 = 一个 JSON 对象,键名即路由名(如 user_create、message)。服务端成功响应包裹在 {"response": {...}} 或动作专属键(如 {"ask": {...}})中,失败统一为 {"<路由名>":{"error":{"status":422,"message":"..."}}}。
2. 认证链路(实测完整时序)
① WS user_create(注册,免鉴权)
② HTTP POST /login → access_token(JWT)+ refresh_token HttpOnly Cookie
③ WS connect(携 token + device_id)→ 会话绑定
④ 之后所有 WS 路由经 authGuardMiddleware 鉴权
① 注册(organization_id 首次部署默认 default,见 《SAMA 0.37 Docker Compose 部署指南》 坑一):
→ {"user_create": {"organization_id": "default", "login": "alice",
"password": "Passw0rd123", "email": "alice@test.local", "device_id": "dev-alice-1"}}
← {"response": {"user": {"native_id": "6ae3e58b...", "_id": "6ae3e58b...",
"login": "alice", "email": "alice@test.local", "recent_activity": 1793320331,
"created_at": "2026-10-30T00:32:11.081Z", "updated_at": "..."}}}
② 登录(旧 WS user_login 路由已标弃用,官方推荐 HTTP):
curl -s -X POST http://localhost:8162/login -H 'Content-Type: application/json' \
-d '{"organization_id":"default","login":"alice","password":"Passw0rd123","device_id":"dev-alice-1"}'
← 200 {"user": {"_id": "6ae3e58b...", "login": "alice", ...},
"access_token": "eyJhbGciOi...", "expired_at": "..."}
Set-Cookie: refresh_token=s%3A...(HttpOnly; Secure; SameSite=Lax;有效期 JWT_REFRESH_TOKEN_EXPIRES_IN)
缺少 device_id 时实测报 'device_id' is required.。
③ WS 连接绑定会话:
→ {"connect": {"token": "<access_token>", "device_id": "dev-alice-1"}}
← {"response": {"success": true}}
token 过期实测回显:{"connect":{"error":{"status":422,"message":"The token has expired."}}},重新 /login 即可。登出用 POST /logout(读 Authorization 头或 refresh cookie),最后一个会话登出会广播离线状态。
3. 消息与会话(核心域实测)
创建频道(type:u 单聊 / g 群聊 / c 频道;g/c 必须带 name 与至少 1 个参与者,否则实测 422 Please select at least one user.):
→ {"conversation_create": {"type": "c", "name": "quzuo-test-channel",
"description": "forensics channel", "participants": ["<bob _id>"]}}
← {"response": {"conversation": {"subscribers_count": 2,
"_id": "6ae3e66a7b884607708c85d7", "type": "c", "name": "quzuo-test-channel",
"description": "forensics channel", "owner_id": "<alice _id>",
"organization_id": "default", "created_at": "...", "updated_at": "..."}}}
发消息(消息体平铺:id 为客户端消息号,cid 为会话 ID,x 为自定义扩展字段;响应键 ask):
→ {"message": {"id": "mid-001", "cid": "6ae3e66a...", "x": {}, "body": "Hello SAMA from quzuo!"}}
← {"ask": {"mid": "mid-001", "server_mid": "6ae3e66a7b884607708c85d8",
"t": 1793320555, "modified": null, "bot_message": null}}
拉取消息(bob 端):
→ {"message_list": {"cid": "6ae3e66a...", "limit": 10}}
← {"message": {"_id": "6ae3e66a7b884607708c85d8", "cid": "6ae3e66a...",
"c_type": "c", "from": "<alice _id>", "t": 1793320555,
"body": "Hello SAMA from quzuo!", "x": {}, "updated_at": "...", "created_at": "..."}}
频道订阅(仅 type=c 且必须先订阅才能收后续推送):
→ {"conversation_subscribe": {"cid": "6ae3e66a..."}} ← {"response": {"success": true}}
4. 文件传输(预签名 URL,实测)
① create_files → 返回 upload_url(预签名 PUT)
② HTTP PUT 文件到 upload_url(实测 200)
③ message 携带 attachments(file_id = 返回的 object_id)
④ 接收方 get_file_urls → 预签名下载 URL(默认 7 天)
→ {"create_files": [{"name": "hello.txt", "size": 27, "content_type": "text/plain"}]}
← {"response": {"files": [{"_id": "6ae3e66a7b884607708c85d9",
"name": "hello.txt", "size": 27, "content_type": "text/plain",
"object_id": "2efc4cb0bbfdc0dab83c83f0", "organization_id": "default",
"user_id": "<alice _id>", "upload_url": "http://<minio>:9000/samabucket/2efc4cb0...?X-Amz-Algorithm=AWS4-HMAC-SHA256&..."}]}}
注意:
get_file_urls按 object_id 查文件(源码FileRepository.findByIdWithOrgScope查询字段为object_id),传_id会报Can't find file(实测踩坑)。下载 URL 有效期由FILE_DOWNLOAD_URL_EXPIRES_IN控制(实测回显X-Amz-Expires=604800)。
→ {"get_file_urls": {"file_ids": ["2efc4cb0bbfdc0dab83c83f0"]}}
← {"response": {"file_urls": {"2efc4cb0bbfdc0dab83c83f0": "http://<minio>:9000/samabucket/2efc4cb0...?X-Amz-Expires=604800&..."}}}
带附件发消息(attachments 数组,file_id 用 object_id):
→ {"message": {"id": "mid-002", "cid": "6ae3e66a...", "body": "",
"attachments": [{"file_id": "2efc4cb0...", "file_name": "hello.txt",
"file_content_type": "text/plain", "file_size": 27}]}}
← {"ask": {"mid": "mid-002", "server_mid": "6ae3e66a7b884607708c85da", "t": 1793320555, ...}}
5. WS 路由总表(与 routes.js 逐条核对)
| 域 | 路由 |
|---|---|
| 连接/探活 | ping、connect |
| 用户 | user_create、user_edit、user_delete、user_login(弃用)、user_logout(弃用)、user_search、user_send_otp、user_reset_password、get_users_by_ids、user_last_activity、user_last_activity_subscribe、user_last_activity_unsubscribe |
| 设备 | device_register、device_list、device_delete |
| 会话 | conversation_create、conversation_list、conversation_update、conversation_delete、conversation_search、conversation_subscribe、conversation_unsubscribe、conversation_handler_create、conversation_handler_delete、get_conversation_handler、get_participants_by_cids、get_admins_by_cids |
| 消息 | message、message_edit、message_delete、message_list、message_read、message_summary、message_tone、message_reactions_update、message_reactions_list、message_decryption_failed、system_message |
| 文件 | create_files、get_file_urls |
| 联系人 | contact_add、contact_batch_add、contact_update、contact_delete、contact_list |
| 拉黑 | block_user、unblock_user、block_list_enable、list_blocked_users |
| 状态 | typing、online_list、activity_status |
| 加密 | request_keys(Signal 风格设备密钥交换) |
| 推送 | push_subscription_create、push_subscription_delete、push_subscription_list、push_event_create |
| 日志 | op_log_list |
公共字段:ping → {"response":{"pong":{}}}(实测心跳保活);分页类路由支持 limit/offset;带 id 字段的路由会在响应中回带请求号。
6. HTTP 管理端点(/admin/*,实测结构)
以 Authorization: Bearer <HTTP_ADMIN_API_KEY> 调用,Key 错误实测 401。路由定义于 app/networking/protocol_processors/http.js:
| 端点 | 方法 | 用途 |
|---|---|---|
/health | GET | 健康检查,实测 {"status":"ok","hostname":"..."} |
/health-stats | GET | 无鉴权运行统计 |
/login / /logout | POST | 用户认证(见 §2) |
/admin/organization | POST | 创建组织(多租户) |
/admin/message | POST/PUT/DELETE | 以管理员身份发/改/删消息 |
/admin/message/system | POST | 发系统消息 |
/admin/message/read | PUT | 标记已读 |
/admin/message/reaction | PUT | 更新表情回应 |
/admin/activity/online | POST | 查询在线用户 |
/admin/server-stats | GET | 服务器统计 |
7. 错误格式与排障
{"message": {"error": {"status": 422, "message": "'cid' field is required."}}}
实测高频错误:422 The token has expired.(token 过期重登)、422 Please select at least one user.(会话参与者为空)、422 'cid' field is required.(消息缺会话 ID)、401 Unauthorized.(未 connect 或未订阅即操作)、Can't find file(get_file_urls 误用 _id)。服务端日志 docker compose logs -f sama-server,日志级别见 《SAMA 0.37 配置参数详解》。
选型与架构背景:《SAMA 0.37 介绍与选型指南》。