""" Scheduler module for handling background tasks """ import logging from apscheduler.schedulers.background import BackgroundScheduler import config from utils.backup import create_backup from db import get_connection def scheduled_backup(): """Führt ein geplantes Backup aus""" logging.info("Starte geplantes Backup...") create_backup(backup_type="scheduled", created_by="scheduler") def cleanup_expired_sessions(): """Clean up expired license sessions - concurrent sessions aware""" try: conn = get_connection() cur = conn.cursor() # Get client config for timeout value cur.execute(""" SELECT session_timeout FROM client_configs WHERE client_name = 'Account Forger' """) result = cur.fetchone() timeout_seconds = result[0] if result else 60 # Find expired sessions that are still active cur.execute(""" SELECT id, license_id, hardware_fingerprint, ip_address, client_version, started_at, hardware_id, machine_name FROM license_sessions WHERE ended_at IS NULL AND last_heartbeat < CURRENT_TIMESTAMP - INTERVAL '%s seconds' """, (timeout_seconds,)) expired_sessions = cur.fetchall() if expired_sessions: logging.info(f"Found {len(expired_sessions)} expired sessions to clean up") # Count sessions by license before cleanup for logging license_session_counts = {} for session in expired_sessions: license_id = session[1] if license_id not in license_session_counts: license_session_counts[license_id] = 0 license_session_counts[license_id] += 1 for session in expired_sessions: # Log to history cur.execute(""" INSERT INTO session_history (license_id, hardware_id, hardware_fingerprint, machine_name, ip_address, client_version, started_at, ended_at, end_reason) VALUES (%s, %s, %s, %s, %s, %s, %s, CURRENT_TIMESTAMP, 'timeout') """, (session[1], session[6], session[2], session[7], session[3], session[4], session[5])) # Mark session as ended instead of deleting cur.execute("UPDATE license_sessions SET ended_at = CURRENT_TIMESTAMP, end_reason = 'timeout' WHERE id = %s", (session[0],)) conn.commit() # Log cleanup summary logging.info(f"Cleaned up {len(expired_sessions)} expired sessions from {len(license_session_counts)} licenses") for license_id, count in license_session_counts.items(): if count > 1: logging.info(f" License ID {license_id}: {count} sessions cleaned up") cur.close() conn.close() except Exception as e: logging.error(f"Error cleaning up sessions: {str(e)}") if 'conn' in locals(): conn.rollback() def deactivate_expired_licenses(): """Deactivate licenses that have expired""" try: conn = get_connection() cur = conn.cursor() # Find active licenses that have expired # Check valid_until < today (at midnight) cur.execute(""" SELECT id, license_key, customer_id, valid_until FROM licenses WHERE is_active = true AND valid_until IS NOT NULL AND valid_until < CURRENT_DATE AND is_fake = false """) expired_licenses = cur.fetchall() if expired_licenses: logging.info(f"Found {len(expired_licenses)} expired licenses to deactivate") for license in expired_licenses: license_id, license_key, customer_id, valid_until = license # Deactivate the license cur.execute(""" UPDATE licenses SET is_active = false, updated_at = CURRENT_TIMESTAMP WHERE id = %s """, (license_id,)) # Log to audit trail cur.execute(""" INSERT INTO audit_log (timestamp, username, action, entity_type, entity_id, old_values, new_values, additional_info) VALUES (CURRENT_TIMESTAMP, 'system', 'DEACTIVATE', 'license', %s, jsonb_build_object('is_active', true), jsonb_build_object('is_active', false), %s) """, (license_id, f"License automatically deactivated due to expiration. Valid until: {valid_until}")) logging.info(f"Deactivated expired license: {license_key} (ID: {license_id})") conn.commit() logging.info(f"Successfully deactivated {len(expired_licenses)} expired licenses") else: logging.debug("No expired licenses found to deactivate") cur.close() conn.close() except Exception as e: logging.error(f"Error deactivating expired licenses: {str(e)}") if 'conn' in locals(): conn.rollback() def init_scheduler(): """Initialize and configure the scheduler""" scheduler = BackgroundScheduler() # Configure daily backup job scheduler.add_job( scheduled_backup, 'cron', hour=config.SCHEDULER_CONFIG['backup_hour'], minute=config.SCHEDULER_CONFIG['backup_minute'], id='daily_backup', replace_existing=True ) # Configure session cleanup job - runs every 60 seconds scheduler.add_job( cleanup_expired_sessions, 'interval', seconds=60, id='session_cleanup', replace_existing=True ) # Configure license expiration job - runs daily at midnight scheduler.add_job( deactivate_expired_licenses, 'cron', hour=0, minute=0, id='license_expiration_check', replace_existing=True ) scheduler.start() logging.info(f"Scheduler started. Daily backup scheduled at {config.SCHEDULER_CONFIG['backup_hour']:02d}:{config.SCHEDULER_CONFIG['backup_minute']:02d}") logging.info("Session cleanup job scheduled to run every 60 seconds") logging.info("License expiration check scheduled to run daily at midnight") return scheduler