Files
social-app/docs/runtime/runtime-route.md
T
zl-q ec33bb0cee refactor: 统一认证端点并删除冗余 profile 模块
- 合并 auth 端点: /verifications/verify → /verify, /verifications/resend → /resend
- 整合密码重置到 /verify 端点 (type=recovery)
- 移除未使用的 /auth/users 端点
- 添加 redirect URL 白名单验证 (site_url + additional_redirect_urls)
- 限流改用 Redis + IP 标识,替代内存锁
- 删除 v1/profile 死代码模块
- 更新前端 auth_api 适配新端点
- 添加 supabase site_url 和 additional_redirect_urls 配置
2026-03-07 14:55:00 +08:00

13 KiB
Raw Blame History

Runtime API Routes

本文档记录所有 HTTP API 端点。修改路由时必须同步更新此文档。

格式说明

  • Request/Response 使用 JSON 格式
  • 错误响应使用 RFC 7807 application/problem+json
  • 所有端点前缀: /api/v1

Auth

POST /auth/verifications

创建验证码(注册发起)。

Request:

{
  "username": "string (3-30 chars)",
  "email": "string (email)",
  "password": "string (min 6 chars)",
  "redirect_to": "string? (optional)",
  "invite_code": "string? (8 chars, 排除易混淆字符 0/1/I/L/O)"
}

Response: 202 Accepted

{
  "email": "user@example.com"
}

邀请码说明:

  • 可选字段,不填则注册不受影响
  • 格式:8 位字母数字组合,排除易混淆字符 (0, 1, I, L, O)
  • 注册时传入有效邀请码会建立邀请关系并增加邀请码使用次数
  • 无效邀请码(不存在/已禁用/已过期/已达上限)不会阻断注册成功

Errors:

  • 422: 请求参数无效
  • 429: 请求过于频繁

POST /auth/resend

重发验证码(统一端点,支持注册/找回密码)。

Request:

{
  "type": "signup | recovery (default: signup)",
  "email": "string (email)",
  "redirect_to": "string? (仅 recovery 可选)"
}

Response: 204 No Content

Errors:

  • 422: 请求参数无效
  • 429: 请求过于频繁

POST /auth/verify

验证码校验(统一端点,按 type 区分场景)。

Request (signup):

{
  "type": "signup",
  "email": "string (email)",
  "token": "string (6 digits)"
}

Response (signup): 200 OK

{
  "access_token": "string",
  "refresh_token": "string",
  "expires_in": 3600,
  "token_type": "bearer",
  "user": {
    "id": "string",
    "email": "string"
  }
}

Request (recovery):

{
  "type": "recovery",
  "email": "string (email)",
  "token": "string (6 digits)",
  "new_password": "string (min 6 chars)"
}

Response (recovery): 204 No Content

Errors:

  • 401: 验证码无效或已过期
  • 422: 请求参数无效
  • 429: 请求过于频繁

POST /auth/sessions

登录(创建会话)。

Request:

{
  "email": "string (email)",
  "password": "string (min 6 chars)"
}

Response: 200 OK

{
  "access_token": "string",
  "refresh_token": "string",
  "expires_in": 3600,
  "token_type": "bearer",
  "user": {
    "id": "string",
    "email": "string"
  }
}

Errors:

  • 401: 邮箱或密码错误
  • 422: 请求参数无效
  • 429: 请求过于频繁

POST /auth/sessions/refresh

刷新 Token。

Request:

{
  "refresh_token": "string"
}

Response: 200 OK

{
  "access_token": "string",
  "refresh_token": "string",
  "expires_in": 3600,
  "token_type": "bearer",
  "user": {
    "id": "string",
    "email": "string"
  }
}

Errors:

  • 401: 无效的 refresh token
  • 422: 请求参数无效
  • 429: 请求过于频繁

DELETE /auth/sessions

登出(删除会话)。

Request:

{
  "refresh_token": "string"
}

Response: 204 No Content

Errors:

  • 422: 请求参数无效
  • 429: 请求过于频繁

GET /auth/users

按邮箱查询用户(需要认证)。

Query Parameters:

  • email: string (required)

Response: 200 OK

{
  "id": "string",
  "email": "string",
  "created_at": "string (ISO 8601)",
  "email_confirmed_at": "string? (ISO 8601)"
}

Errors:

  • 403: 无权限访问
  • 404: 用户不存在
  • 422: 请求参数无效

Schedule Items

POST /schedule-items

创建日历事项(需要认证)。

Request:

{
  "title": "string (1-255 chars, required)",
  "description": "string? (max 2000 chars)",
  "start_at": "string (ISO 8601 datetime, required)",
  "end_at": "string? (ISO 8601 datetime)",
  "timezone": "string? (default: UTC)",
  "metadata": {
    "color": "#FF6B6B",
    "location": "会议室A",
    "notes": "记得带身份证",
    "attachments": [],
    "version": 1
  }
}

