- Add consent_given_at and anonymized_at fields + Alembic migration (002) - Add GET /waitlist/unsubscribe endpoint (HMAC token, anonymizes PII) - Add cleanup.py: cron-able script to anonymize unconfirmed entries after 48h - Clear IP address on email confirmation (no longer needed) - Add unsubscribe link in confirmation email footer - Record consent timestamp on signup - Add 4 new tests (unsubscribe, consent timestamp) - Update .env.example, schemas
59 lines
1.5 KiB
Python
59 lines
1.5 KiB
Python
"""
|
|
Periodic cleanup: anonymize unconfirmed waitlist entries older than CONFIRM_TOKEN_EXPIRY_HOURS.
|
|
|
|
Run as a cron job:
|
|
python -m app.cleanup
|
|
|
|
Or integrate into docker-compose with a one-shot service.
|
|
"""
|
|
|
|
import asyncio
|
|
import datetime
|
|
import logging
|
|
|
|
from sqlalchemy import select, and_
|
|
|
|
from app.config import settings
|
|
from app.db import async_session
|
|
from app.models import WaitlistEntry
|
|
from app.routes import _anonymize_entry
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
async def anonymize_expired() -> int:
|
|
"""Anonymize all unconfirmed entries past the token expiry window. Returns count."""
|
|
cutoff = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(
|
|
hours=settings.CONFIRM_TOKEN_EXPIRY_HOURS
|
|
)
|
|
|
|
async with async_session() as db:
|
|
result = await db.execute(
|
|
select(WaitlistEntry).where(
|
|
and_(
|
|
WaitlistEntry.confirmed == False, # noqa: E712
|
|
WaitlistEntry.anonymized_at == None, # noqa: E711
|
|
WaitlistEntry.created_at < cutoff,
|
|
)
|
|
)
|
|
)
|
|
entries = result.scalars().all()
|
|
|
|
for entry in entries:
|
|
_anonymize_entry(entry)
|
|
|
|
await db.commit()
|
|
|
|
if entries:
|
|
logger.info("Anonymized %d expired unconfirmed entries", len(entries))
|
|
else:
|
|
logger.info("No expired unconfirmed entries to anonymize")
|
|
|
|
return len(entries)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
count = asyncio.run(anonymize_expired())
|
|
raise SystemExit(0)
|