Files
eryao/docs/protocols/profile/profile-protocol.md
T
qzl e80a82bef4 docs: 更新协议文档,删除废弃计划文档
- 更新 http-error-codes, user-points-chat-data-protocol
- 更新 divination-run-protocol, profile-protocol
- 删除废弃的后端和前端设计计划文档
2026-04-08 17:23:02 +08:00

4.2 KiB

Profile Protocol (Frontend <-> Backend)

This document defines the canonical backend contract for user profile read/write and avatar upload signing.

Protocol verification status:

  • Backend route source: backend/src/v1/users/router.py
  • Backend schema source: backend/src/v1/users/schemas.py
  • Backend service source: backend/src/v1/users/service.py
  • Frontend mapping source: apps/lib/features/settings/data/apis/profile_api.dart
  • Storage config source: backend/src/core/config/settings.py
  • Current status: aligned

Compatibility strategy

  • Current strategy: additive evolution (backward-compatible).
  • Breaking change requires explicit migration + rollback notes (requires-migration).

Route overview

  • Get profile: GET /api/v1/users/me/profile
  • Update profile: PATCH /api/v1/users/me/profile
  • Update settings: PATCH /api/v1/users/me/settings
  • Create avatar upload url: POST /api/v1/users/me/avatar/upload-url
  • Upload avatar directly: POST /api/v1/users/me/avatar (multipart)

Auth and trust boundary

  • All routes require authenticated user context.
  • user_id is derived from verified JWT sub; never accepted from client payload.

Profile read contract

GET /api/v1/users/me/profile

Response:

{
  "user_id": "uuid",
  "display_name": "string",
  "bio": "string|null",
  "avatar_path": "avatars/{user_id}/{file}",
  "avatar_url": "https://...signed-or-public...",
  "settings": {
    "version": 1,
    "preferences": {
      "interface_language": "zh-CN",
      "ai_language": "zh-CN",
      "timezone": "Asia/Shanghai",
      "country": "CN"
    },
    "privacy": {},
    "notification": {}
  },
  "updated_at": "2026-04-05T12:34:56+00:00"
}

Mapping note:

  • display_name maps to profiles.username.
  • avatar_path is stored in profile layer.
  • avatar_url is render-ready URL generated from storage strategy.

Profile update contract

PATCH /api/v1/users/me/profile

Request:

{
  "display_name": "string(1..30)",
  "bio": "string(0..200)",
  "avatar_path": "avatars/{user_id}/{file}"
}

Rules:

  • At least one field must be provided.
  • display_name must be non-empty after trim.
  • bio can be empty string and should be normalized to null only if agreed by API implementation.
  • avatar_path must stay in current user prefix: avatars/{current_user.id}/.

Response:

  • Returns the same shape as GET /users/me/profile.

Settings update contract

PATCH /api/v1/users/me/settings

Request:

{
  "settings": {
    "version": 1,
    "preferences": {
      "interface_language": "zh-CN",
      "ai_language": "zh-CN",
      "timezone": "Asia/Shanghai",
      "country": "CN"
    },
    "privacy": {},
    "notification": {
      "allow_notifications": true,
      "allow_vibration": true
    }
  }
}

Rules:

  • settings must conform to ProfileSettingsV1.
  • Additional fields are forbidden.

Response:

  • Returns the same shape as GET /users/me/profile.

Avatar upload signing contract

POST /api/v1/users/me/avatar/upload-url

Request:

{
  "mime_type": "image/png|image/jpeg|image/webp",
  "file_size": 123456,
  "ext": "png|jpg|jpeg|webp"
}

Response:

{
  "bucket": "avatars",
  "path": "avatars/{user_id}/{uuid}.png",
  "upload_url": "https://...signed...",
  "expires_in": 600
}

Validation rules:

  • bucket must equal config.storage.avatar.bucket.
  • file_size must be >0 and <= config.storage.avatar.max_size_mb.
  • Only image mime types are allowed.
  • Path must be server-generated and never trusted from client.

Direct avatar upload contract

POST /api/v1/users/me/avatar

Request:

  • multipart/form-data
  • field name: file

Validation rules:

  • extension must be one of png|jpg|jpeg|webp
  • mime must map to image type (image/png|image/jpeg|image/webp)
  • payload size must be <= config.storage.avatar.max_size_mb

Behavior:

  • backend writes avatar bytes to bucket=config.storage.avatar.bucket
  • backend stores canonical path in profile
  • response returns latest profile payload (ProfileResponse)

Error contract linkage

  • All errors must follow RFC7807 application/problem+json.
  • code values must be registered in docs/protocols/common/http-error-codes.md.