Response: 201 Created

{
  "id": "uuid",
  "title": "string",
  "description": "string?",
  "start_at": "string",
  "end_at": "string?",
  "timezone": "string",
  "metadata": {},
  "status": "active",
  "source_type": "manual",
  "created_at": "string",
  "updated_at": "string"
}

Errors:

  • 400: end_at 早于 start_at
  • 401: 未认证
  • 503: 服务不可用

GET /schedule-items

按时间范围查询日历事项列表(需要认证)。

Query Parameters:

  • start_at: ISO 8601 date/datetime(查询范围起始)
  • end_at: ISO 8601 date/datetime(查询范围结束)

Response: 200 OK

[
  {
    "id": "uuid",
    "title": "string",
    "start_at": "string",
    "end_at": "string?",
    "timezone": "string",
    "status": "active"
  }
]

Errors:

  • 400: end_at 早于 start_at
  • 401: 未认证

GET /schedule-items/{id}

获取单个日历事项详情(需要认证)。

Response: 200 OK

Errors:

  • 401: 未认证
  • 404: 事项不存在

PATCH /schedule-items/{id}

更新日历事项(需要认证)。

Request: 支持 title/description/start_at/end_at/timezone/metadata/status 部分更新

Response: 200 OK

Errors:

  • 401: 未认证
  • 404: 事项不存在

DELETE /schedule-items/{id}

删除日历事项(软删除,需要认证)。

Response: 204 No Content

Errors:

  • 401: 未认证
  • 404: 事项不存在

POST /schedule-items/{id}/share

分享日历事项给他人(需要认证)。

通过邮箱邀请其他用户,被邀请人将收到待办消息邀请。

Request:

{
  "email": "string (required, email of user to share with)",
  "permission_view": "boolean (default: true)",
  "permission_edit": "boolean (default: false)",
  "permission_invite": "boolean (default: false)"
}

Permission 位说明:

权限 说明
view 1 查看事项详情
invite 2 邀请其他人订阅此事项
edit 4 修改事项内容、管理订阅

可组合使用,如 view+edit = 5view+invite+edit = 7。

Response: 200 OK

{
  "message": "Invitation sent to user@example.com"
}

Errors:

  • 401: 未认证
  • 403: 非日历所有者无权分享
  • 404: 日历事项不存在或用户不存在

Inbox Messages

GET /inbox/messages

获取当前用户的待办消息列表(需要认证)。

Query Parameters:

  • status: string (optional) - 过滤状态:pending/accepted/rejected/dismissed

Response: 200 OK

[
  {
    "id": "uuid",
    "recipient_id": "uuid",
    "sender_id": "uuid?",
    "message_type": "calendar",
    "schedule_item_id": "uuid?",
    "content": "string?",
    "is_read": false,
    "status": "pending",
    "created_at": "2024-01-01T00:00:00Z"
  }
]

Errors:

  • 401: 未认证

POST /inbox/messages/{id}/accept

接受邀请(需要认证)。

接受日历邀请时,会为当前用户创建订阅关系。

Request:

{
  "permission_view": "boolean (default: true)",
  "permission_edit": "boolean (default: false)",
  "permission_invite": "boolean (default: false)"
}

Response: 204 No Content

Errors:

  • 401: 未认证
  • 404: 消息不存在
  • 400: 消息不是待处理状态或不是日历类型邀请

POST /inbox/messages/{id}/dismiss

忽略邀请(需要认证)。

Response: 204 No Content

Errors:

  • 401: 未认证
  • 404: 消息不存在
  • 400: 消息不是待处理状态

Users

GET /users/me

获取当前用户信息(需要认证)。

Response: 200 OK

{
  "id": "string",
  "username": "string",
  "avatar_url": "string?",
  "bio": "string?"
}

Errors:

  • 401: 未认证

PATCH /users/me

更新当前用户信息(需要认证)。

Request:

{
  "username": "string? (3-30 chars)",
  "avatar_url": "string? (URL)",
  "bio": "string? (max 200 chars)"
}

Response: 200 OK

{
  "id": "string",
  "username": "string",
  "avatar_url": "string?",
  "bio": "string?"
}

Errors:

  • 401: 未认证
  • 422: 请求参数无效

POST /users/search

搜索用户(需要认证)。

支持两种查询模式:

  • 用户名查询:模糊匹配,返回最多 20 个结果
  • 邮箱查询:精确匹配,返回 0 或 1 个结果

查询类型自动识别:包含 @ 符号视为邮箱查询。

Request:

{
  "query": "string (1-100 chars)"
}

Response: 200 OK

[
  {
    "id": "string",
    "username": "string",
    "avatar_url": "string?",
    "bio": "string?"
  }
]

Errors:

  • 401: 未认证
  • 503: Auth 服务不可用(仅邮箱查询)
  • 422: 请求参数无效

