"""浏览器推送订阅 API""" from __future__ import annotations import logging from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel, Field from app.api.auth import get_current_user, get_optional_user from app.models.user import User from app.core.database import SessionLocal from app.models.push_subscription import PushSubscription logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/v1/push", tags=["push"]) class PushSubscriptionRequest(BaseModel): endpoint: str = Field(..., description="Push 订阅端点 URL") keys: dict = Field(..., description="包含 p256dh 和 auth") user_agent: str = Field(default="", description="设备 UA") @router.post("/subscribe") async def subscribe_push( req: PushSubscriptionRequest, current_user: User = Depends(get_optional_user), ): """保存浏览器推送订阅。用户可选登录。""" db = SessionLocal() try: # 检查是否已存在相同 endpoint existing = db.query(PushSubscription).filter( PushSubscription.endpoint == req.endpoint ).first() if existing: # 更新已有记录 existing.p256dh = req.keys.get("p256dh", "") existing.auth = req.keys.get("auth", "") existing.user_id = str(current_user.id) if current_user else existing.user_id existing.user_agent = req.user_agent db.commit() return {"status": "updated", "subscription_id": str(existing.id)} sub = PushSubscription( user_id=str(current_user.id) if current_user else None, endpoint=req.endpoint, p256dh=req.keys.get("p256dh", ""), auth=req.keys.get("auth", ""), user_agent=req.user_agent, ) db.add(sub) db.commit() db.refresh(sub) return {"status": "created", "subscription_id": str(sub.id)} finally: db.close() @router.delete("/unsubscribe") async def unsubscribe_push( endpoint: str, current_user: User = Depends(get_optional_user), ): """取消推送订阅。""" db = SessionLocal() try: q = db.query(PushSubscription).filter(PushSubscription.endpoint == endpoint) if current_user: q = q.filter(PushSubscription.user_id == str(current_user.id)) sub = q.first() if sub: db.delete(sub) db.commit() return {"status": "deleted"} return {"status": "not_found"} finally: db.close()