"""密码重置令牌创建与校验。""" from __future__ import annotations import secrets from datetime import datetime, timedelta from sqlalchemy import delete, select from sqlalchemy.ext.asyncio import AsyncSession from models.models import PasswordResetToken, User def _token_expires_at(hours: int) -> datetime: return datetime.utcnow() + timedelta(hours=max(1, min(168, int(hours or 24)))) async def invalidate_user_reset_tokens(db: AsyncSession, user_id: int) -> None: await db.execute( delete(PasswordResetToken).where(PasswordResetToken.user_id == user_id) ) async def create_password_reset_token( db: AsyncSession, user: User, expire_hours: int = 24, ) -> str: await invalidate_user_reset_tokens(db, user.id) token = secrets.token_urlsafe(32) db.add( PasswordResetToken( user_id=user.id, token=token, expires_at=_token_expires_at(expire_hours), ) ) await db.flush() return token async def verify_password_reset_token(db: AsyncSession, token: str) -> User | None: value = (token or "").strip() if not value: return None result = await db.execute( select(PasswordResetToken, User) .join(User, User.id == PasswordResetToken.user_id) .where(PasswordResetToken.token == value) ) row = result.first() if not row: return None record, user = row if record.expires_at < datetime.utcnow(): await db.delete(record) await db.flush() return None await db.delete(record) await db.flush() return user