Friends

POST /friends/requests

发送好友请求(需要认证)。

Request:

{
  "target_user_id": "string (uuid)",
  "content": "string? (max 500 chars)"
}

Response: 201 Created

{
  "id": "uuid",
  "from_user_id": "uuid",
  "to_user_id": "uuid",
  "content": "string?",
  "status": "pending",
  "created_at": "string (ISO 8601)",
  "updated_at": "string (ISO 8601)"
}

Errors:

  • 400: 不能添加自己为好友
  • 401: 未认证
  • 404: 目标用户不存在
  • 409: 已是好友或请求已存在
  • 422: 请求参数无效

GET /friends/requests/inbox

获取收到的好友请求(需要认证)。

Response: 200 OK

[
  {
    "id": "uuid",
    "from_user_id": "uuid",
    "to_user_id": "uuid",
    "content": "string?",
    "status": "pending",
    "created_at": "string (ISO 8601)",
    "updated_at": "string (ISO 8601)"
  }
]

Errors:

  • 401: 未认证

GET /friends/requests/outgoing

获取发出的好友请求(需要认证)。

Response: 200 OK

[
  {
    "id": "uuid",
    "from_user_id": "uuid",
    "to_user_id": "uuid",
    "content": "string?",
    "status": "pending",
    "created_at": "string (ISO 8601)",
    "updated_at": "string (ISO 8601)"
  }
]

Errors:

  • 401: 未认证

POST /friends/requests/{id}/accept

接受好友请求(需要认证)。

Response: 200 OK

{
  "id": "uuid",
  "from_user_id": "uuid",
  "to_user_id": "uuid",
  "content": "string?",
  "status": "accepted",
  "created_at": "string (ISO 8601)",
  "updated_at": "string (ISO 8601)"
}

Errors:

  • 401: 未认证
  • 404: 请求不存在
  • 409: 请求已被处理

POST /friends/requests/{id}/decline

拒绝好友请求(需要认证)。

Response: 200 OK

{
  "id": "uuid",
  "from_user_id": "uuid",
  "to_user_id": "uuid",
  "content": "string?",
  "status": "declined",
  "created_at": "string (ISO 8601)",
  "updated_at": "string (ISO 8601)"
}

Errors:

  • 401: 未认证
  • 404: 请求不存在
  • 409: 请求已被处理

DELETE /friends/requests/{id}

取消发出的好友请求(需要认证)。

Response: 204 No Content

Errors:

  • 401: 未认证
  • 404: 请求不存在

GET /friends

获取好友列表(需要认证)。

Response: 200 OK

[
  {
    "id": "uuid",
    "friend_id": "uuid",
    "username": "string",
    "avatar_url": "string?",
    "bio": "string?",
    "created_at": "string (ISO 8601)"
  }
]

Errors:

  • 401: 未认证

DELETE /friends/{id}

删除好友(需要认证)。

Response: 204 No Content

Errors:

  • 401: 未认证
  • 404: 好友关系不存在

Agent Runtime

POST /agent/runs

创建一次 Agent 异步运行任务(需要认证)。

Request:

{
  "session_id": "string? (optional, 为空时自动创建会话)",
  "prompt": "string (1-5000 chars)"
}

Response: 202 Accepted

{
  "task_id": "string",
  "session_id": "string",
  "created": true
}

Errors:

  • 401: 未认证
  • 403: 非会话 owner
  • 422: 请求参数无效

POST /agent/runs/{session_id}/resume

恢复一次等待工具结果的 Agent 运行(需要认证)。

Request:

{
  "tool_call_id": "string"
}

Response: 202 Accepted

{
  "task_id": "string",
  "session_id": "string",
  "created": false
}

Errors:

  • 401: 未认证
  • 403: 非会话 owner
  • 422: 请求参数无效

GET /agent/runs/{session_id}/events

订阅 Agent SSE 事件流(需要认证)。

Headers:

  • Last-Event-ID (optional): 断点续传游标

Response: 200 OK Content-Type: text/event-stream

id: 2-0
event: RUN_STARTED
data: {"session_id":"..."}

Errors:

  • 401: 未认证
  • 403: 非会话 owner

Infra

GET /infra/health

检查基础设施健康状态。

Response: 200 OK

{
  "status": "healthy" | "unhealthy",
  "services": {
    "redis": {
      "status": "healthy" | "unhealthy",
      "latency_ms": 0
    }
  }
}

GET /health

检查服务健康状态。

Response: 200 OK

{
  "status": "ok"
}

Error Response Format (RFC 7807)

所有错误响应使用 application/problem+json 格式:

{
  "type": "about:blank",
  "title": "Unauthorized",
  "status": 401,
  "detail": "验证码无效或已过期",
  "instance": "/api/v1/auth/verify"
}

前端应优先读取 detail 字段显示给用户。