Files
social-app/docs/plans/2026-02-26-restful-api-refactor-plan.md
T

31 KiB
Raw Blame History

RESTful API 重构实现计划

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: 将后端 HTTP API 改造为完全符合 RESTful 规范,同步更新前端适配代码,并添加路由文档。

Architecture:

  • 后端:重命名路由 URL,简化响应模型,profile 模块重命名为 users
  • 前端:更新 API 调用路径,简化响应模型
  • 文档:新增路由文档,更新 AGENTS.md 规则

Tech Stack: FastAPI, Pydantic, Flutter, Dart


Phase 1: 后端 Schema 重构

Task 1: 重命名和简化 Auth Schema

Files:

  • Modify: backend/src/v1/auth/schemas.py

Step 1: 更新 schema 文件

将现有 schema 重命名并简化:

from __future__ import annotations

from typing import Literal

from pydantic import BaseModel, EmailStr, Field


class VerificationCreateRequest(BaseModel):
    username: str = Field(min_length=3, max_length=30)
    email: EmailStr
    password: str = Field(min_length=6)
    redirect_to: str | None = None


class VerificationResendRequest(BaseModel):
    email: EmailStr


class VerificationVerifyRequest(BaseModel):
    email: EmailStr
    token: str = Field(pattern=r"^\d{6}$")


class SessionCreateRequest(BaseModel):
    email: EmailStr
    password: str = Field(min_length=6)


class SessionRefreshRequest(BaseModel):
    refresh_token: str = Field(min_length=1)


class SessionDeleteRequest(BaseModel):
    refresh_token: str = Field(min_length=1)


class AuthUser(BaseModel):
    id: str
    email: EmailStr


class SessionResponse(BaseModel):
    access_token: str
    refresh_token: str
    expires_in: int
    token_type: str
    user: AuthUser


class UserByEmailResponse(BaseModel):
    id: str
    email: EmailStr
    created_at: str
    email_confirmed_at: str | None = None


class VerificationCreateResponse(BaseModel):
    email: EmailStr


class PasswordResetRequest(BaseModel):
    email: EmailStr
    redirect_to: str | None = None


class PasswordResetResponse(BaseModel):
    message: str = "Password reset email sent"

Step 2: 验证类型检查

Run: cd backend && uv run basedpyright src/v1/auth/schemas.py Expected: No errors

Step 3: Commit

git add backend/src/v1/auth/schemas.py
git commit -m "refactor(auth): rename and simplify auth schemas for RESTful API"

Task 2: 创建 Users Schema

Files:

  • Create: backend/src/v1/users/schemas.py

Step 1: 创建 users schema 文件

from __future__ import annotations

from pydantic import (
    AnyHttpUrl,
    BaseModel,
    ConfigDict,
    Field,
    field_validator,
    model_validator,
)


class UserResponse(BaseModel):
    id: str
    username: str
    avatar_url: str | None = None
    bio: str | None = None


class UserUpdateRequest(BaseModel):
    model_config = ConfigDict(extra="forbid")

    username: str | None = Field(default=None, min_length=3, max_length=30)
    avatar_url: str | None = Field(default=None)
    bio: str | None = Field(default=None, max_length=200)

    @field_validator("avatar_url", mode="before")
    @classmethod
    def validate_avatar_url(cls, v: str | None) -> str | None:
        if v is None:
            return None
        parsed = AnyHttpUrl(v)
        if parsed.scheme not in ("http", "https"):
            raise ValueError("avatar_url must use http or https scheme")
        return str(parsed)

    @model_validator(mode="after")
    def require_one_field(self) -> "UserUpdateRequest":
        if self.username is None and self.avatar_url is None and self.bio is None:
            raise ValueError("At least one field must be provided")
        return self

Step 2: 验证类型检查

Run: cd backend && uv run basedpyright src/v1/users/schemas.py Expected: No errors

Step 3: Commit

git add backend/src/v1/users/schemas.py
git commit -m "feat(users): create users schemas"

