Files
aiagent/backend/app/api/push.py

81 lines
2.5 KiB
Python
Raw Normal View History

"""浏览器推送订阅 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()