271 lines
9.6 KiB
Python
271 lines
9.6 KiB
Python
"""
|
|
AutoBackups - Aplicación Principal Flask
|
|
Sistema automatizado de backup para proyectos Simatic S7
|
|
"""
|
|
|
|
import sys
|
|
import os
|
|
import logging
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
from flask import Flask, render_template, request, jsonify, redirect, url_for
|
|
from apscheduler.schedulers.background import BackgroundScheduler
|
|
from apscheduler.triggers.cron import CronTrigger
|
|
import atexit
|
|
|
|
# Agregar el directorio src al path para imports
|
|
current_dir = Path(__file__).parent
|
|
src_dir = current_dir
|
|
sys.path.insert(0, str(src_dir))
|
|
|
|
# Imports de módulos propios
|
|
from models.config_model import Config
|
|
from models.project_model import ProjectManager
|
|
from utils.file_utils import DiskSpaceChecker
|
|
from services.basic_backup_service import BasicBackupService
|
|
from routes import register_api_routes, register_web_routes
|
|
|
|
|
|
# Crear instancia Flask
|
|
app = Flask(__name__, template_folder="../templates", static_folder="../static")
|
|
|
|
|
|
class AutoBackupsFlaskApp:
|
|
"""Aplicación principal de AutoBackups con Flask"""
|
|
|
|
def __init__(self):
|
|
self.config = None
|
|
self.project_manager = None
|
|
self.disk_checker = None
|
|
self.backup_service = None
|
|
self.scheduler = None
|
|
self.logger = None
|
|
|
|
# Inicializar aplicación
|
|
self._setup_logging()
|
|
self._load_configuration()
|
|
self._initialize_services()
|
|
self._setup_scheduler()
|
|
self._register_routes()
|
|
|
|
def _setup_logging(self):
|
|
"""Configurar sistema de logging"""
|
|
try:
|
|
# Crear directorio de logs si no existe
|
|
logs_dir = Path(__file__).parent.parent / ".logs"
|
|
logs_dir.mkdir(exist_ok=True)
|
|
|
|
# Nombre del archivo de log con timestamp
|
|
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
|
log_filename = logs_dir / f"autobackups_{timestamp}.log"
|
|
|
|
# Configurar logging
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
handlers=[
|
|
logging.FileHandler(log_filename, encoding="utf-8"),
|
|
logging.StreamHandler(sys.stdout),
|
|
],
|
|
)
|
|
|
|
self.logger = logging.getLogger(__name__)
|
|
self.logger.info(f"AutoBackups Flask iniciado - Log: {log_filename}")
|
|
|
|
except Exception as e:
|
|
print(f"Error configurando logging: {e}")
|
|
sys.exit(1)
|
|
|
|
def _load_configuration(self):
|
|
"""Cargar configuración del sistema"""
|
|
try:
|
|
self.logger.info("Cargando configuración...")
|
|
self.config = Config()
|
|
self.logger.info("Configuración cargada exitosamente")
|
|
|
|
# Mostrar información básica de configuración
|
|
dirs_count = len(self.config.observation_directories)
|
|
self.logger.info(f"Directorios de observación: {dirs_count}")
|
|
self.logger.info(f"Destino: {self.config.backup_destination}")
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Error cargando configuración: {e}")
|
|
sys.exit(1)
|
|
|
|
def _initialize_services(self):
|
|
"""Inicializar servicios principales"""
|
|
try:
|
|
self.logger.info("Inicializando servicios...")
|
|
|
|
# Project Manager
|
|
self.project_manager = ProjectManager()
|
|
self.logger.info("Project Manager inicializado")
|
|
|
|
# Disk Space Checker
|
|
self.disk_checker = DiskSpaceChecker()
|
|
self.logger.info("Disk Space Checker inicializado")
|
|
|
|
# Basic Backup Service
|
|
self.backup_service = BasicBackupService(self.config)
|
|
self.logger.info("Basic Backup Service inicializado")
|
|
|
|
self.logger.info("Todos los servicios inicializados correctamente")
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Error inicializando servicios: {e}")
|
|
sys.exit(1)
|
|
|
|
def _setup_scheduler(self):
|
|
"""Configurar scheduler para tareas automáticas"""
|
|
try:
|
|
self.scheduler = BackgroundScheduler(timezone="America/Mexico_City")
|
|
|
|
# Agregar job de escaneo cada hora
|
|
self.scheduler.add_job(
|
|
func=self.scheduled_project_scan,
|
|
trigger=CronTrigger(minute=0), # Cada hora en el minuto 0
|
|
id="project_scan",
|
|
name="Escaneo de proyectos automático",
|
|
replace_existing=True,
|
|
)
|
|
|
|
# Iniciar scheduler
|
|
self.scheduler.start()
|
|
self.logger.info("Scheduler configurado e iniciado")
|
|
|
|
# Asegurar que el scheduler se detenga al cerrar la aplicación
|
|
atexit.register(lambda: self.scheduler.shutdown())
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Error configurando scheduler: {e}")
|
|
|
|
def _register_routes(self):
|
|
"""Registrar rutas de Flask usando módulos separados"""
|
|
register_web_routes(app, self)
|
|
register_api_routes(app, self)
|
|
self.logger.info("Rutas Flask registradas desde módulos")
|
|
|
|
def scheduled_project_scan(self):
|
|
"""Escaneo programado de proyectos"""
|
|
try:
|
|
self.logger.info("Iniciando escaneo programado de proyectos...")
|
|
projects_found = self.discover_projects()
|
|
self.logger.info(f"Escaneo completado: {projects_found} proyectos")
|
|
except Exception as e:
|
|
self.logger.error(f"Error en escaneo programado: {e}")
|
|
|
|
def discover_projects(self) -> int:
|
|
"""Descubrir proyectos en directorios de observación"""
|
|
try:
|
|
self.logger.info("Iniciando descubrimiento de proyectos...")
|
|
|
|
# Usar el servicio básico de backup
|
|
if hasattr(self, "backup_service"):
|
|
projects = self.backup_service.discover_projects_basic()
|
|
else:
|
|
projects = []
|
|
|
|
# Obtener proyectos existentes para evitar duplicados
|
|
existing_projects = self.project_manager.get_all_projects()
|
|
existing_paths = {project.path for project in existing_projects}
|
|
|
|
# Agregar solo proyectos nuevos al manager
|
|
new_projects_count = 0
|
|
for project_info in projects:
|
|
project_path = project_info.get("path")
|
|
if project_path not in existing_paths:
|
|
self.project_manager.add_or_update_project(project_info)
|
|
new_projects_count += 1
|
|
self.logger.debug(
|
|
f"Nuevo proyecto agregado: {project_info.get('name')}"
|
|
)
|
|
else:
|
|
self.logger.debug(
|
|
f"Proyecto ya existe, omitido: {project_info.get('name')}"
|
|
)
|
|
|
|
msg = f"Descubrimiento completado: {len(projects)} proyectos encontrados, {new_projects_count} nuevos agregados"
|
|
self.logger.info(msg)
|
|
|
|
return new_projects_count
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Error en descubrimiento de proyectos: {e}")
|
|
return 0
|
|
|
|
def check_system_requirements(self) -> bool:
|
|
"""Verificar requerimientos del sistema"""
|
|
try:
|
|
self.logger.info("Verificando requerimientos del sistema...")
|
|
|
|
# Verificar espacio en disco
|
|
backup_destination = self.config.backup_destination
|
|
min_space_mb = self.config.get_min_free_space_mb()
|
|
|
|
free_space_mb = self.disk_checker.get_free_space_mb(backup_destination)
|
|
|
|
if free_space_mb < min_space_mb:
|
|
self.logger.error(
|
|
f"Espacio insuficiente en destino de backup. "
|
|
f"Disponible: {free_space_mb:.1f}MB, "
|
|
f"Requerido: {min_space_mb}MB"
|
|
)
|
|
return False
|
|
|
|
self.logger.info(f"Espacio en disco OK: {free_space_mb:.1f}MB disponibles")
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Error verificando requerimientos del sistema: {e}")
|
|
return False
|
|
|
|
|
|
# Instancia global de la aplicación
|
|
autobackups_app = None
|
|
|
|
|
|
def main():
|
|
"""Función principal"""
|
|
global autobackups_app
|
|
|
|
try:
|
|
print("AutoBackups - Sistema de Backup Automatizado (Flask)")
|
|
print("=" * 55)
|
|
|
|
# Crear y configurar aplicación Flask
|
|
autobackups_app = AutoBackupsFlaskApp()
|
|
|
|
# Verificar requerimientos del sistema
|
|
if not autobackups_app.check_system_requirements():
|
|
print("Error en la verificación de requerimientos del sistema.")
|
|
sys.exit(1)
|
|
|
|
# Ejecutar descubrimiento inicial de proyectos
|
|
projects_found = autobackups_app.discover_projects()
|
|
print(f"Descubrimiento inicial: {projects_found} proyectos encontrados")
|
|
|
|
# Configurar Flask
|
|
host = autobackups_app.config.web_interface.get("host", "127.0.0.1")
|
|
port = autobackups_app.config.web_interface.get("port", 5000)
|
|
debug = autobackups_app.config.web_interface.get("debug", False)
|
|
|
|
print(f"\nServidor web iniciando en http://{host}:{port}")
|
|
print("Presiona Ctrl+C para detener el servidor")
|
|
|
|
# Iniciar servidor Flask
|
|
app.run(host=host, port=port, debug=debug, use_reloader=False)
|
|
|
|
except KeyboardInterrupt:
|
|
print("\nAplicación interrumpida por el usuario")
|
|
if autobackups_app and autobackups_app.scheduler:
|
|
autobackups_app.scheduler.shutdown()
|
|
except Exception as e:
|
|
print(f"Error inesperado: {e}")
|
|
if autobackups_app and autobackups_app.scheduler:
|
|
autobackups_app.scheduler.shutdown()
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|