Task 3: 创建 Users 模块基础文件

Files:

  • Create: backend/src/v1/users/__init__.py
  • Create: backend/src/v1/users/dependencies.py
  • Move: backend/src/v1/profile/repository.pybackend/src/v1/users/repository.py
  • Move: backend/src/v1/profile/service.pybackend/src/v1/users/service.py

Step 1: 创建 __init__.py

from __future__ import annotations

Step 2: 创建 dependencies.py

from __future__ import annotations

from typing import Annotated

from fastapi import Depends

from core.auth.models import CurrentUser
from core.db import get_db
from v1.auth.dependencies import get_current_user
from v1.users.repository import UserRepository
from v1.users.service import UserService


async def get_user_repository(
    db=Depends(get_db),
) -> UserRepository:
    return UserRepository(db)


async def get_user_service(
    repo: Annotated[UserRepository, Depends(get_user_repository)],
    current_user: Annotated[CurrentUser, Depends(get_current_user)],
) -> UserService:
    return UserService(repo, current_user)

Step 3: 移动并更新 repository.py

复制 v1/profile/repository.pyv1/users/repository.py,无需修改内容。

Step 4: 移动并更新 service.py

复制 v1/profile/service.pyv1/users/service.py,更新导入:

from v1.users.schemas import UserResponse, UserUpdateRequest

Step 5: 验证类型检查

Run: cd backend && uv run basedpyright src/v1/users/ Expected: No errors

Step 4: Commit

git add backend/src/v1/users/
git commit -m "feat(users): create users module from profile"

Phase 2: 后端路由重构

Task 4: 重构 Auth Router

Files:

  • Modify: backend/src/v1/auth/router.py

Step 1: 更新路由定义

from __future__ import annotations

from typing import Annotated

from fastapi import APIRouter, Depends, Response
from fastapi import HTTPException

from core.auth.models import CurrentUser
from v1.auth.rate_limit import enforce_rate_limit
from v1.auth.dependencies import get_auth_service
from v1.users.dependencies import get_current_user
from v1.auth.schemas import (
    VerificationCreateRequest,
    VerificationCreateResponse,
    VerificationResendRequest,
    VerificationVerifyRequest,
    SessionCreateRequest,
    SessionDeleteRequest,
    SessionRefreshRequest,
    SessionResponse,
    UserByEmailResponse,
)
from v1.auth.service import AuthService


router = APIRouter(prefix="/auth", tags=["auth"])


@router.post("/verifications", response_model=VerificationCreateResponse, status_code=202)
async def create_verification(
    payload: VerificationCreateRequest,
    service: AuthService = Depends(get_auth_service),
) -> VerificationCreateResponse:
    await enforce_rate_limit(
        scope="signup_start",
        identifier=payload.email,
        limit=5,
        window_seconds=60,
    )
    return await service.create_verification(payload)


@router.post("/verifications/resend", status_code=204)
async def resend_verification(
    payload: VerificationResendRequest,
    service: AuthService = Depends(get_auth_service),
) -> Response:
    await enforce_rate_limit(
        scope="signup_resend",
        identifier=payload.email,
        limit=5,
        window_seconds=60,
    )
    await service.resend_verification(payload)
    return Response(status_code=204)


@router.post("/verifications/verify", response_model=SessionResponse)
async def verify_verification(
    payload: VerificationVerifyRequest,
    service: AuthService = Depends(get_auth_service),
) -> SessionResponse:
    await enforce_rate_limit(
        scope="signup_verify",
        identifier=payload.email,
        limit=10,
        window_seconds=600,
    )
    return await service.verify_verification(payload)


@router.post("/sessions", response_model=SessionResponse)
async def create_session(
    payload: SessionCreateRequest,
    service: AuthService = Depends(get_auth_service),
) -> SessionResponse:
    await enforce_rate_limit(
        scope="login",
        identifier=payload.email,
        limit=10,
        window_seconds=60,
    )
    return await service.create_session(payload)


