# 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 重命名并简化：

```python
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**

```bash
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 文件**

```python
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**

```bash
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.py` → `backend/src/v1/users/repository.py`
- Move: `backend/src/v1/profile/service.py` → `backend/src/v1/users/service.py`

**Step 1: 创建 __init__.py**

```python
from __future__ import annotations
```

**Step 2: 创建 dependencies.py**

```python
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.py` 到 `v1/users/repository.py`，无需修改内容。

**Step 4: 移动并更新 service.py**

复制 `v1/profile/service.py` 到 `v1/users/service.py`，更新导入：

```python
from v1.users.schemas import UserResponse, UserUpdateRequest
```

**Step 5: 验证类型检查**

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

**Step 4: Commit**

```bash
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: 更新路由定义**

```python
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**

```bash
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：

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

将方法重命名：
- `signup_start` → `create_verification`
- `signup_resend` → `resend_verification`
- `signup_verify` → `verify_verification`
- `login` → `create_session`
- `refresh` → `refresh_session`
- `logout` → `delete_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**

```bash
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**

```bash
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**

```python
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**

```bash
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` 改为根路径：

```python
@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**

```bash
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: 更新路由注册**

```python
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**

```bash
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**

```bash
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.py` → `backend/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**

```bash
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 路径**

```dart
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**

```bash
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: 简化响应模型**

```dart
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);
  }
}
```

删除 `SignupStartResponse` 和 `SignupResendResponse`。

**Step 2: Commit**

```bash
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**

```bash
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 调用**

- `signupStart` → `createVerification`
- `signupResend` → `resendVerification`（现在返回 void）
- 更新返回类型

**Step 2: Commit**

```bash
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 调用**

- `login` → `createSession`
- `refresh` → `refreshSession`
- `logout` → `deleteSession`

**Step 2: Commit**

```bash
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**

```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**

```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**

```bash
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**

```bash
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 方法**

```dart
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**

```bash
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**

```bash
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: 创建路由文档**

```markdown
# 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
```json
{
  "email": "user@example.com"
}
```

**Errors:**
- 422: 请求参数无效
- 429: 请求过于频繁

---

### POST /auth/verifications/resend

重发验证码。

**Request:**
```json
{
  "email": "string (email)"
}
```

**Response:** 204 No Content

**Errors:**
- 422: 请求参数无效
- 429: 请求过于频繁

---

### POST /auth/verifications/verify

验证码校验。

**Request:**
```json
{
  "email": "string (email)",
  "token": "string (6 digits)"
}
```

**Response:** 200 OK
```json
{
  "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:**
```json
{
  "email": "string (email)",
  "password": "string (min 6 chars)"
}
```

**Response:** 200 OK
```json
{
  "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:**
```json
{
  "refresh_token": "string"
}
```

**Response:** 200 OK
```json
{
  "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:**
```json
{
  "refresh_token": "string"
}
```

**Response:** 204 No Content

**Errors:**
- 422: 请求参数无效

---

### GET /auth/users

按邮箱查询用户（需要认证）。

**Query Parameters:**
- `email`: string (required)

**Response:** 200 OK
```json
{
  "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
```json
{
  "id": "string",
  "username": "string",
  "avatar_url": "string?",
  "bio": "string?"
}
```

**Errors:**
- 401: 未认证

---

### PATCH /users/me

更新当前用户信息（需要认证）。

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

**Response:** 200 OK
```json
{
  "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
```json
{
  "id": "string",
  "username": "string",
  "avatar_url": "string?",
  "bio": "string?"
}
```

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

---

## Agent Chat

### POST /agent-chats

运行 Agent 对话（需要认证）。

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

**Response:** 200 OK
```json
{
  "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
```json
{
  "status": "healthy" | "unhealthy",
  "services": {
    "redis": {
      "status": "healthy" | "unhealthy",
      "latency_ms": 0
    }
  }
}
```

---

### GET /health

检查服务健康状态。

**Response:** 200 OK
```json
{
  "status": "ok"
}
```

---

## Error Response Format (RFC 7807)

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

```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: 添加路由同步规则**

在文件末尾添加：

```markdown
## 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**

```bash
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 | 验收 |
