Dateien
Hetzner-Backup/v2_adminpanel/scheduler.py
2025-07-03 20:38:33 +00:00

179 Zeilen
6.6 KiB
Python

"""
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