@router.post("/sessions/refresh", response_model=SessionResponse)
async def refresh_session(
    payload: SessionRefreshRequest,
    service: AuthService = Depends(get_auth_service),
) -> SessionResponse:
    await enforce_rate_limit(
        scope="refresh",
        identifier=payload.refresh_token,
        limit=10,
        window_seconds=60,
    )
    return await service.refresh_session(payload)


@router.delete("/sessions", status_code=204)
async def delete_session(
    payload: SessionDeleteRequest,
    service: AuthService = Depends(get_auth_service),
) -> Response:
    await enforce_rate_limit(
        scope="logout",
        identifier=payload.refresh_token,
        limit=10,
        window_seconds=60,
    )
    await service.delete_session(payload.refresh_token)
    return Response(status_code=204)


@router.get("/users", response_model=UserByEmailResponse)
async def get_user_by_email(
    email: str,
    current_user: Annotated[CurrentUser, Depends(get_current_user)],
    service: AuthService = Depends(get_auth_service),
) -> UserByEmailResponse:
    if current_user.role != "service_role" and current_user.email != email:
        raise HTTPException(status_code=403, detail="Forbidden")
    return await service.get_user_by_email(email)

Step 2: 验证类型检查

Run: cd backend && uv run basedpyright src/v1/auth/router.py Expected: No errors

Step 3: Commit

git add backend/src/v1/auth/router.py
git commit -m "refactor(auth): rename routes to RESTful style"

Task 5: 重构 Auth Service

Files:

  • Modify: backend/src/v1/auth/service.py

Step 1: 更新 service 方法名和导入

更新导入和方法名以匹配新的 schema:

from v1.auth.schemas import (
    VerificationCreateRequest,
    VerificationCreateResponse,
    VerificationResendRequest,
    VerificationVerifyRequest,
    SessionCreateRequest,
    SessionDeleteRequest,
    SessionRefreshRequest,
    SessionResponse,
    UserByEmailResponse,
)

将方法重命名:

  • signup_startcreate_verification
  • signup_resendresend_verification
  • signup_verifyverify_verification
  • logincreate_session
  • refreshrefresh_session
  • logoutdelete_session

create_verification 返回 VerificationCreateResponse(email=payload.email)

Step 2: 验证类型检查

Run: cd backend && uv run basedpyright src/v1/auth/service.py Expected: No errors

Step 3: Commit

git add backend/src/v1/auth/service.py
git commit -m "refactor(auth): rename service methods for RESTful API"

Task 6: 更新 Auth Gateway

Files:

  • Modify: backend/src/v1/auth/gateway.py

Step 1: 更新导入和方法调用

更新导入使用新 schema 名称,方法调用更新为新的 service 方法名。

Step 2: 验证类型检查

Run: cd backend && uv run basedpyright src/v1/auth/gateway.py Expected: No errors

Step 3: Commit

git add backend/src/v1/auth/gateway.py
git commit -m "refactor(auth): update gateway for new service methods"

Task 7: 创建 Users Router

Files:

  • Create: backend/src/v1/users/router.py

Step 1: 创建 users router

from __future__ import annotations

from typing import Annotated

from fastapi import APIRouter, Depends, Path

from v1.users.dependencies import get_user_service
from v1.users.schemas import UserResponse, UserUpdateRequest
from v1.users.service import UserService


router = APIRouter(prefix="/users", tags=["users"])


@router.get("/me", response_model=UserResponse)
async def get_me(
    service: Annotated[UserService, Depends(get_user_service)],
) -> UserResponse:
    return await service.get_me()


@router.patch("/me", response_model=UserResponse)
async def update_me(
    payload: UserUpdateRequest,
    service: Annotated[UserService, Depends(get_user_service)],
) -> UserResponse:
    return await service.update_me(payload)


@router.get("/{username}", response_model=UserResponse)
async def get_by_username(
    username: Annotated[
        str, Path(min_length=3, max_length=30, pattern="^[a-zA-Z0-9_]+$")
    ],
    service: Annotated[UserService, Depends(get_user_service)],
) -> UserResponse:
    return await service.get_by_username(username)

