minmal-file-cloud-email-pim.../backend/app/services/backup_scheduler.py

88 lines
2.7 KiB
Python

"""Background scheduler for periodic SFTP backups."""
import os
import threading
import time
from datetime import datetime, timezone, timedelta
_scheduler_thread = None
_scheduler_running = False
def start_backup_scheduler(app):
"""Start the background backup scheduler."""
global _scheduler_thread, _scheduler_running
if _scheduler_running:
return
_scheduler_running = True
def scheduler_loop():
while _scheduler_running:
try:
with app.app_context():
_check_and_run_backups(app)
except Exception as e:
print(f'[Backup Scheduler] Error: {e}')
# Check every 60 seconds
for _ in range(60):
if not _scheduler_running:
break
time.sleep(1)
_scheduler_thread = threading.Thread(target=scheduler_loop, daemon=True)
_scheduler_thread.start()
def stop_backup_scheduler():
global _scheduler_running
_scheduler_running = False
def _check_and_run_backups(app):
"""Check all active backup targets and run if due."""
from app.extensions import db
from app.models.backup_target import BackupTarget
from app.services.sftp_backup import create_backup_zip, upload_backup_to_sftp
targets = BackupTarget.query.filter_by(is_active=True).all()
now = datetime.now(timezone.utc)
for target in targets:
if not target.backup_interval_minutes or target.backup_interval_minutes <= 0:
continue
# Check if backup is due
if target.last_backup_at:
next_due = target.last_backup_at + timedelta(minutes=target.backup_interval_minutes)
if now < next_due:
continue
# Run backup
db_uri = app.config['SQLALCHEMY_DATABASE_URI']
db_path = db_uri.replace('sqlite:///', '')
upload_path = app.config['UPLOAD_PATH']
zip_path = None
try:
zip_path = create_backup_zip(db_path, upload_path)
version = upload_backup_to_sftp(target, zip_path, app)
target.last_backup_at = now
target.last_backup_status = 'success'
target.last_backup_message = f'Version {version} erfolgreich hochgeladen'
db.session.commit()
print(f'[Backup] {target.name}: {version} OK')
except Exception as e:
target.last_backup_at = now
target.last_backup_status = 'error'
target.last_backup_message = str(e)[:500]
db.session.commit()
print(f'[Backup] {target.name}: FEHLER - {e}')
finally:
if zip_path and os.path.exists(zip_path):
os.unlink(zip_path)