import logging from datetime import datetime from pathlib import Path from apscheduler.schedulers.background import BackgroundScheduler from sqlalchemy.orm import Session from config import settings from database import SecurityEvent, SessionLocal, Share log = logging.getLogger("droplake.cleanup") def purge_expired(): db: Session = SessionLocal() try: now = datetime.utcnow() # Expired shares expired = db.query(Share).filter(Share.expires_at < now).all() # Burn-after-read shares that have been downloaded burnt = ( db.query(Share) .filter(Share.burn_after_read.is_(True), Share.downloaded.is_(True)) .all() ) # Deduplicate by id seen = {} for s in expired + burnt: seen[s.id] = s to_remove = list(seen.values()) for share in to_remove: blob = Path(settings.storage_path) / share.storage_path blob.unlink(missing_ok=True) db.add(SecurityEvent(event_type="share_cleaned", share_id=share.id)) db.delete(share) if to_remove: db.commit() log.info("Cleanup: removed %d share(s)", len(to_remove)) except Exception as exc: log.error("Cleanup error: %s", exc) db.rollback() finally: db.close() def start_scheduler() -> BackgroundScheduler: scheduler = BackgroundScheduler(daemon=True) scheduler.add_job( purge_expired, "interval", minutes=settings.cleanup_interval_minutes, id="cleanup", replace_existing=True, misfire_grace_time=60, ) scheduler.start() log.info("Cleanup scheduler started (every %dm)", settings.cleanup_interval_minutes) return scheduler