Step 2: 验证类型检查

Run: cd backend && uv run basedpyright src/v1/users/router.py Expected: No errors

Step 3: Commit

git add backend/src/v1/users/router.py
git commit -m "feat(users): create users router"

Task 8: 更新 Agent Chat Router

Files:

  • Modify: backend/src/v1/agent_chat/router.py

Step 1: 更新 URL 路径

/run 改为根路径:

@router.post("", response_model=AgentChatRunResponse)
async def run_agent_chat(
    payload: AgentChatRunRequest,
    service: Annotated[AgentChatService, Depends(get_agent_chat_service)],
) -> AgentChatRunResponse:
    return await service.run(payload)

Step 2: 验证类型检查

Run: cd backend && uv run basedpyright src/v1/agent_chat/router.py Expected: No errors

Step 3: Commit

git add backend/src/v1/agent_chat/router.py
git commit -m "refactor(agent-chat): change route to RESTful style"

Task 9: 更新主路由注册

Files:

  • Modify: backend/src/v1/router.py

Step 1: 更新路由注册

from __future__ import annotations

from fastapi import APIRouter

from core.http.models import HealthResponse
from v1.agent_chat.router import router as agent_chat_router
from v1.auth.router import router as auth_router
from v1.infra.router import router as infra_router
from v1.users.router import router as users_router


router = APIRouter(prefix="/api/v1")
router.include_router(auth_router)
router.include_router(infra_router)
router.include_router(users_router)
router.include_router(agent_chat_router)


@router.get("/health", response_model=HealthResponse)
async def health() -> HealthResponse:
    return HealthResponse(status="ok")

Step 2: 验证类型检查

Run: cd backend && uv run basedpyright src/v1/router.py Expected: No errors

Step 3: Commit

git add backend/src/v1/router.py
git commit -m "refactor: register users router instead of profile"

Phase 3: 后端测试更新

Task 10: 更新 Auth 路由测试

Files:

  • Modify: backend/tests/integration/test_auth_routes.py

Step 1: 更新测试用例的 URL 和断言

  • /signup/start/auth/verifications
  • /signup/resend/auth/verifications/resend
  • /signup/verify/auth/verifications/verify
  • /login/auth/sessions
  • /refresh/auth/sessions/refresh
  • /logout/auth/sessions
  • /users/by-email/auth/users?email=xxx

更新断言:

  • signup_start 响应只包含 email 字段
  • signup_resend 响应为 204,无 body

Step 2: 运行测试验证

Run: cd backend && uv run pytest tests/integration/test_auth_routes.py -v Expected: All tests pass

Step 3: Commit

git add backend/tests/integration/test_auth_routes.py
git commit -m "test(auth): update integration tests for RESTful routes"

Task 11: 创建 Users 路由测试

Files:

  • Move: backend/tests/integration/test_profile_routes.pybackend/tests/integration/test_users_routes.py

Step 1: 移动并更新测试文件

  • 更新 URL/profile/me/users/me/profile/{username}/users/{username}
  • 更新导入和断言

Step 2: 运行测试验证

Run: cd backend && uv run pytest tests/integration/test_users_routes.py -v Expected: All tests pass

Step 3: Commit

git add backend/tests/integration/test_users_routes.py
git rm backend/tests/integration/test_profile_routes.py
git commit -m "test(users): rename profile tests to users"

Task 12: 运行完整测试套件

Step 1: 运行所有后端测试

Run: cd backend && uv run pytest -v Expected: All tests pass

Step 2: 运行类型检查

Run: cd backend && uv run basedpyright src/ Expected: No errors

Step 3: 运行 lint

Run: cd backend && uv run ruff check src/ Expected: No errors


Phase 4: 前端适配

Task 13: 更新 Auth API

Files:

  • Modify: apps/lib/features/auth/data/auth_api.dart

