Initial commit: CloudRestoreAS v1.0.0 - Aplicación completa de restauración automática SQL Server

This commit is contained in:
2026-01-25 16:53:00 -07:00
commit 854ffa116f
43 changed files with 6357 additions and 0 deletions

105
app/ui/logs_tab.py Normal file
View File

@@ -0,0 +1,105 @@
"""Tab de logs y soporte."""
import subprocess
from pathlib import Path
from PySide6.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
QTextEdit, QComboBox, QLabel, QMessageBox
)
from ..db.event_repository import EventRepository
from ..constants import LOGS_DIR, LogLevel
class LogsTab(QWidget):
"""Tab de visualización de logs y soporte."""
def __init__(self, engine):
"""Inicializa el tab."""
super().__init__()
self.engine = engine
self._setup_ui()
self.refresh_logs()
def _setup_ui(self):
"""Configura la interfaz."""
layout = QVBoxLayout(self)
# Controles
controls_layout = QHBoxLayout()
controls_layout.addWidget(QLabel("Nivel:"))
self.level_filter = QComboBox()
self.level_filter.addItems([
"Todos",
LogLevel.DEBUG,
LogLevel.INFO,
LogLevel.WARNING,
LogLevel.ERROR,
LogLevel.CRITICAL
])
self.level_filter.setCurrentText(LogLevel.INFO)
self.level_filter.currentTextChanged.connect(self.refresh_logs)
controls_layout.addWidget(self.level_filter)
refresh_btn = QPushButton("🔄 Actualizar")
refresh_btn.clicked.connect(self.refresh_logs)
controls_layout.addWidget(refresh_btn)
controls_layout.addStretch()
open_folder_btn = QPushButton("📁 Abrir Carpeta Logs")
open_folder_btn.clicked.connect(self._open_logs_folder)
controls_layout.addWidget(open_folder_btn)
layout.addLayout(controls_layout)
# Área de logs
self.logs_text = QTextEdit()
self.logs_text.setReadOnly(True)
self.logs_text.setLineWrapMode(QTextEdit.LineWrapMode.NoWrap)
layout.addWidget(self.logs_text)
# Info
info_layout = QHBoxLayout()
info_label = QLabel("💡 Los logs se rotan automáticamente cada 10MB.")
info_layout.addWidget(info_label)
info_layout.addStretch()
layout.addLayout(info_layout)
def refresh_logs(self):
"""Refresca los logs desde la base de datos."""
level = self.level_filter.currentText()
if level == "Todos":
events = EventRepository.get_all(limit=200)
else:
events = EventRepository.get_all(level=level, limit=200)
# Formatear logs
log_lines = []
for event in reversed(events): # Mostrar más recientes primero
timestamp = event.created_at.replace("T", " ").split(".")[0]
job_info = f" [Job: {event.job_id[:8]}]" if event.job_id else ""
log_lines.append(
f"{timestamp} | {event.level:8s}{job_info} | {event.message}"
)
self.logs_text.setPlainText("\n".join(log_lines))
def _open_logs_folder(self):
"""Abre la carpeta de logs en el explorador."""
try:
if LOGS_DIR.exists():
subprocess.run(["explorer", str(LOGS_DIR)])
else:
QMessageBox.warning(
self,
"Carpeta No Existe",
f"La carpeta de logs no existe:\n{LOGS_DIR}"
)
except Exception as e:
QMessageBox.critical(self, "Error", f"Error abriendo carpeta: {e}")