141 lines
4.7 KiB
Python
141 lines
4.7 KiB
Python
import os
|
|
import sys
|
|
import threading
|
|
import time
|
|
from pathlib import Path
|
|
from PIL import Image
|
|
import pystray
|
|
from pystray import MenuItem as item
|
|
|
|
# Add agent root to sys.path
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
|
|
from agent.config import load_config, AgentConfig
|
|
from agent.service import AgentDaemon
|
|
from agent.uploader import ChunkUploader
|
|
from create_icons import generate_app_icons
|
|
|
|
class WindowsTrayAgent:
|
|
"""Windows System Tray (Área de Notificaciones) Application for OnEver Drive."""
|
|
|
|
def __init__(self):
|
|
self.config = load_config()
|
|
self.daemon = AgentDaemon(self.config)
|
|
self.icon = None
|
|
self.is_paused = False
|
|
self.icon_image = self._load_icon()
|
|
|
|
def _load_icon(self) -> Image.Image:
|
|
assets_dir = Path(__file__).resolve().parent / "assets"
|
|
png_path = assets_dir / "icon.png"
|
|
if not png_path.exists():
|
|
_, png_path = generate_app_icons()
|
|
return Image.open(png_path)
|
|
|
|
def _get_status_text(self) -> str:
|
|
if not self.config.client_code:
|
|
return "Estado: Sin Registrar"
|
|
if self.is_paused:
|
|
return "Estado: Pausado"
|
|
return f"Estado: Conectado ({self.config.client_code})"
|
|
|
|
def _toggle_pause(self, icon, item_obj):
|
|
self.is_paused = not self.is_paused
|
|
if self.is_paused:
|
|
self.daemon.stop()
|
|
self.notify("Sincronización en pausa", "El servicio de backup ha sido pausado.")
|
|
else:
|
|
self.daemon.start()
|
|
self.notify("Sincronización activa", "El servicio de backup se ha reanudado.")
|
|
|
|
def _open_gui(self, icon=None, item_obj=None):
|
|
def run_gui():
|
|
from agent_gui import launch_gui
|
|
launch_gui()
|
|
|
|
threading.Thread(target=run_gui, daemon=True).start()
|
|
|
|
def _manual_backup(self, icon=None, item_obj=None):
|
|
import tkinter as tk
|
|
from tkinter import filedialog, messagebox
|
|
|
|
def run_picker():
|
|
root = tk.Tk()
|
|
root.withdraw()
|
|
filepath = filedialog.askopenfilename(
|
|
title="Seleccionar archivo para backup",
|
|
filetypes=[("Archivos SQL / Datos", "*.bak;*.mdf;*.*")]
|
|
)
|
|
if not filepath:
|
|
root.destroy()
|
|
return
|
|
|
|
self.notify("Iniciando Backup", f"Preparando transferencia por chunks: {Path(filepath).name}")
|
|
|
|
def upload_worker():
|
|
try:
|
|
uploader = ChunkUploader(self.config)
|
|
res = uploader.upload_file(Path(filepath))
|
|
self.notify("Backup Completado ✓", f"{Path(filepath).name} verificado con éxito en el servidor.")
|
|
except Exception as ex:
|
|
self.notify("Error en Backup ✗", f"Fallo al subir {Path(filepath).name}: {str(ex)}")
|
|
|
|
threading.Thread(target=upload_worker, daemon=True).start()
|
|
root.destroy()
|
|
|
|
threading.Thread(target=run_picker, daemon=True).start()
|
|
|
|
def notify(self, title: str, message: str):
|
|
"""Displays a native Windows Notification Balloon."""
|
|
if self.icon:
|
|
try:
|
|
self.icon.notify(message, title)
|
|
except Exception:
|
|
pass
|
|
|
|
def _on_exit(self, icon, item_obj):
|
|
self.daemon.stop()
|
|
icon.stop()
|
|
|
|
def build_menu(self):
|
|
client_title = f"OnEver Drive ({self.config.client_name or 'Agente'})"
|
|
return pystray.Menu(
|
|
item(client_title, lambda: None, enabled=False),
|
|
item(lambda text: self._get_status_text(), lambda: None, enabled=False),
|
|
pystray.Menu.SEPARATOR,
|
|
item("Abrir Panel de Control...", self._open_gui, default=True),
|
|
item("Hacer Backup Manual...", self._manual_backup),
|
|
item(lambda text: "Reanudar Servicio" if self.is_paused else "Pausar Servicio", self._toggle_pause),
|
|
pystray.Menu.SEPARATOR,
|
|
item("Salir", self._on_exit)
|
|
)
|
|
|
|
def run(self):
|
|
# Start background daemon worker
|
|
if self.config.device_id:
|
|
self.daemon.start()
|
|
|
|
# Create tray icon
|
|
self.icon = pystray.Icon(
|
|
name="OnEverDrive",
|
|
icon=self.icon_image,
|
|
title="OnEver Drive — Agente de Backup",
|
|
menu=self.build_menu()
|
|
)
|
|
|
|
# Notify on startup
|
|
if self.config.client_code:
|
|
self.notify("OnEver Drive Activo", f"Agente en ejecución ({self.config.client_code})")
|
|
else:
|
|
self.notify("OnEver Drive", "Agente iniciado. Requiere registro en el servidor.")
|
|
|
|
# Run system tray event loop
|
|
self.icon.run()
|
|
|
|
def main():
|
|
agent = WindowsTrayAgent()
|
|
agent.run()
|
|
|
|
if __name__ == "__main__":
|
|
main()
|