Align the project baseline with the latest admin interface styling and layout structure while documenting setup and usage updates in README.
44 lines
1.6 KiB
Python
44 lines
1.6 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import UTC, datetime, timedelta
|
|
|
|
from sqlalchemy import delete, or_
|
|
|
|
from app.core.database import session_scope
|
|
from app.modules.auth.models import EmailVerificationToken, PasswordResetToken, RefreshToken
|
|
|
|
|
|
def cleanup_expired_tokens(retention_days: int = 30) -> dict[str, int]:
|
|
now = datetime.now(UTC)
|
|
revoked_cutoff = now - timedelta(days=retention_days)
|
|
with session_scope() as db:
|
|
refresh_deleted = db.execute(
|
|
delete(RefreshToken).where(
|
|
or_(
|
|
RefreshToken.expires_at < now,
|
|
RefreshToken.revoked_at.is_not(None) & (RefreshToken.revoked_at < revoked_cutoff),
|
|
)
|
|
)
|
|
).rowcount or 0
|
|
reset_deleted = db.execute(
|
|
delete(PasswordResetToken).where(
|
|
or_(
|
|
PasswordResetToken.expires_at < now,
|
|
PasswordResetToken.used_at.is_not(None) & (PasswordResetToken.used_at < now),
|
|
)
|
|
)
|
|
).rowcount or 0
|
|
verify_deleted = db.execute(
|
|
delete(EmailVerificationToken).where(
|
|
or_(
|
|
EmailVerificationToken.expires_at < now,
|
|
EmailVerificationToken.used_at.is_not(None) & (EmailVerificationToken.used_at < now),
|
|
)
|
|
)
|
|
).rowcount or 0
|
|
return {
|
|
"refresh_tokens_deleted": int(refresh_deleted),
|
|
"password_reset_tokens_deleted": int(reset_deleted),
|
|
"email_verification_tokens_deleted": int(verify_deleted),
|
|
}
|