refactor: 删除未使用的 api_external_url 配置并完善 runtime 文档
- 删除 SupabaseSettings 中未使用的 api_external_url computed field - 更新测试文件移除相关测试用例 - backend/AGENTS.md 新增软删除设计规则 - runtime-database.md 更新表结构(删除 user_agents,表名更新为 agent_chat_sessions/messages,system_agents) - runtime-frontend.md 补充路由结构和功能模块说明 - 根 AGENTS.md 清理过时技能路径引用
This commit is contained in:
@@ -164,6 +164,26 @@ Use `schemas / repository / service` pattern:
|
||||
- service_role key is backend-only; never expose credentials
|
||||
- Prohibit calling Supabase Admin API (service_role key) from repository/service layers
|
||||
|
||||
### Soft Delete
|
||||
|
||||
**Soft delete marks data as invisible, not cascade delete.**
|
||||
|
||||
- Use `deleted_at: datetime | None` column (via `SoftDeleteMixin`)
|
||||
- **Query filtering**: Repository `_apply_soft_delete_filter()` auto-excludes deleted records
|
||||
- **No automatic cascade**: Related data stays intact; visibility controlled by JOIN filtering
|
||||
- **Cascade only for strong dependencies**: When parent deletion must invalidate children, implement in Service layer explicitly
|
||||
- **Recovery**: Only restore the record itself; related data visibility restored automatically via queries
|
||||
- **Unique constraints**: Use partial indexes excluding `deleted_at IS NOT NULL` to allow re-creation
|
||||
|
||||
```python
|
||||
# Partial unique index in migration
|
||||
op.execute("""
|
||||
CREATE UNIQUE INDEX ux_user_email
|
||||
ON users(email)
|
||||
WHERE deleted_at IS NULL
|
||||
""")
|
||||
```
|
||||
|
||||
### Migrations
|
||||
|
||||
- **Alembic is the single source of truth** for schema migrations
|
||||
|
||||
@@ -128,11 +128,6 @@ class SupabaseSettings(BaseModel):
|
||||
def public_url(self) -> str:
|
||||
return f"{self.public_scheme}://{self.public_host}:{self.kong_http_port}"
|
||||
|
||||
@computed_field
|
||||
@property
|
||||
def api_external_url(self) -> str:
|
||||
return self.public_url
|
||||
|
||||
@computed_field
|
||||
@property
|
||||
def url(self) -> str:
|
||||
|
||||
@@ -23,27 +23,13 @@ def test_social_prefixed_supabase_env_populates_settings(
|
||||
settings = Settings()
|
||||
|
||||
assert settings.supabase.public_url == "https://public.example:8443"
|
||||
assert settings.supabase.api_external_url == "https://public.example:8443"
|
||||
assert settings.supabase.anon_key == "anon-key"
|
||||
assert settings.supabase.service_role_key == "service-key"
|
||||
assert settings.supabase.jwt_secret == "jwt-secret"
|
||||
|
||||
supabase_settings = settings.model_dump()["supabase"]
|
||||
assert supabase_settings["public_url"] == "https://public.example:8443"
|
||||
assert supabase_settings["api_external_url"] == "https://public.example:8443"
|
||||
assert supabase_settings["anon_key"] == "anon-key"
|
||||
assert supabase_settings["service_role_key"] == "service-key"
|
||||
assert supabase_settings["jwt_secret"] == "jwt-secret"
|
||||
assert settings.database_url == "postgresql+asyncpg://user:pass@db:5432/app"
|
||||
|
||||
|
||||
def test_social_prefixed_api_external_url_is_loaded(
|
||||
monkeypatch: MonkeyPatch,
|
||||
) -> None:
|
||||
monkeypatch.setenv("SOCIAL_SUPABASE__PUBLIC_SCHEME", "https")
|
||||
monkeypatch.setenv("SOCIAL_SUPABASE__PUBLIC_HOST", "api.example")
|
||||
monkeypatch.setenv("SOCIAL_SUPABASE__KONG_HTTP_PORT", "8443")
|
||||
|
||||
settings = Settings()
|
||||
|
||||
assert settings.supabase.api_external_url == "https://api.example:8443"
|
||||
|
||||
Reference in New Issue
Block a user