Step 1: 更新 URL 路径

import 'package:social_app/core/api/api_client.dart';
import 'models/signup_request.dart';
import 'models/login_request.dart';
import 'models/auth_response.dart';

class AuthApi {
  final ApiClient _client;
  static const _prefix = '/api/v1/auth';

  AuthApi(this._client);

  Future<VerificationCreateResponse> createVerification(SignupStartRequest request) async {
    final response = await _client.post(
      '$_prefix/verifications',
      data: request.toJson(),
    );
    return VerificationCreateResponse.fromJson(response.data);
  }

  Future<void> resendVerification(SignupResendRequest request) async {
    await _client.post(
      '$_prefix/verifications/resend',
      data: request.toJson(),
    );
  }

  Future<AuthResponse> verifyVerification(SignupVerifyRequest request) async {
    final response = await _client.post(
      '$_prefix/verifications/verify',
      data: request.toJson(),
    );
    return AuthResponse.fromJson(response.data);
  }

  Future<AuthResponse> createSession(LoginRequest request) async {
    final response = await _client.post(
      '$_prefix/sessions',
      data: request.toJson(),
    );
    return AuthResponse.fromJson(response.data);
  }

  Future<AuthResponse> refreshSession(RefreshRequest request) async {
    final response = await _client.post(
      '$_prefix/sessions/refresh',
      data: request.toJson(),
    );
    return AuthResponse.fromJson(response.data);
  }

  Future<void> deleteSession(LogoutRequest request) async {
    await _client.delete(
      '$_prefix/sessions',
      data: request.toJson(),
    );
  }
}

Step 2: Commit

git add apps/lib/features/auth/data/auth_api.dart
git commit -m "refactor(auth): update API routes to RESTful style"

Task 14: 更新 Auth Response Models

Files:

  • Modify: apps/lib/features/auth/data/models/auth_response.dart

Step 1: 简化响应模型

class AuthUser {
  final String id;
  final String email;

  const AuthUser({required this.id, required this.email});

  factory AuthUser.fromJson(Map<String, dynamic> json) {
    return AuthUser(id: json['id'] as String, email: json['email'] as String);
  }
}

class AuthResponse {
  final String accessToken;
  final String refreshToken;
  final int expiresIn;
  final String tokenType;
  final AuthUser user;

  const AuthResponse({
    required this.accessToken,
    required this.refreshToken,
    required this.expiresIn,
    required this.tokenType,
    required this.user,
  });

  factory AuthResponse.fromJson(Map<String, dynamic> json) {
    return AuthResponse(
      accessToken: json['access_token'] as String,
      refreshToken: json['refresh_token'] as String,
      expiresIn: json['expires_in'] as int,
      tokenType: json['token_type'] as String,
      user: AuthUser.fromJson(json['user'] as Map<String, dynamic>),
    );
  }
}

class VerificationCreateResponse {
  final String email;

  const VerificationCreateResponse({required this.email});

  factory VerificationCreateResponse.fromJson(Map<String, dynamic> json) {
    return VerificationCreateResponse(email: json['email'] as String);
  }
}

删除 SignupStartResponseSignupResendResponse

Step 2: Commit

git add apps/lib/features/auth/data/models/auth_response.dart
git commit -m "refactor(auth): simplify response models"

Task 15: 更新 Auth Repository

Files:

  • Modify: apps/lib/features/auth/data/auth_repository.dart
  • Modify: apps/lib/features/auth/data/auth_repository_impl.dart

Step 1: 更新方法名和返回类型

将方法名和返回类型更新为新的 API 方法。

Step 2: Commit

git add apps/lib/features/auth/data/auth_repository.dart
git add apps/lib/features/auth/data/auth_repository_impl.dart
git commit -m "refactor(auth): update repository for new API"

Task 16: 更新 Register Cubit

Files:

  • Modify: apps/lib/features/auth/presentation/cubits/register_cubit.dart

