190 lines
5.4 KiB
Python
190 lines
5.4 KiB
Python
"""Constantes globales de la aplicación."""
|
|
|
|
import platform
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
IS_WINDOWS = sys.platform == "win32"
|
|
|
|
# Plataforma y arquitectura de ESTE build, en el vocabulario que usa el PANEL para
|
|
# decidir qué artefacto le corresponde a cada servidor (a24c.cras_releases).
|
|
# Se reportan en POST /api/restore/instance-config junto con app_version.
|
|
APP_PLATFORM = "windows" if IS_WINDOWS else "linux"
|
|
|
|
# platform.machine() varía por SO para la misma arquitectura ("AMD64" en Windows,
|
|
# "x86_64" en Linux); se normaliza a un solo vocabulario.
|
|
_ARCH_ALIASES = {
|
|
"x86_64": "x86_64",
|
|
"amd64": "x86_64",
|
|
"x86": "x86",
|
|
"i386": "x86",
|
|
"i686": "x86",
|
|
"aarch64": "arm64",
|
|
"arm64": "arm64",
|
|
}
|
|
|
|
|
|
def _resolve_arch() -> str:
|
|
raw = (platform.machine() or "").strip().lower()
|
|
return _ARCH_ALIASES.get(raw, raw or "unknown")
|
|
|
|
|
|
APP_ARCH = _resolve_arch()
|
|
|
|
|
|
def _resolve_app_dir() -> Path:
|
|
"""Directorio donde vive el ejecutable (persistente)."""
|
|
if getattr(sys, "frozen", False):
|
|
return Path(sys.executable).resolve().parent
|
|
return Path(__file__).resolve().parent.parent
|
|
|
|
|
|
def _resolve_bundle_dir() -> Path:
|
|
"""Recursos embebidos en el binario PyInstaller onefile."""
|
|
if getattr(sys, "frozen", False):
|
|
return Path(sys._MEIPASS)
|
|
return _resolve_app_dir() / "packaging" / "bundled" / APP_PLATFORM
|
|
|
|
|
|
APP_DIR = _resolve_app_dir()
|
|
CONFIG_DIR = APP_DIR / "config"
|
|
ENV_PATH = CONFIG_DIR / ".env"
|
|
|
|
# Sello con la versión que corrió por última vez en esta instalación. El bootstrap lo
|
|
# reescribe en cada arranque; el instalador remoto del PANEL lo lee por SFTP para
|
|
# verificar el despliegue sin depender de stdout (el .exe se compila con console=False).
|
|
VERSION_PATH = CONFIG_DIR / ".version"
|
|
|
|
# Sello con el hash de packaging/bundled-versions.json del build que desplegó 7zip/odbc.
|
|
# Si cambia, el bootstrap re-copia esas carpetas (ver _copy_bundled_tree).
|
|
BUNDLED_STAMP_PATH = CONFIG_DIR / ".bundled_deps"
|
|
|
|
DATA_DIR = CONFIG_DIR / "data"
|
|
LOGS_DIR = CONFIG_DIR / "logs"
|
|
ODBC_DIR = CONFIG_DIR / "odbc"
|
|
SEVEN_ZIP_DIR = CONFIG_DIR / "7zip"
|
|
|
|
BUNDLE_DIR = _resolve_bundle_dir()
|
|
BUNDLED_SOURCE_7ZIP = BUNDLE_DIR / "bundled" / "7zip"
|
|
BUNDLED_SOURCE_ODBC = BUNDLE_DIR / "bundled" / "odbc"
|
|
def _default_7zip_name() -> str:
|
|
if IS_WINDOWS:
|
|
return "7z.exe"
|
|
return "7zz"
|
|
|
|
|
|
BUNDLED_7ZIP_EXE = SEVEN_ZIP_DIR / _default_7zip_name()
|
|
BUNDLED_7ZIP_FALLBACK = SEVEN_ZIP_DIR / ("7za.exe" if IS_WINDOWS else "7zz")
|
|
|
|
# Carpetas de trabajo junto al ejecutable
|
|
DIR_ENTRADA = APP_DIR / "Entrada"
|
|
DIR_PROCESADOS = APP_DIR / "Procesados"
|
|
DIR_FALLADOS = APP_DIR / "Fallados"
|
|
DIR_TEMP = APP_DIR / "Temp"
|
|
|
|
DB_PATH = DATA_DIR / "app.db"
|
|
|
|
# Estados de jobs
|
|
class JobStatus:
|
|
QUEUED = "queued"
|
|
EXTRACTING = "extracting"
|
|
RESTORING = "restoring"
|
|
CLEANING = "cleaning"
|
|
COMPLETED = "completed"
|
|
FAILED = "failed"
|
|
FAILED_RESTART = "failed_restart"
|
|
CANCELLED = "cancelled"
|
|
|
|
|
|
class LogLevel:
|
|
DEBUG = "DEBUG"
|
|
INFO = "INFO"
|
|
WARNING = "WARNING"
|
|
ERROR = "ERROR"
|
|
CRITICAL = "CRITICAL"
|
|
|
|
|
|
class StepType:
|
|
STABILITY_CHECK = "stability_check"
|
|
NODE_MAPPING = "node_mapping"
|
|
EXTRACT = "extract"
|
|
LOCATE_BAK = "locate_bak"
|
|
SQL_CONNECT = "sql_connect"
|
|
FILELIST = "filelist"
|
|
RESTORE = "restore"
|
|
CLEANUP = "cleanup"
|
|
SFTP_COPY = "sftp_copy"
|
|
FORWARD_ZIP = "forward_zip"
|
|
|
|
|
|
def default_seven_zip_path() -> str:
|
|
for candidate in (BUNDLED_7ZIP_EXE, BUNDLED_7ZIP_FALLBACK):
|
|
if candidate.exists():
|
|
return str(candidate)
|
|
return ""
|
|
|
|
|
|
def default_work_paths() -> dict[str, str]:
|
|
return {
|
|
"input_folder": str(DIR_ENTRADA),
|
|
"processed_folder": str(DIR_PROCESADOS),
|
|
"failed_folder": str(DIR_FALLADOS),
|
|
"extract_folder": str(DIR_TEMP),
|
|
"data_sql_folder": "",
|
|
"seven_zip_exe": default_seven_zip_path(),
|
|
}
|
|
|
|
|
|
DEFAULT_CONFIG = {
|
|
"paths": default_work_paths(),
|
|
"sql": {
|
|
"server": "localhost",
|
|
"use_windows_auth": IS_WINDOWS,
|
|
"username": "",
|
|
"password_encrypted": "",
|
|
},
|
|
"concurrency": {
|
|
"extract_workers": 1,
|
|
"restore_workers": 1,
|
|
},
|
|
"stability": {
|
|
"check_enabled": True,
|
|
"check_interval_seconds": 5,
|
|
"stable_duration_seconds": 10,
|
|
"use_ready_marker": False,
|
|
},
|
|
"timeouts": {
|
|
"extract_minutes": 30,
|
|
"restore_minutes": 60,
|
|
},
|
|
"retries": {
|
|
"max_attempts": 3,
|
|
"retry_delay_seconds": 60,
|
|
},
|
|
"features": {
|
|
"dry_run_mode": False,
|
|
"auto_scan_enabled": True,
|
|
"scan_interval_seconds": 30,
|
|
},
|
|
"panel": {
|
|
"api_url": "",
|
|
"api_token": "",
|
|
"instance_key": "",
|
|
"verify_ssl": False,
|
|
},
|
|
"retention": {
|
|
# Limpieza diaria de respaldos obsoletos para no saturar el disco del servidor.
|
|
"enabled": True,
|
|
# Procesados/: por nodo, borra los aplicados con finished_at < (ref_del_nodo - days).
|
|
"days": 2,
|
|
# Fallados/: por antigüedad absoluta, borra los más viejos que (hoy - failed_days).
|
|
"failed_days": 7,
|
|
# Hora local (0-23) a la que corre la limpieza diaria.
|
|
"run_at_hour": 3,
|
|
# Cada cuánto despierta el hilo para evaluar si toca correr.
|
|
"check_interval_seconds": 3600,
|
|
# Arranca en SECO: solo reporta qué borraría. El operador lo desactiva tras validar.
|
|
"dry_run": True,
|
|
},
|
|
}
|