✨ feat: 阶段 0 收尾——认证边界、请求上下文与垂直切片(WP-01 第 5-7 步)
- 认证:Web Session(__Host- Cookie、HttpOnly/Secure/SameSite=Lax、CSPRNG opaque token 数据库只存哈希)、CSRF 双提交、Origin 校验、dev 登录/登出端点;未认证 401、缺 CSRF 403
- 中间件:Hunjing-Request-ID 回显/生成(request_ UUIDv4)+ W3C traceparent 校验透传;JSON 结构化日志与敏感头脱敏
- 迁移 2:conversation/branch/message/run/idempotency_record 五表(含检查约束与复合索引);Session 补 token/csrf 哈希列
- 垂直切片:POST /v1/conversations/{id}/messages 按 Idempotency-Key 幂等(同键同体 200 重放、异体 409),同事务原子写 Message+Run+Audit+Outbox+幂等记录;ContentBlock text/markdown 判别联合(未知类型 422)
- SSE:GET /v1/runs/{id}/events 返回快照与终止状态事件 envelope
- 测试 26 项全绿:IDOR 跨域 404 fail-closed、失败注入回滚无孤儿(Run 崩溃后 message/run/audit/outbox/幂等全零)、SSE 事件序、auth 边界;ruff/mypy strict 干净
- 修复真实问题:asyncpg 跨事件循环(anyio/pytest-asyncio 双 loop、模块级 engine 改为 app 实例持有)、httpx 拒存 http 下 Secure Cookie
This commit is contained in:
1 parent
28127949db
commit
ba502297ac
16 files changed
+1165
No files matched your search
@@ -0,0 +1,37 @@
|
||||
"""请求级数据库会话依赖与引擎生命周期。
|
||||
|
||||
引擎由应用实例持有(create_app 时创建):一个应用一个引擎与连接池,
|
||||
避免模块级引擎跨事件循环复用连接。
|
||||
"""
|
||||
|
||||
from collections.abc import AsyncIterator
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, FastAPI, Request
|
||||
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker
|
||||
|
||||
from app.db.base import create_engine, create_session_factory
|
||||
|
||||
|
||||
def init_engine(app: FastAPI) -> async_sessionmaker[AsyncSession]:
|
||||
"""在 app.state 上初始化引擎与会话工厂(应用启动时调用一次)。"""
|
||||
engine: AsyncEngine = create_engine()
|
||||
app.state.engine = engine
|
||||
factory = create_session_factory(engine)
|
||||
app.state.session_factory = factory
|
||||
return factory
|
||||
|
||||
|
||||
async def get_db(request: Request) -> AsyncIterator[AsyncSession]:
|
||||
"""每请求一个会话:成功提交、异常回滚。"""
|
||||
factory: async_sessionmaker[AsyncSession] = request.app.state.session_factory
|
||||
async with factory() as session:
|
||||
try:
|
||||
yield session
|
||||
await session.commit()
|
||||
except Exception:
|
||||
await session.rollback()
|
||||
raise
|
||||
|
||||
|
||||
DbSession = Annotated[AsyncSession, Depends(get_db)]
|
||||
Reference in new issue
Block a user