Memberships
This commit is contained in:
+1
@@ -0,0 +1 @@
|
||||
"""Management command package."""
|
||||
@@ -0,0 +1,53 @@
|
||||
import logging
|
||||
import time
|
||||
from datetime import timedelta
|
||||
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.contrib.sessions.models import Session
|
||||
from django.utils import timezone
|
||||
|
||||
from members.billing import sync_account
|
||||
from members.models import Account, RateWindow, Registration, WebhookEvent
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Reconcile Stripe subscriptions and LDAP groups, including offline expiry."
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("--loop", action="store_true")
|
||||
parser.add_argument("--interval", type=int, default=60)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
if options["interval"] < 10:
|
||||
raise CommandError("Use an interval of at least 10 seconds.")
|
||||
while True:
|
||||
failed = 0
|
||||
# Expire EVERY account before any potentially slow Stripe history
|
||||
# reads. This pass requires only the local DB and LDAP.
|
||||
for account_id in Account.objects.values_list("pk", flat=True).iterator():
|
||||
try:
|
||||
sync_account(account_id, refresh_stripe=False)
|
||||
except Exception as exc:
|
||||
failed += 1
|
||||
logger.error("Local expiry sync failed account=%s kind=%s", account_id, type(exc).__name__)
|
||||
# A complete sweep is intentional: webhook loss must not prevent
|
||||
# renewal recovery or revocation. Each account has its own lock.
|
||||
for account_id in Account.objects.values_list("pk", flat=True).iterator():
|
||||
try:
|
||||
if not sync_account(account_id):
|
||||
failed += 1
|
||||
except Exception as exc:
|
||||
failed += 1
|
||||
logger.error("Membership sync failed account=%s kind=%s", account_id, type(exc).__name__)
|
||||
now = timezone.now()
|
||||
Session.objects.filter(expire_date__lte=now).delete()
|
||||
Registration.objects.filter(expires_at__lte=now).delete()
|
||||
RateWindow.objects.filter(expires_at__lte=now).delete()
|
||||
WebhookEvent.objects.filter(processed_at__lt=now - timedelta(days=30)).delete()
|
||||
if not options["loop"]:
|
||||
if failed:
|
||||
raise CommandError(f"{failed} account(s) failed to synchronize; inspect worker logs.")
|
||||
return
|
||||
time.sleep(options["interval"])
|
||||
Reference in New Issue
Block a user