Spaces:
Running
Running
AuthorBot
Fix: datetime naive/aware crash across embed-token, access-repo, superadmin, expiry-task + friendly no-subscription UI
0cadd33 | """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__) | |
| 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) | |