chore: sync current workspace to dev

This commit is contained in:
qzl
2026-02-24 18:18:42 +08:00
parent 105cf82d21
commit 08571cfc95
79 changed files with 1899 additions and 844 deletions
@@ -0,0 +1,70 @@
# Auth + ProfileSupabase 优先)设计
**Date:** 2026-02-24
**Status:** Approved
## 目标
在最大化复用 Supabase Auth 能力前提下,完成以下能力:
1. 用户注册:`username + email + password`
2. 用户登录:`email + password`
3. 用户按 email 查询(Auth 维度)
4. 用户资料更新(Profile 维度)
## 范围与原则
- 保留并复用现有 `/auth/signup``/auth/login``/profile/me` 主体能力
- 仅补齐差距,不重复造轮子
- 登录标识仅使用 email`username` 仅用于展示
- `profiles.username` 允许重复,不加唯一约束
- 当前为开发阶段,按“无历史数据”处理迁移
## 数据模型设计
`public.profiles` 字段调整:
- 保留:`id`, `username`, `avatar_url`, `bio`, `created_at`, `updated_at`, `deleted_at`
- 删除:`display_name`
- `id` 继续绑定 `auth.users.id`FK + ON DELETE CASCADE
## 认证与资料流转
### 注册
- 请求:`username + email + password`
- 后端调用 Supabase `auth.sign_up`,并将 `username` 写入 `raw_user_meta_data.username`
- 由数据库触发器在 `auth.users` 插入后自动创建 `public.profiles`
- `profiles.id = auth.users.id`
- `profiles.username = raw_user_meta_data.username`
### 登录
- 保持现有 `email + password` 登录,不改协议
### 按 email 查找用户
- 新增后端接口:`GET /auth/users/by-email?email=...`
- 通过 Supabase Admin 能力查询(后端 service_role
- 返回最小必要字段(例如 `id/email/created_at/email_confirmed_at`
### 资料更新
- `PATCH /profile/me` 继续使用
- 去除 `display_name` 更新项
- 允许更新:`username/avatar_url/bio`
## 安全与权限
- 按 email 查找接口必须后端受控,避免普通用户枚举
- 注册与更新时做输入校验(格式、长度)
- 认证继续依赖 Supabase 签发 token,后端负责业务鉴权
## 验收标准
1. 注册时提交 `username+email+password` 可成功创建 Auth 用户
2. 注册后自动创建 profile,且 `profiles.username` 等于注册时 `username`
3. 登录保持 `email+password` 可用
4. `PATCH /profile/me` 可更新 `username/avatar_url/bio`
5. `GET /auth/users/by-email` 返回正确用户或 404
6. `profiles` 表无 `display_name` 字段
@@ -1,108 +0,0 @@
# Plan: Base Service for Redis and Qdrant
**Date:** 2026-02-05
**Author:** AI Assistant
**Status:** Draft
## Overview
Create a reusable base service module under `backend/src/services/base` that standardizes Redis and Qdrant client creation, lifecycle management, and error handling. Align the design with the DIVA-backend equivalent (once provided) and integrate configuration through existing `SOCIAL_REDIS__*` and `SOCIAL_QDRANT__*` settings.
## Requirements
### Functional
- [ ] Provide a base service abstraction that exposes Redis and Qdrant clients to other services.
- [ ] Use async client implementations compatible with FastAPI async execution.
- [ ] Support connection lifecycle hooks (initialize, health check, close).
- [ ] Centralize error handling and translate connection failures to consistent HTTP errors.
- [ ] Mirror DIVA-backend base service features and naming conventions where applicable.
### Non-Functional
- [ ] Performance: reuse client instances; avoid per-request connection creation.
- [ ] Security: never log secrets (API keys/passwords); enforce TLS settings when enabled.
- [ ] Reliability: implement timeouts and retry policy where supported by client libraries.
## Technical Approach
Introduce a `services/base` package that provides a small, composable base class plus Redis/Qdrant client factories. Configuration will be sourced from `core/config/settings.py` using the existing `.env` keys. The base service will accept injected clients to keep testability high and avoid global state, while a module-level factory will handle creation and cleanup.
### Key Decisions
| Decision | Rationale |
|----------|-----------|
| Use async Redis and Qdrant clients | Matches FastAPI async usage and avoids blocking the event loop. |
| Constructor injection with factories | Keeps services testable and avoids hidden global state. |
| Centralized error mapping in base service | Ensures consistent HTTP 503 responses and logging. |
## Implementation Steps
### Phase 1: DIVA-backend Parity Review (1-2 hours)
1. Locate DIVA-backend base service module (path or repo) and document its responsibilities, public API, and lifecycle behavior.
2. Produce a parity checklist to map DIVA behaviors to this repo (naming, error types, retry policy, health checks).
### Phase 2: Configuration and Client Factories (3 hours)
1. Add `RedisSettings` and `QdrantSettings` sections to `backend/src/core/config/settings.py` using existing `SOCIAL_REDIS__*` and `SOCIAL_QDRANT__*` env keys.
2. Create `backend/src/services/base/redis_client.py` and `backend/src/services/base/qdrant_client.py` with async client factory functions and close helpers.
3. Add structured logging for client initialization, connection failures, and shutdown paths.
### Phase 3: Base Service Class (3 hours)
1. Create `backend/src/services/base/service.py` with a `BaseService` that accepts optional Redis/Qdrant clients (dependency injection).
2. Add helper methods (e.g., `require_redis()`, `require_qdrant()`) that raise HTTP 503 on unavailable clients.
3. Define error translation utilities for Redis/Qdrant exceptions with consistent messages and logging.
### Phase 4: Tests (TDD) and Minimal Integration (4 hours)
1. Unit tests for settings parsing and default values (RED/GREEN).
2. Unit tests for base service behavior: missing client errors, exception mapping, and logging context.
3. Integration tests using running Redis/Qdrant containers to verify client factories can connect and execute a simple command.
4. E2E test that exercises a minimal endpoint using the base service (e.g., `/health/infra`), or record an explicit exception if no API integration is allowed.
## Files to Modify
| File | Changes |
|------|---------|
| backend/src/core/config/settings.py | Add Redis/Qdrant settings models and defaults. |
| backend/src/app.py | (If needed) register startup/shutdown hooks for client lifecycle. |
| backend/src/v1/router.py | (If needed) add an infra health endpoint to support E2E. |
## Files to Create
| File | Purpose |
|------|---------|
| backend/src/services/base/__init__.py | Package export surface for base services. |
| backend/src/services/base/service.py | Base service class for Redis/Qdrant access. |
| backend/src/services/base/redis_client.py | Redis client factory and teardown helpers. |
| backend/src/services/base/qdrant_client.py | Qdrant client factory and teardown helpers. |
| backend/tests/unit/services/base/test_service.py | Unit tests for base service error handling. |
| backend/tests/unit/services/base/test_clients.py | Unit tests for client factory behavior. |
| backend/tests/integration/services/base/test_clients.py | Integration tests with Redis/Qdrant containers. |
| backend/tests/e2e/test_infra_health.py | E2E test for an endpoint using base service. |
## Dependencies
- [ ] `redis` (async client) for Redis connectivity.
- [ ] `qdrant-client` for Qdrant connectivity (async/GRPC as configured).
- [ ] No additional infra services required (Redis/Qdrant already in Docker compose).
## Testing Strategy
- **Unit Tests:** Base service behavior, missing client errors, exception translation, settings parsing.
- **Integration Tests:** Connect to Redis and Qdrant, run minimal ping/health operations.
- **E2E Tests:** Call a minimal endpoint that uses the base service to validate wiring and error handling.
## Risks & Mitigations
| Risk | Impact | Likelihood | Mitigation |
|------|--------|------------|------------|
| DIVA-backend module not available | Medium | High | Add a parity checklist and update plan once module location is provided. |
| Client library mismatch (sync vs async) | Medium | Medium | Select async-supported libraries and verify compatibility in unit tests. |
| Lack of API integration for E2E | High | Medium | Add a minimal infra health endpoint or record a documented exception. |
| Connection config mismatches | Medium | Medium | Validate settings with integration tests and mirror `.env.example`. |
## Estimated Effort
| Phase | Effort |
|-------|--------|
| Phase 1 | 2 hours |
| Phase 2 | 3 hours |
| Phase 3 | 3 hours |
| Phase 4 | 4 hours |
| **Total** | **12 hours** |
@@ -1,143 +0,0 @@
# Plan: Env Config Refactor
**Date:** 2026-02-05
**Author:** AI Assistant
**Status:** Draft
## Overview
`.env` / `.env.example``backend/src/core/config/settings.py` 做一次一致性重构,消除同一含义的重复配置来源(例如 `DATABASE_URL` 与分段口令、host/port 与完整 URL)。目标是明确一组规范化环境变量,确保后端仅通过 Settings 读取,并兼顾 `infra/docker/docker-compose.yml` 的现有依赖。
## Requirements
### Functional
- [ ] 提出“规范化环境变量”清单(canonical set),覆盖后端与 Supabase 本地栈的关键配置。
- [ ] 定义 Settings 的读取与推导策略(优先级、默认值、派生字段)。
- [ ] 给出 `.env` / `.env.example` 的迁移步骤与兼容策略。
- [ ] 兼容 `infra/docker/docker-compose.yml` 使用的变量(保证 compose 不被破坏)。
### Non-Functional
- [ ] Performance: Settings 解析不增加明显启动耗时
- [ ] Security: 不在仓库中暴露真实密钥;对后端使用的数据库 URL 与密钥来源保持单一可信源
## Technical Approach
以“后端设置单一来源 + docker-compose 继续使用 Supabase 变量”为原则:
- 后端只接受 `SOCIAL_DATABASE_URL` 与必要的 Supabase 访问变量(`public_url/anon_key/service_role_key/jwt_secret`)。
- Supabase stack 继续使用 `SOCIAL_SUPABASE__*` 变量,保持 compose 模板稳定。
- 通过 Settings 做派生字段(例如 `supabase.url`)与兼容性读入(可选旧字段,设置弃用期)。
### Key Decisions
| Decision | Rationale |
|----------|-----------|
| 保留 `SOCIAL_DATABASE_URL` 作为后端唯一数据库连接来源 | 避免与分段 `POSTGRES_*` 产生冲突,清晰配置入口 |
| Supabase stack 变量继续使用 `SOCIAL_SUPABASE__*` | docker-compose 已广泛引用,改动成本高 |
| Settings 允许短期兼容旧字段 | 保障迁移期间部署安全,减少切换风险 |
## Implementation Steps
### Phase 1: Inventory & Mapping (2 hours)
1. 盘点 `.env` / `.env.example``settings.py` 的变量差异,标注重复与冲突字段。
2. 输出 canonical env vars 列表与映射关系表(旧 -> 新)。
### Phase 2: Settings Refactor (3 hours)
1.`settings.py` 中实现新的读取优先级与派生字段。
2. 为旧字段加兼容读取与弃用注记(仅内存兼容,不继续写入)。
### Phase 3: Env Templates Update (2 hours)
1. 更新 `.env.example` 为 canonical 变量,并标注“后端使用/compose 使用”。
2. 更新 `.env`(本地开发用)以匹配新模板。
### Phase 4: Validation & Docs (2 hours)
1. 本地启动 docker-compose,验证 Supabase stack 与后端连接正常。
2. 写简要迁移说明(README 或 docs/ 中短节)。
## Files to Modify
| File | Changes |
|------|---------|
| `.env.example` | 替换为 canonical 变量,移除重复字段 |
| `.env` | 与模板对齐,移除重复字段 |
| `backend/src/core/config/settings.py` | 调整 Settings 读取与派生策略 |
| `infra/docker/docker-compose.yml` | 仅在必要时新增兼容映射变量 |
| `README.md``docs/*` | 增加迁移说明 |
## Files to Create
| File | Purpose |
|------|---------|
| `docs/plans/PLAN-env-config-refactor-2026-02-05.md` | 规划文档 |
## Dependencies
- [ ] 无新增第三方依赖
## Proposed Canonical Env Vars
### Backend (Settings)
- `SOCIAL_DATABASE_URL` (required) — 后端数据库连接(唯一来源)
- `SOCIAL_SUPABASE__PUBLIC_URL` — Supabase 公网/本地外部访问 URL
- `SOCIAL_SUPABASE__API_EXTERNAL_URL` — Supabase Auth 回调外部 URL
- `SOCIAL_SUPABASE__ANON_KEY` — 前端/匿名访问 key
- `SOCIAL_SUPABASE__SERVICE_ROLE_KEY` — 后端服务角色 key
- `SOCIAL_SUPABASE__JWT_SECRET` — JWT 验证密钥(后端验证用)
### Supabase Stack (docker-compose)
- `SOCIAL_SUPABASE__POSTGRES_HOST`
- `SOCIAL_SUPABASE__POSTGRES_PORT`
- `SOCIAL_SUPABASE__POSTGRES_DB`
- `SOCIAL_SUPABASE__POSTGRES_PASSWORD`
- `SOCIAL_SUPABASE__KONG_HTTP_PORT`
- `SOCIAL_SUPABASE__KONG_HTTPS_PORT`
- `SOCIAL_SUPABASE__SITE_URL`
- `SOCIAL_SUPABASE__JWT_SECRET`
- `SOCIAL_SUPABASE__ANON_KEY`
- `SOCIAL_SUPABASE__SERVICE_ROLE_KEY`
- 其余 `SOCIAL_SUPABASE__*` 保持现状(Logflare、SMTP、Pooler 等)
## Mapping Strategy
1. **Backend DB**
- Only: `SOCIAL_DATABASE_URL`
- Deprecated: `SOCIAL_SUPABASE__POSTGRES_*`(后端不再拼接)
2. **Supabase URL**
- Primary: `SOCIAL_SUPABASE__PUBLIC_URL`
- Fallback: `SOCIAL_SUPABASE__API_EXTERNAL_URL`
- Settings 中 `supabase.url` 由上述字段派生
3. **Docker Compose**
- 继续读 `SOCIAL_SUPABASE__POSTGRES_*`
- 不引入 `SOCIAL_DATABASE_URL` 到 compose,以免混淆职责
## Migration Steps
1.`settings.py` 中增加兼容逻辑:若 `SOCIAL_DATABASE_URL` 不存在,可临时从 `SOCIAL_SUPABASE__POSTGRES_*` 组装(同时记录弃用)。
2. 更新 `.env.example`:只保留 canonical 变量并标注用途。
3. 更新 `.env`:移除重复字段,确保本地后端使用 `SOCIAL_DATABASE_URL`
4. 校验 compose`infra/docker/docker-compose.yml` 不依赖被移除字段。
5. 发布说明:提示下游用户迁移并在下个版本移除兼容逻辑。
## Testing Strategy
- **Unit Tests:** Settings 派生字段与优先级规则
- **Integration Tests:** 后端连接 Supabase DB(使用 `SOCIAL_DATABASE_URL`
- **E2E Tests:** 关键登录/读写流程(确认 JWT 与 Service Role 配置无误)
## Risks & Mitigations
| Risk | Impact | Likelihood | Mitigation |
|------|--------|------------|------------|
| compose 变量被误删导致 Supabase 启动失败 | High | Medium | 迁移前后对照 `docker-compose.yml`,保留全部 `SOCIAL_SUPABASE__*` 依赖 |
| 后端数据库连接断开 | High | Medium | 兼容旧字段,先引入新变量再切换 |
| 开发环境 `.env` 未更新 | Medium | High | 更新模板并在 README 明确迁移步骤 |
## Estimated Effort
| Phase | Effort |
|-------|--------|
| Phase 1 | 2 hours |
| Phase 2 | 3 hours |
| Phase 3 | 2 hours |
| Phase 4 | 2 hours |
| **Total** | **9 hours** |
@@ -1,176 +0,0 @@
# Plan: FastAPI + Celery 日志管理器系统
**Date:** 2026-01-29
**Author:** AI Assistant
**Status:** Draft
## Overview
构建一个统一、可扩展的日志管理器系统,覆盖 FastAPI 与 Celery worker 的运行时日志,提供结构化 JSON 输出、错误分离、日志轮转与上下文追踪。目标是满足生产环境可观测性需求,便于检索、关联与故障排查,并与当前项目配置体系保持一致。
## Requirements
### Functional
- [ ] 统一管理 FastAPI 与 Celery worker 日志
- [ ] 日志持久化到 `logs/`,错误日志单独输出到 `logs/errors/`
- [ ] 支持按大小或按时间进行日志轮转
- [ ] 结构化日志(JSON),包含时间戳、级别、模块/函数、消息与上下文
- [ ] ERROR/CRITICAL 记录完整堆栈与错误上下文
- [ ] 支持环境差异化配置(dev/test/prod
### Non-Functional
- [ ] 性能:日志写入对请求延迟影响可控,支持异步队列化扩展
- [ ] 安全:避免记录敏感信息,支持字段脱敏
- [ ] 可维护性:模块化、可测试、与现有配置体系一致
## Technical Approach
### 调研摘要
- Python 官方建议使用 `logging` + `dictConfig` 管理多 handler、多 formatter 与过滤器,适用于生产环境配置化管理。
- FastAPI 通常通过中间件注入 request_id 和上下文,并使用结构化日志输出以便集中检索。
- Celery 官方文档建议在自定义场景下关闭 `worker_hijack_root_logger`,通过信号配置自定义 handler。
- 结构化日志库中,structlog 更贴近标准 logging,可与 `logging` 生态协同;loguru 简化配置但替换性强、与 Celery 深度集成时可控性较弱。
- 生产环境推荐 JSON 结构化日志 + 轮转 + 错误分离,并通过外部系统聚合与告警(如 Sentry)。
### 方案对比(至少两种)
| 方案 | 描述 | 优点 | 缺点 | 结论 |
|------|------|------|------|------|
| 方案 Astdlib logging + 自定义 JSON Formatter | 纯标准库实现 JSON formatter + handler/filters | 依赖最少,符合标准库,易与 Celery/FastAPI 集成 | 结构化上下文绑定与 request_id 传递需手写 | 可作为备选最小方案 |
| 方案 Bstdlib logging + structlog | 用 structlog 生成结构化事件,输出到 logging handler | 结构化上下文与 contextvars 支持好,兼容 logging handler | 引入第三方依赖与配置复杂度 | 推荐主方案 |
| 方案 Cloguru | 直接使用 loguru logger | 配置简单、体验好 | 与 Celery/标准 logging 生态整合成本高 | 不推荐作为主方案 |
### 选型结论
- 采用方案 B`logging` 作为底座,structlog 负责结构化事件与上下文绑定;保留可切换到方案 A 的最小实现路径。
- 通过 `dictConfig` 做环境配置,使用 Rotating/TimedRotating handler 支持按大小或时间轮转。
## Implementation Steps
### Phase 1: 基础日志骨架与配置 (3 hours)
1. 新增日志配置模型(Settings 扩展),支持环境、轮转方式与路径配置。
2. 创建日志模块骨架:formatter、handler、filter、context。
3. 集成 `dictConfig` 初始化入口,支持 dev/test/prod 配置切换。
### Phase 2: FastAPI 集成与上下文 (4 hours)
1. 实现请求中间件:生成 `request_id`,绑定用户与请求上下文(IP、路径、方法)。
2. 定义异常处理器:捕获未处理异常并记录堆栈与上下文。
3. 添加应用启动时日志初始化流程。
### Phase 3: Celery 集成 (3 hours)
1. 在 Celery 应用配置中设置 `worker_hijack_root_logger = False`
2. 使用 Celery 信号(`setup_logging``after_setup_task_logger`)初始化日志并注入 task 上下文。
3. 统一日志格式、error 处理与 request_id 关联(如 task_id)。
### Phase 4: 错误分离与轮转策略 (3 hours)
1. 添加 error handler:仅接受 ERROR/CRITICAL,输出到 `logs/errors/`
2. 实现轮转策略配置(按大小、按时间),并提供统一切换配置项。
3. 增加字段脱敏与敏感字段黑名单过滤器。
### Phase 5: 可选增强功能 (4 hours)
1. 日志查询与过滤接口(基础 API + 分页)。
2. 日志聚合统计(按级别/模块/时间窗口)。
3. Sentry 集成与异常告警。
## Files to Modify
| File | Changes |
|------|---------|
| backend/src/core/config/settings.py | 扩展日志相关配置模型 |
## Files to Create
| File | Purpose |
|------|---------|
| backend/src/core/logging/__init__.py | 模块导出与初始化入口 |
| backend/src/core/logging/config.py | dictConfig 构建与环境配置 |
| backend/src/core/logging/formatters.py | JSON formatter 与字段规范 |
| backend/src/core/logging/handlers.py | 文件、控制台、错误 handler |
| backend/src/core/logging/filters.py | 等级过滤、敏感字段脱敏 |
| backend/src/core/logging/context.py | contextvars 绑定与获取 |
| backend/src/core/logging/middleware.py | FastAPI 请求中间件 |
| backend/src/core/logging/celery.py | Celery 日志信号集成 |
| backend/src/core/logging/examples.py | 使用示例(可选) |
## Dependencies
- [ ] structlog: 结构化日志与 contextvars 支持
- [ ] python-json-logger(备选): 若需要纯 logging JSON formatter
- [ ] sentry-sdk(可选): 异常告警与追踪
## 配置示例
```toml
# .env 示例(通过 pydantic settings 读取)
SOCIAL_RUNTIME__LOG_LEVEL=INFO
SOCIAL_RUNTIME__LOG_JSON=true
SOCIAL_RUNTIME__LOG_ROTATION=TIME
SOCIAL_RUNTIME__LOG_ROTATION_WHEN=midnight
SOCIAL_RUNTIME__LOG_ROTATION_BACKUP_COUNT=14
SOCIAL_RUNTIME__LOG_DIR=logs
SOCIAL_RUNTIME__LOG_ERROR_DIR=logs/errors
```
## 使用示例代码
```python
from core.logging import configure_logging, get_logger
configure_logging()
logger = get_logger(__name__)
logger.info("user login", extra={"user_id": "u_123"})
```
## Testing Strategy
- **Unit Tests:** formatter 输出结构、filter 脱敏规则、context 绑定行为
- **Integration Tests:** FastAPI 中间件注入的 request_id 与错误分离写入
- **E2E Tests:** 关键流程触发错误,验证 error 日志输出与轮转
## Test Database 约定
### Supabase 组件能力范围
- Supabase 的 Auth/Storage/Realtime 等组件是独立服务,默认指向同一个主数据库。
- 单独创建一个“测试数据库”(Postgres database)并不会自动获得这些组件的能力,除非显式为这些服务配置新的数据库连接。
- 因此,“测试数据库”默认只具备纯 Postgres 能力;Supabase 组件能力仍然作用在主数据库上。
### 对测试的影响
- **只走直连数据库的测试**(如通过 SQLAlchemy/psycopg 直连)不会受影响。
- **依赖 Supabase 组件的测试**(例如通过 Auth/Storage/Realtime API)会仍然落到主数据库,可能导致:
- 测试数据污染主库
- 并发测试互相干扰
- 若需要 Supabase 组件也“指向测试数据库”,需要启动一套独立 Supabase 栈或重新配置各服务连接(通常不建议在同一栈内动态切换)。
### 环境变量与自动创建
- 建议为“测试数据库”提供独立环境变量(仅测试环境读取),例如:
- `SOCIAL_TEST_DATABASE__HOST`
- `SOCIAL_TEST_DATABASE__PORT`
- `SOCIAL_TEST_DATABASE__NAME`
- `SOCIAL_TEST_DATABASE__USER`
- `SOCIAL_TEST_DATABASE__PASSWORD`
- 若使用 Docker 启动 Postgres,建议在容器初始化阶段自动创建测试数据库(避免手动创建):
- 通过 `docker-entrypoint-initdb.d` 的 init SQL 脚本创建测试数据库与权限
- 保证容器重建后自动恢复测试数据库
- 若使用独立 Supabase 栈做测试,测试环境变量应指向该栈的数据库与服务端口。
## Risks & Mitigations
| Risk | Impact | Likelihood | Mitigation |
|------|--------|------------|------------|
| Celery 日志被自动劫持导致重复或丢失 | High | Medium | 设置 `worker_hijack_root_logger=False` 并通过信号统一配置 |
| 结构化字段不一致导致下游解析失败 | Medium | Medium | 统一 schema,增加单元测试与校验 |
| 误记录敏感信息 | High | Medium | 增加脱敏过滤器与字段黑名单 |
| 日志量过大影响性能 | Medium | Medium | 轮转 + 级别控制 + 可选异步队列化 |
## Estimated Effort
| Phase | Effort |
|-------|--------|
| Phase 1 | 3 hours |
| Phase 2 | 4 hours |
| Phase 3 | 3 hours |
| Phase 4 | 3 hours |
| Phase 5 | 4 hours |
| **Total** | **17 hours** |
@@ -1,113 +0,0 @@
# Plan: Merge Supabase Compose and Base Services
**Date:** 2026-02-05
**Author:** AI Assistant
**Status:** Draft
## Overview
Integrate Supabase Docker services into the project's `infra/docker/docker-compose.yml` and align all environment variables with the project's `.env` conventions. Add reusable BaseRepository and BaseService abstractions (soft-delete filtering and auth/user validation) and refactor profile/auth services to use them, with full TDD coverage.
## Requirements
### Functional
- [ ] Merge Supabase Docker Compose services into `infra/docker/docker-compose.yml` using project `.env` variable names.
- [ ] Update `.env.example` to include all required Supabase compose variables.
- [ ] Implement BaseRepository with standard soft-delete filtering (excludes `deleted_at` rows by default).
- [ ] Implement BaseService with shared auth/user validation helpers.
- [ ] Refactor profile repository/service and auth service to use BaseRepository/BaseService.
- [ ] Add unit, integration, and E2E tests following TDD.
### Non-Functional
- [ ] Performance: keep repository queries indexed and avoid extra round-trips.
- [ ] Security: validate user identity consistently; no secrets in repo; no bypass of auth checks.
- [ ] Compatibility: keep Supabase config compatible with existing `Settings` and `.env` prefixes.
## Technical Approach
Introduce small, reusable base classes in `backend/src/core` for repository and service concerns, then refactor profile and auth modules to leverage them. Merge the Supabase compose services from the official template into `infra/docker/docker-compose.yml`, mapping variables to `SOCIAL_SUPABASE__*` and related infra keys already used in `backend/src/core/config/settings.py`.
### Key Decisions
| Decision | Rationale |
|----------|-----------|
| BaseRepository provides a `base_select()` or `apply_soft_delete_filter()` | Avoid duplicated `deleted_at` filters and enforce consistent behavior. |
| BaseService handles user validation helpers | Keeps auth checks consistent across services and reduces duplicated error handling. |
| Compose variables aligned to `SOCIAL_*` prefixes | Matches existing settings resolution and simplifies local/dev parity. |
## Implementation Steps
### Phase 1: Compose Merge and Env Alignment (3 hours)
1. Identify the Supabase Docker Compose template to merge (official Supabase Docker template) and list required services and env vars.
2. Merge Supabase services into `infra/docker/docker-compose.yml`, keeping existing Redis/Qdrant services intact and aligning ports/volumes.
3. Map Supabase compose env variables to project `.env` names (e.g., `SOCIAL_SUPABASE__*`, `SOCIAL_INFRA__*` where needed).
4. Update `.env.example` with all required Supabase-related variables, keeping comments updated for local vs. cloud usage.
5. Add/adjust docker compose healthchecks or depends_on as needed for startup ordering.
### Phase 2: BaseRepository and BaseService (4 hours)
1. Add `backend/src/core/db/repository.py` (or `backend/src/core/repository/base.py`) with a BaseRepository that applies `SoftDeleteMixin` filters by default.
2. Add `backend/src/core/services/base.py` with BaseService helpers for current user validation (e.g., `require_user`, `require_user_id`).
3. Add unit tests for BaseRepository soft delete filtering and BaseService auth validation (TDD red/green).
### Phase 3: Refactor Profile/Auth (4 hours)
1. Refactor `backend/src/v1/profile/repository.py` to inherit from BaseRepository and remove duplicated `deleted_at` logic.
2. Refactor `backend/src/v1/profile/service.py` to inherit from BaseService and use shared validation helpers where applicable.
3. Refactor `backend/src/v1/auth/service.py` to adopt BaseService helpers for user validation (where applicable) and keep gateway contract unchanged.
4. Update unit tests for profile and auth services to reflect base class usage and ensure behavior unchanged.
### Phase 4: Integration/E2E Tests and Hardening (4 hours)
1. Add integration tests for repository soft delete behavior using SQLAlchemy session fixtures.
2. Add or update E2E tests for profile flow to ensure auth/user validation still enforced.
3. Run coverage check (80%+), fix gaps, and verify CI pre-commit tooling passes.
## Files to Modify
| File | Changes |
|------|---------|
| infra/docker/docker-compose.yml | Merge Supabase services; map env vars to `SOCIAL_*`. |
| .env.example | Add Supabase compose variables and update comments. |
| backend/src/v1/profile/repository.py | Inherit BaseRepository; simplify soft delete filtering. |
| backend/src/v1/profile/service.py | Inherit BaseService; use shared validation helpers. |
| backend/src/v1/auth/service.py | Use BaseService helpers where applicable. |
| backend/tests/unit/v1/profile/* | Update tests for BaseRepository/BaseService. |
| backend/tests/unit/v1/auth/* | Update tests for base service helpers (if needed). |
| backend/tests/integration/* | Add/adjust tests for soft delete filtering. |
| backend/tests/e2e/* | Update/extend critical auth/profile flow tests. |
## Files to Create
| File | Purpose |
|------|---------|
| backend/src/core/db/repository.py | BaseRepository with soft-delete filtering. |
| backend/src/core/services/base.py | BaseService with auth/user validation helpers. |
| backend/tests/unit/core/db/test_base_repository.py | Unit tests for soft delete filters. |
| backend/tests/unit/core/services/test_base_service.py | Unit tests for auth/user validation. |
## Dependencies
- [ ] Supabase official Docker Compose template (source of services/env vars).
- [ ] No new Python dependencies expected.
## Testing Strategy
- **Unit Tests:** BaseRepository soft-delete filter logic; BaseService user validation helpers; updated profile/auth service behavior.
- **Integration Tests:** SQLAlchemy queries exclude soft-deleted rows; profile endpoints still return expected responses.
- **E2E Tests:** Critical profile flow with authenticated user; verify unauthorized access remains blocked.
## Risks & Mitigations
| Risk | Impact | Likelihood | Mitigation |
|------|--------|------------|------------|
| Missing or outdated Supabase compose template | Medium | Medium | Pin to official template version and document source in plan. |
| Env var mismatches break local auth or DB connections | High | Medium | Add validation checklist and update `.env.example` with exact mappings. |
| BaseRepository changes alter query behavior | Medium | Medium | Add unit/integration tests and verify no regressions. |
| Auth validation refactor introduces regressions | High | Low | TDD with unit + E2E tests; keep behavior parity. |
## Estimated Effort
| Phase | Effort |
|-------|--------|
| Phase 1 | 3 hours |
| Phase 2 | 4 hours |
| Phase 3 | 4 hours |
| Phase 4 | 4 hours |
| **Total** | **15 hours** |
@@ -1,148 +0,0 @@
# 测试数据隔离方案(Supabase + Python 后端)
## 背景现状
- 后端在 `backend/src/core/config/settings.py` 使用 `SOCIAL_DATABASE__*` 生成 `database_url`
- 本地 Supabase 通过 `supabase-db` 容器提供 Postgres,宿主端口由 `SOCIAL_DATABASE__PORT` 控制(默认映射到容器 5432)。
- 注意:`supabase-pooler` 的 5432 仅用于连接池;测试与迁移应直连 `supabase-db` 的宿主端口。
- 单元数据库测试目前使用 SQLite 内存库(见 `tests/unit/database/*`),不影响开发库。
- 真实 Postgres 的集成/E2E 当前未统一隔离策略;当开始接入真实 DB 时,需要按本文方案隔离与清理。
## 目标
- 测试过程不污染开发数据。
- 测试可重复、可并行、可在本地与 CI 稳定运行。
- 变更成本可控,优先在现有架构上落地。
## 结论(适配本项目)
采用“事务回滚 + 独立测试数据库”的混合策略:
- 默认测试使用事务回滚,快速、零污染(适用于单连接/单事务场景)。
- 需要真实提交、并发或触发器行为的测试使用独立测试数据库。
## 方案设计
### A. 事务回滚(默认)
适用:单元测试、绝大多数集成测试(当这些测试连接真实 Postgres 时)。
核心思路:
- 每个测试在事务中运行。
- 测试结束自动回滚。
优点:
- 速度快,无需新增数据库。
- 测试间完全隔离。
限制:
- 无法验证真实 COMMIT 结果。
- 并发、多连接事务隔离测试不准确。
### B. 独立测试数据库(E2E/并发)
适用:E2E、并发、触发器、LISTEN/NOTIFY 等需要真实提交的场景。
核心思路:
- 在现有 Supabase Postgres 实例中创建独立数据库。
- 测试使用专用 `SOCIAL_DATABASE__NAME` 连接,端口/账号/密码来自 `SOCIAL_DATABASE__*`
- 测试前应用迁移,测试后清理。
优点:
- 行为最接近真实环境。
- 与开发数据完全隔离。
成本:
- 需要迁移与清理策略。
## 与现有测试模块的衔接
- `tests/unit/database/*` 已使用 SQLite 内存库,无需改造。
- 未来若 `tests/integration/*``tests/e2e/*` 连接真实 Postgres,应切换到本文的测试库策略。
- 使用 `SOCIAL_DATABASE__NAME=postgres_test` 启动测试,以避免污染开发库。
## 实施步骤(与项目当前结构对齐)
### 1) 创建独立测试数据库
在本地 Supabase 容器中创建测试库:
```bash
docker exec -e PGPASSWORD="$SOCIAL_DATABASE__PASSWORD" supabase-db \
psql -U "$SOCIAL_DATABASE__USER" -c "CREATE DATABASE postgres_test;"
```
说明:
- 容器名为 `supabase-db`(已在 `infra/docker` 运行)。
- 数据库名建议 `postgres_test`,与 `.env``SOCIAL_DATABASE__NAME=postgres` 区分。
### 2) 运行迁移到测试库
使用测试环境变量指向测试库后,应用 Alembic 迁移:
```bash
SOCIAL_RUNTIME__ENVIRONMENT=test \
SOCIAL_DATABASE__NAME=postgres_test \
uv run alembic upgrade head
```
说明:
- 执行位置:`/home/qzl/Code/social-app/backend`
- 仍使用当前 `.env` 中的 `SOCIAL_DATABASE__HOST``SOCIAL_DATABASE__PORT`
### 3) 事务回滚测试(默认)
测试执行时注入事务回滚机制:
- 在测试会话层创建单连接事务。
- 对每个测试用例使用 SAVEPOINT(或嵌套事务)。
- 测试结束回滚到 SAVEPOINT。
这套策略可保持速度与隔离性,同时不需要额外数据库。
### 4) 独立测试数据库执行(E2E/并发)
对于需要真实提交的测试,使用测试库运行:
```bash
SOCIAL_RUNTIME__ENVIRONMENT=test \
SOCIAL_DATABASE__NAME=postgres_test \
uv run pytest tests/e2e
```
清理策略(二选一):
- 小规模测试:TRUNCATE public schema 的业务表(不影响 `auth` 等系统 schema)。
- 大规模测试:`DROP DATABASE postgres_test;` 后重建并迁移。
### 5) 本地/CI 统一策略
- 本地默认:事务回滚。
- CI:独立测试库(保证完全隔离、无隐式依赖)。
## 风险与规避
- 不要在清理时操作 `auth``storage` 等 Supabase 系统 schema。
- E2E 使用独立数据库,避免与开发数据交叉。
- 迁移必须由 Alembic 统一维护,禁止手动改库。
## 落地检查清单
- [ ] 已创建 `postgres_test` 数据库。
- [ ] 测试库迁移已应用。
- [ ] 事务回滚测试已接入(默认路径)。
- [ ] E2E 使用测试库运行。
- [ ] 清理策略执行脚本可复用。
## 备注
本方案基于当前项目的 Supabase 本地 Docker 结构与后端配置方式(`SOCIAL_DATABASE__*`)。
无需变更 Supabase 组件,优先在测试层完成隔离与清理。
+43 -16
View File
@@ -8,21 +8,30 @@
### 一键启动 (推荐)
```bash
# 使用一键启动脚本
./infra/scripts/start.sh
# 前提:基础设施已手动启动(redis + supabase
# docker compose --env-file .env -f infra/docker/docker-compose.yml up -d
# 一键执行 bootstrap + 拉起 web/workertmux
bash infra/scripts/dev-app-up.sh
# 查看窗口
tmux list-windows -t social-dev
# 进入会话观察日志
tmux attach -t social-dev
```
或者手动执行:
```bash
# 1. 启动基础设施
docker compose --env-file .env -f infra/docker/docker-compose.yml up -d redis db
# 1. 启动基础设施(当前编排不包含 web/worker
docker compose --env-file .env -f infra/docker/docker-compose.yml up -d
# 2. 运行迁移和初始化
docker compose --env-file .env -f infra/docker/docker-compose.yml run --rm init-job
docker compose --env-file .env -f infra/docker/docker-compose.yml --profile job run --rm init-job
# 3. 启动 Web 和 Worker
docker compose --env-file .env -f infra/docker/docker-compose.yml up -d web worker-critical worker-default worker-bulk
# 3. 一键执行应用层启动(bootstrap + web + workers
bash infra/scripts/dev-app-up.sh
```
### 本地 CLI (开发调试)
@@ -42,15 +51,25 @@ PYTHONPATH=backend/src uv run celery -A core.celery.app worker --loglevel=info -
PYTHONPATH=backend/src uv run celery -A core.celery.app worker --loglevel=info --queues=bulk --concurrency=1
```
### tmux 会话管理
```bash
# 进入会话
tmux attach -t social-dev
# 杀掉会话(停止 web/workers
tmux kill-session -t social-dev
```
## 服务说明
| 服务 | 说明 | 队列 |
| 服务 | 说明 | 备注 |
|------|------|------|
| web | Web 服务 (gunicorn) | - |
| worker-critical | 关键任务 worker | critical |
| worker-default | 默认任务 worker | default |
| worker-bulk | 批量任务 worker | bulk |
| init-job | 数据库迁移和初始化 | - |
| redis | 缓存与 Celery broker | docker-compose 编排 |
| supabase-* | 认证与数据库相关服务 | docker-compose 编排 |
| init-job | 数据库迁移和初始化 | docker-compose 按需 run |
| web | Web 服务 (gunicorn) | 本地 CLI 启动 |
| worker-* | Celery worker | 本地 CLI 启动 |
## 配置说明
@@ -79,15 +98,21 @@ PYTHONPATH=backend/src uv run celery -A core.celery.app worker --loglevel=info -
## 健康检查
```bash
curl -fsS http://127.0.0.1:8000/health
# Supabase 网关
curl -fsS http://127.0.0.1:${SOCIAL_SUPABASE__KONG_HTTP_PORT:-8000}/health
# 数据库迁移与初始化
docker compose --env-file .env -f infra/docker/docker-compose.yml --profile job run --rm init-job
```
## 查看服务状态
```bash
docker compose --env-file .env -f infra/docker/docker-compose.yml ps
docker compose --env-file .env -f infra/docker/docker-compose.yml logs -f web
docker compose --env-file .env -f infra/docker/docker-compose.yml logs -f worker-critical
docker compose --env-file .env -f infra/docker/docker-compose.yml logs -f db
# init-job 为一次性任务(run --rm),如需查看日志请重跑:
docker compose --env-file .env -f infra/docker/docker-compose.yml --profile job run --rm init-job
```
---
@@ -98,3 +123,5 @@ docker compose --env-file .env -f infra/docker/docker-compose.yml logs -f worker
|------|------|
| 2026-02-24 | 创建运行时手册,删除 legacy 脚本,统一使用 gunicorn |
| 2026-02-24 | 清理配置:合并 AppSettings 到 WebSettings,删除 Worker 旧配置 (enabled_queues/queues),统一使用 SOCIAL_WEB__GUNICORN__* 命名 |
| 2026-02-24 | 开发阶段 compose 暂不编排 web/worker,仅保留 redis/supabase 与 init-job |
| 2026-02-24 | 新增 dev-app-up 脚本:手动基础设施后,一键 bootstrap + tmux 拉起 web/worker |