Step 1: 更新 API 调用

  • signupStartcreateVerification
  • signupResendresendVerification(现在返回 void
  • 更新返回类型

Step 2: Commit

git add apps/lib/features/auth/presentation/cubits/register_cubit.dart
git commit -m "refactor(auth): update register cubit for new API"

Task 17: 更新 Auth Cubit

Files:

  • Modify: apps/lib/features/auth/presentation/cubits/auth_cubit.dart

Step 1: 更新 API 调用

  • logincreateSession
  • refreshrefreshSession
  • logoutdeleteSession

Step 2: Commit

git add apps/lib/features/auth/presentation/cubits/auth_cubit.dart
git commit -m "refactor(auth): update auth cubit for new API"

Task 18: 创建 Users API 和 Repository

Files:

  • Create: apps/lib/features/users/data/users_api.dart
  • Create: apps/lib/features/users/data/users_repository.dart
  • Create: apps/lib/features/users/data/users_repository_impl.dart
  • Create: apps/lib/features/users/data/models/user_response.dart

Step 1: 创建 users_api.dart

import 'package:social_app/core/api/api_client.dart';
import 'models/user_response.dart';

class UsersApi {
  final ApiClient _client;
  static const _prefix = '/api/v1/users';

  UsersApi(this._client);

  Future<UserResponse> getMe() async {
    final response = await _client.get('$_prefix/me');
    return UserResponse.fromJson(response.data);
  }

  Future<UserResponse> updateMe(UserUpdateRequest request) async {
    final response = await _client.patch(
      '$_prefix/me',
      data: request.toJson(),
    );
    return UserResponse.fromJson(response.data);
  }

  Future<UserResponse> getByUsername(String username) async {
    final response = await _client.get('$_prefix/$username');
    return UserResponse.fromJson(response.data);
  }
}

Step 2: 创建 user_response.dart

class UserResponse {
  final String id;
  final String username;
  final String? avatarUrl;
  final String? bio;

  const UserResponse({
    required this.id,
    required this.username,
    this.avatarUrl,
    this.bio,
  });

  factory UserResponse.fromJson(Map<String, dynamic> json) {
    return UserResponse(
      id: json['id'] as String,
      username: json['username'] as String,
      avatarUrl: json['avatar_url'] as String?,
      bio: json['bio'] as String?,
    );
  }
}

class UserUpdateRequest {
  final String? username;
  final String? avatarUrl;
  final String? bio;

  const UserUpdateRequest({this.username, this.avatarUrl, this.bio});

  Map<String, dynamic> toJson() {
    return {
      if (username != null) 'username': username,
      if (avatarUrl != null) 'avatar_url': avatarUrl,
      if (bio != null) 'bio': bio,
    };
  }
}

Step 3: Commit

git add apps/lib/features/users/
git commit -m "feat(users): create users API and models"

Task 19: 更新使用 Profile 的页面

Files:

  • Modify: 所有引用 profile API 的 cubit 和 screen

Step 1: 全局搜索 profile 引用

Run: cd apps && grep -r "profile" lib/ --include="*.dart"

Step 2: 更新为 users API

更新所有引用 profile 的代码改为使用新的 users API。

Step 3: Commit

git add apps/lib/
git commit -m "refactor: migrate profile to users API"

Task 20: 更新 ApiClient 添加 delete 和 patch 方法

Files:

  • Modify: apps/lib/core/api/api_client.dart

Step 1: 添加 delete 和 patch 方法

Future<Response<T>> delete<T>(
  String path, {
  dynamic data,
  Options? options,
}) async {
  try {
    return await _dio.delete<T>(path, data: data, options: options);
  } on DioException catch (e) {
    throw ApiException.fromDioError(e);
  }
}

Future<Response<T>> patch<T>(
  String path, {
  dynamic data,
  Options? options,
}) async {
  try {
    return await _dio.patch<T>(path, data: data, options: options);
  } on DioException catch (e) {
    throw ApiException.fromDioError(e);
  }
}

Step 2: Commit

git add apps/lib/core/api/api_client.dart
git commit -m "feat(api): add delete and patch methods"

Task 21: 更新前端测试

Files:

  • Modify: apps/test/features/auth/presentation/cubits/register_cubit_test.dart
  • 其他相关测试文件

Step 1: 更新测试用例

更新 mock 和断言以匹配新的 API 方法名和返回类型。

Step 2: 运行测试

Run: cd apps && flutter test Expected: All tests pass

Step 3: Commit

git add apps/test/
git commit -m "test: update tests for RESTful API"

Phase 5: 文档

Task 22: 创建路由文档

Files:

  • Create: docs/runtime/runtime-route.md

Step 1: 创建路由文档

# Runtime API Routes

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

## 格式说明

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

## Auth

### POST /auth/verifications

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

**Request:**
```json
{
  "username": "string (3-30 chars)",
  "email": "string (email)",
  "password": "string (min 6 chars)",
  "redirect_to": "string? (optional)"
}

Response: 202 Accepted

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

Errors:

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

POST /auth/verifications/resend

重发验证码。

Request:

{
  "email": "string (email)"
}

Response: 204 No Content

Errors:

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

POST /auth/verifications/verify

验证码校验。

Request:

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

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

登录(创建会话)。

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: 请求参数无效

DELETE /auth/sessions

登出(删除会话)。

Request:

{
  "refresh_token": "string"
}

Response: 204 No Content

Errors:

  • 422: 请求参数无效

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: 请求参数无效

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: 请求参数无效

GET /users/{username}

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

Path Parameters:

  • username: string (3-30 chars, alphanumeric and underscore)

Response: 200 OK

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

Errors:

  • 401: 未认证
  • 404: 用户不存在
  • 422: 请求参数无效

Agent Chat

POST /agent-chats

运行 Agent 对话(需要认证)。

Request:

{
  "message": "string (1-8000 chars)",
  "session_id": "string? (UUID)"
}

Response: 200 OK

{
  "session_id": "string (UUID)",
  "output": "string",
  "events": [
    {
      "type": "string",
      "run_id": "string?",
      "message_id": "string?",
      "delta": "string?",
      "tool_name": "string?",
      "result": "string?",
      "output": "string?",
      "error": "string?"
    }
  ]
}

Errors:

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

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/verifications/verify"
}

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


**Step 2: Commit**

```bash
git add docs/runtime/runtime-route.md
git commit -m "docs: add runtime route documentation"

Task 23: 更新根目录 AGENTS.md

Files:

  • Modify: AGENTS.md

Step 1: 添加路由同步规则

在文件末尾添加:

## API Route Documentation

When modifying HTTP routes (adding, updating, or removing endpoints):

- Sync changes to `docs/runtime/runtime-route.md`
- Include: HTTP method, path, request/response schema, status codes, error format
- Keep documentation in sync with actual implementation

Step 2: Commit

git add AGENTS.md
git commit -m "docs: add route sync rule to AGENTS.md"

Phase 6: 验收

Task 24: 端到端验证

Step 1: 启动后端服务

Run: docker compose --env-file .env -f infra/docker/docker-compose.yml up -d

Step 2: 运行后端测试

Run: cd backend && uv run pytest -v Expected: All tests pass

Step 3: 运行前端测试

Run: cd apps && flutter test Expected: All tests pass

Step 4: 手动验证注册/登录流程

使用 Flutter app 或 curl 测试:

  1. POST /api/v1/auth/verifications - 注册
  2. POST /api/v1/auth/verifications/verify - 验证
  3. POST /api/v1/auth/sessions - 登录
  4. GET /api/v1/users/me - 获取用户信息
  5. DELETE /api/v1/auth/sessions - 登出

Summary

Phase Tasks Description
Phase 1 1-3 后端 Schema 重构
Phase 2 4-9 后端路由重构
Phase 3 10-12 后端测试更新
Phase 4 13-21 前端适配
Phase 5 22-23 文档
Phase 6 24 验收