Initial commit: CloudRestoreAS v1.0.0 - Aplicación completa de restauración automática SQL Server
This commit is contained in:
140
app/db/database.py
Normal file
140
app/db/database.py
Normal file
@@ -0,0 +1,140 @@
|
||||
"""Esquema y gestión de la base de datos SQLite."""
|
||||
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
from ..constants import DB_PATH
|
||||
|
||||
|
||||
class DatabaseManager:
|
||||
"""Gestor de la base de datos SQLite."""
|
||||
|
||||
def __init__(self, db_path: Optional[Path] = None):
|
||||
"""
|
||||
Inicializa el gestor de base de datos.
|
||||
|
||||
Args:
|
||||
db_path: Ruta a la base de datos (usa DB_PATH por defecto)
|
||||
"""
|
||||
self.db_path = db_path or DB_PATH
|
||||
self.db_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
self._initialize_schema()
|
||||
|
||||
def get_connection(self) -> sqlite3.Connection:
|
||||
"""Obtiene una conexión a la base de datos."""
|
||||
conn = sqlite3.connect(str(self.db_path), check_same_thread=False)
|
||||
conn.row_factory = sqlite3.Row
|
||||
return conn
|
||||
|
||||
def _initialize_schema(self):
|
||||
"""Crea las tablas si no existen."""
|
||||
with self.get_connection() as conn:
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Tabla de jobs
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS jobs (
|
||||
job_id TEXT PRIMARY KEY,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
source_path TEXT NOT NULL,
|
||||
source_name TEXT NOT NULL,
|
||||
source_hash TEXT,
|
||||
node_name TEXT,
|
||||
db_name TEXT,
|
||||
status TEXT NOT NULL,
|
||||
attempts INTEGER DEFAULT 0,
|
||||
last_error TEXT,
|
||||
started_at TEXT,
|
||||
finished_at TEXT,
|
||||
total_ms INTEGER,
|
||||
extract_ms INTEGER,
|
||||
restore_ms INTEGER,
|
||||
filelist_ms INTEGER
|
||||
)
|
||||
""")
|
||||
|
||||
# Tabla de pasos de jobs
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS job_steps (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
job_id TEXT NOT NULL,
|
||||
step TEXT NOT NULL,
|
||||
started_at TEXT NOT NULL,
|
||||
finished_at TEXT,
|
||||
duration_ms INTEGER,
|
||||
exit_code INTEGER,
|
||||
stdout TEXT,
|
||||
stderr TEXT,
|
||||
error TEXT,
|
||||
FOREIGN KEY (job_id) REFERENCES jobs(job_id)
|
||||
)
|
||||
""")
|
||||
|
||||
# Tabla de nodos (mapeo node_name -> db_name)
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS nodes (
|
||||
node_name TEXT PRIMARY KEY,
|
||||
db_name TEXT NOT NULL,
|
||||
active INTEGER DEFAULT 1,
|
||||
notes TEXT,
|
||||
updated_at TEXT NOT NULL
|
||||
)
|
||||
""")
|
||||
|
||||
# Tabla de eventos
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS events (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
created_at TEXT NOT NULL,
|
||||
level TEXT NOT NULL,
|
||||
job_id TEXT,
|
||||
message TEXT NOT NULL,
|
||||
FOREIGN KEY (job_id) REFERENCES jobs(job_id)
|
||||
)
|
||||
""")
|
||||
|
||||
# Tabla de configuración
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS config (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
)
|
||||
""")
|
||||
|
||||
# Índices
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_jobs_status ON jobs(status)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_jobs_created ON jobs(created_at)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_events_created ON events(created_at)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_events_job ON events(job_id)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_job_steps_job ON job_steps(job_id)")
|
||||
|
||||
conn.commit()
|
||||
|
||||
def execute(self, query: str, params: tuple = ()) -> sqlite3.Cursor:
|
||||
"""Ejecuta una consulta SQL."""
|
||||
with self.get_connection() as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(query, params)
|
||||
conn.commit()
|
||||
return cursor
|
||||
|
||||
def fetchone(self, query: str, params: tuple = ()) -> Optional[sqlite3.Row]:
|
||||
"""Ejecuta una consulta y devuelve una fila."""
|
||||
with self.get_connection() as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(query, params)
|
||||
return cursor.fetchone()
|
||||
|
||||
def fetchall(self, query: str, params: tuple = ()) -> list[sqlite3.Row]:
|
||||
"""Ejecuta una consulta y devuelve todas las filas."""
|
||||
with self.get_connection() as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(query, params)
|
||||
return cursor.fetchall()
|
||||
|
||||
|
||||
# Instancia global
|
||||
db = DatabaseManager()
|
||||
Reference in New Issue
Block a user