Arag / app /tasks /expiry_check_task.py
AuthorBot
Fix: datetime naive/aware crash across embed-token, access-repo, superadmin, expiry-task + friendly no-subscription UI
0cadd33
Raw
History Blame Contribute Delete
2.18 kB
"""Author RAG Chatbot SaaS — Subscription Expiry Check Task.
Runs daily at 8am UTC. Sends warning emails at:
- 7 days before expiry
- 1 day before expiry
"""
import structlog
from app.tasks.celery_app import celery_app
logger = structlog.get_logger(__name__)
@celery_app.task(name="app.tasks.expiry_check_task.check_expiring_subscriptions")
def check_expiring_subscriptions():
"""Check all active subscriptions for upcoming expiry and send alerts."""
import asyncio
asyncio.run(_run())
async def _run():
from datetime import datetime, timedelta, timezone
from sqlalchemy import select
from app.dependencies import _get_session_factory
from app.models.client_access import ClientAccess
from app.models.user import User
from app.services.email_service import EmailService
email = EmailService()
now = datetime.now(timezone.utc).replace(tzinfo=None) # SQLite stores naive datetimes
warning_days = [7, 1]
async with _get_session_factory()() as db:
result = await db.execute(
select(ClientAccess, User)
.join(User, User.id == ClientAccess.author_id)
.where(
ClientAccess.is_revoked == False,
ClientAccess.expires_at > now,
User.notify_subscription_expiry == True,
)
)
records = result.all()
sent = 0
for access, user in records:
expires = access.expires_at.replace(tzinfo=None) if access.expires_at.tzinfo else access.expires_at
days_left = (expires - now).days
if days_left in warning_days:
try:
email.send_subscription_expiry_warning(
to=user.email,
name=user.full_name or user.email,
days_remaining=days_left,
expires_at=access.expires_at.strftime("%B %d, %Y"),
)
sent += 1
except Exception as e:
logger.warning("Expiry warning email failed", user_id=user.id, error=str(e))
logger.info("Expiry check complete", warnings_sent=sent)