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

133
app/db/node_repository.py Normal file
View File

@@ -0,0 +1,133 @@
"""Repositorio para gestionar nodos."""
from typing import Optional, List
from datetime import datetime
from dataclasses import dataclass
from .database import db
@dataclass
class Node:
"""Modelo de Node."""
node_name: str
db_name: str
active: bool
notes: Optional[str]
updated_at: str
class NodeRepository:
"""Repositorio para operaciones con nodos."""
@staticmethod
def create(node_name: str, db_name: str, notes: Optional[str] = None):
"""
Crea o actualiza un nodo.
Args:
node_name: Nombre del nodo (case-insensitive)
db_name: Nombre de la base de datos
notes: Notas opcionales
"""
now = datetime.utcnow().isoformat()
node_name_upper = node_name.upper()
db.execute("""
INSERT OR REPLACE INTO nodes (node_name, db_name, active, notes, updated_at)
VALUES (?, ?, 1, ?, ?)
""", (node_name_upper, db_name, notes, now))
@staticmethod
def get(node_name: str) -> Optional[Node]:
"""Obtiene un nodo por nombre (case-insensitive)."""
node_name_upper = node_name.upper()
row = db.fetchone("SELECT * FROM nodes WHERE node_name = ?", (node_name_upper,))
if not row:
return None
return Node(
node_name=row["node_name"],
db_name=row["db_name"],
active=bool(row["active"]),
notes=row["notes"],
updated_at=row["updated_at"]
)
@staticmethod
def get_all(active_only: bool = False) -> List[Node]:
"""
Obtiene todos los nodos.
Args:
active_only: Si True, solo devuelve nodos activos
Returns:
Lista de nodos
"""
query = "SELECT * FROM nodes"
if active_only:
query += " WHERE active = 1"
query += " ORDER BY node_name"
rows = db.fetchall(query)
return [Node(
node_name=row["node_name"],
db_name=row["db_name"],
active=bool(row["active"]),
notes=row["notes"],
updated_at=row["updated_at"]
) for row in rows]
@staticmethod
def update(
node_name: str,
db_name: Optional[str] = None,
active: Optional[bool] = None,
notes: Optional[str] = None
):
"""Actualiza un nodo."""
node_name_upper = node_name.upper()
updates = []
params = []
if db_name is not None:
updates.append("db_name = ?")
params.append(db_name)
if active is not None:
updates.append("active = ?")
params.append(1 if active else 0)
if notes is not None:
updates.append("notes = ?")
params.append(notes)
if updates:
now = datetime.utcnow().isoformat()
updates.append("updated_at = ?")
params.append(now)
params.append(node_name_upper)
db.execute(
f"UPDATE nodes SET {', '.join(updates)} WHERE node_name = ?",
tuple(params)
)
@staticmethod
def delete(node_name: str):
"""Elimina un nodo."""
node_name_upper = node_name.upper()
db.execute("DELETE FROM nodes WHERE node_name = ?", (node_name_upper,))
@staticmethod
def get_db_for_node(node_name: str) -> Optional[str]:
"""
Obtiene el nombre de DB para un nodo (si está activo).
Args:
node_name: Nombre del nodo
Returns:
Nombre de la DB o None si no existe o está inactivo
"""
node = NodeRepository.get(node_name)
if node and node.active:
return node.db_name
return None