feat: BreakPilot PWA - Full codebase (clean push without large binaries)
Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed
Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed
All services: admin-v2, studio-v2, website, ai-compliance-sdk, consent-service, klausur-service, voice-service, and infrastructure. Large PDFs and compiled binaries excluded via .gitignore.
This commit is contained in:
141
backend/session/cleanup_job.py
Normal file
141
backend/session/cleanup_job.py
Normal file
@@ -0,0 +1,141 @@
|
||||
"""
|
||||
Session Cleanup Job
|
||||
|
||||
Removes expired sessions from PostgreSQL.
|
||||
Valkey handles its own expiry via TTL.
|
||||
|
||||
This job should be run periodically (e.g., via cron or APScheduler).
|
||||
|
||||
Usage:
|
||||
# Run directly
|
||||
python -m session.cleanup_job
|
||||
|
||||
# Or import and call
|
||||
from session.cleanup_job import run_cleanup
|
||||
|
||||
await run_cleanup()
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
from datetime import datetime, timezone
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def run_cleanup():
|
||||
"""Run session cleanup job."""
|
||||
from .session_store import get_session_store
|
||||
|
||||
logger.info("Starting session cleanup job...")
|
||||
|
||||
try:
|
||||
store = await get_session_store()
|
||||
count = await store.cleanup_expired_sessions()
|
||||
logger.info(f"Session cleanup completed: removed {count} expired sessions")
|
||||
return count
|
||||
except Exception as e:
|
||||
logger.error(f"Session cleanup failed: {e}")
|
||||
raise
|
||||
|
||||
|
||||
async def run_cleanup_with_pg():
|
||||
"""
|
||||
Run cleanup directly with PostgreSQL connection.
|
||||
|
||||
Useful when session store is not initialized.
|
||||
"""
|
||||
database_url = os.environ.get("DATABASE_URL")
|
||||
if not database_url:
|
||||
logger.warning("DATABASE_URL not set, skipping cleanup")
|
||||
return 0
|
||||
|
||||
try:
|
||||
import asyncpg
|
||||
|
||||
conn = await asyncpg.connect(database_url)
|
||||
try:
|
||||
# Delete sessions expired more than 7 days ago
|
||||
result = await conn.execute("""
|
||||
DELETE FROM user_sessions
|
||||
WHERE expires_at < NOW() - INTERVAL '7 days'
|
||||
""")
|
||||
count = int(result.split()[-1]) if result else 0
|
||||
logger.info(f"Session cleanup completed: removed {count} expired sessions")
|
||||
return count
|
||||
finally:
|
||||
await conn.close()
|
||||
except Exception as e:
|
||||
logger.error(f"Session cleanup failed: {e}")
|
||||
return 0
|
||||
|
||||
|
||||
def setup_scheduler():
|
||||
"""
|
||||
Setup APScheduler for periodic cleanup.
|
||||
|
||||
Runs cleanup every 6 hours.
|
||||
"""
|
||||
try:
|
||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||
from apscheduler.triggers.interval import IntervalTrigger
|
||||
|
||||
scheduler = AsyncIOScheduler()
|
||||
|
||||
scheduler.add_job(
|
||||
run_cleanup,
|
||||
trigger=IntervalTrigger(hours=6),
|
||||
id="session_cleanup",
|
||||
name="Session Cleanup Job",
|
||||
replace_existing=True,
|
||||
)
|
||||
|
||||
scheduler.start()
|
||||
logger.info("Session cleanup scheduler started (runs every 6 hours)")
|
||||
|
||||
return scheduler
|
||||
|
||||
except ImportError:
|
||||
logger.warning("APScheduler not installed, cleanup job not scheduled")
|
||||
return None
|
||||
|
||||
|
||||
def register_with_fastapi(app):
|
||||
"""
|
||||
Register cleanup job with FastAPI app lifecycle.
|
||||
|
||||
Usage:
|
||||
from session.cleanup_job import register_with_fastapi
|
||||
register_with_fastapi(app)
|
||||
"""
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
scheduler = None
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app):
|
||||
nonlocal scheduler
|
||||
# Startup
|
||||
scheduler = setup_scheduler()
|
||||
# Run initial cleanup
|
||||
asyncio.create_task(run_cleanup())
|
||||
|
||||
yield
|
||||
|
||||
# Shutdown
|
||||
if scheduler:
|
||||
scheduler.shutdown()
|
||||
|
||||
return lifespan
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||
)
|
||||
|
||||
# Run cleanup
|
||||
asyncio.run(run_cleanup_with_pg())
|
||||
Reference in New Issue
Block a user