From 2af2389294f9b344c9661a0ab42786effeda8877 Mon Sep 17 00:00:00 2001 From: consultoria-as Date: Sun, 17 May 2026 21:37:00 +0000 Subject: [PATCH] feat(manager): add remote VM support via NEXUS_SERVER_HOST - config.py: add NEXUS_SERVER_HOST env var for cross-VM deployment - health_service.py: graceful Redis failure when only localhost-bound - systemd service: document remote VM configuration - README: add dedicated 'VM separada' installation section - .env.example: new file with remote connection examples --- manager/.env.example | 25 ++++++++ manager/README.md | 84 +++++++++++++++++++++++++-- manager/config.py | 12 ++-- manager/services/health_service.py | 13 ++++- manager/systemd/nexus-manager.service | 21 ++++++- 5 files changed, 141 insertions(+), 14 deletions(-) create mode 100644 manager/.env.example diff --git a/manager/.env.example b/manager/.env.example new file mode 100644 index 0000000..4d2181e --- /dev/null +++ b/manager/.env.example @@ -0,0 +1,25 @@ +# Nexus Instance Manager — Environment Variables +# Copy to .env and fill in your values. + +# ─── Database (REQUIRED) ─────────────────────────────────────────────────── +# If manager runs on a separate VM, use the IP of the PostgreSQL server. +MASTER_DB_URL=postgresql://nexus:PASSWORD@192.168.10.91/nexus_autopartes +TENANT_DB_URL_TEMPLATE=postgresql://nexus:PASSWORD@192.168.10.91/{db_name} + +# ─── Remote Nexus Server IP (for VM-separated deployment) ────────────────── +# IP or hostname of the server running POS, Dashboard, Quart, Redis. +NEXUS_SERVER_HOST=192.168.10.91 + +# ─── Security (REQUIRED) ─────────────────────────────────────────────────── +MANAGER_JWT_SECRET=change-me-to-a-random-64-char-hex-string + +# ─── Demo Defaults ───────────────────────────────────────────────────────── +DEMO_DEFAULT_DAYS=14 +DEMO_DEFAULT_PIN=0000 + +# ─── Redis (OPTIONAL — health check only) ────────────────────────────────── +# Redis may only listen on localhost. If so, health check will show warning. +REDIS_URL=redis://192.168.10.91:6379/0 + +# ─── Internal ────────────────────────────────────────────────────────────── +POS_DIR=/home/Autopartes/pos diff --git a/manager/README.md b/manager/README.md index d6965e7..5a01ac9 100644 --- a/manager/README.md +++ b/manager/README.md @@ -35,11 +35,12 @@ manager/ │ └── index.html # Single Page App ├── scripts/ │ └── init_manager.py # Inicialización de DB + admin -└── systemd/ - └── nexus-manager.service +├── systemd/ +│ └── nexus-manager.service # Servicio systemd +└── README.md # Documentación completa ``` -## Instalación rápida +## Instalación rápida (mismo servidor) ### 1. Dependencias @@ -65,8 +66,8 @@ Esto crea: Asegúrate de que estas variables estén disponibles (en systemd o `.env`): ```bash -MASTER_DB_URL=postgresql://postgres@/nexus_autoparts -TENANT_DB_URL_TEMPLATE=postgresql://postgres@/{db_name} +MASTER_DB_URL=postgresql://postgres@localhost/nexus_autoparts +TENANT_DB_URL_TEMPLATE=postgresql://postgres@localhost/{db_name} MANAGER_JWT_SECRET=genera-un-segredo-largo-aqui POS_DIR=/home/Autopartes/pos REDIS_URL=redis://localhost:6379/0 @@ -100,6 +101,76 @@ server { } ``` +--- + +## Instalación en Máquina Virtual separada (misma red local) + +Si el manager corre en una VM diferente al servidor principal (donde está PostgreSQL + POS): + +### Requisitos de red +- PostgreSQL del servidor principal debe escuchar en `0.0.0.0:5432` (verificar `listen_addresses = '*'` en `postgresql.conf`) +- POS (5001), Dashboard (5000) y Quart (5002) ya escuchan en `0.0.0.0` por defecto +- Redis puede estar solo en `127.0.0.1`; en ese caso el health check mostrará advertencia pero no afecta el funcionamiento + +### 1. Clonar el repo en la VM + +```bash +git clone https://git.consultoria-as.com/consultoria-as/Autoparts-DB.git /home/Autopartes +cd /home/Autopartes/manager +``` + +### 2. Instalar dependencias + +```bash +pip install -r requirements.txt +``` + +### 3. Configurar variables para conexión remota + +Crea `/home/Autopartes/manager/.env` o edita el servicio systemd: + +```bash +# IP del servidor principal donde corre PostgreSQL y POS +NEXUS_SERVER_HOST=192.168.10.91 + +# PostgreSQL remoto (cambiar localhost por la IP del servidor) +MASTER_DB_URL=postgresql://nexus:PASSWORD@192.168.10.91/nexus_autoparts +TENANT_DB_URL_TEMPLATE=postgresql://nexus:PASSWORD@192.168.10.91/{db_name} + +# Redis remoto (puede no funcionar si Redis solo escucha en localhost) +REDIS_URL=redis://192.168.10.91:6379/0 + +# Seguridad +MANAGER_JWT_SECRET=genera-un-segredo-largo-aqui +POS_DIR=/home/Autopartes/pos +``` + +**Nota importante:** La VM manager no necesita una instalación completa del POS. Solo necesita: +- Los archivos de `manager/` +- Los archivos de `pos/` (para reutilizar `tenant_manager.py` y migraciones) +- Conectividad TCP al puerto 5432 del servidor principal + +### 4. Inicializar DB y admin + +```bash +cd /home/Autopartes/manager +python scripts/init_manager.py --email admin@nexus.local --password TU_PASSWORD +``` + +### 5. Systemd + +```bash +cp systemd/nexus-manager.service /etc/systemd/system/ +# Edita el archivo y cambia localhost por la IP del servidor en MASTER_DB_URL y NEXUS_SERVER_HOST +nano /etc/systemd/system/nexus-manager.service + +systemctl daemon-reload +systemctl enable nexus-manager +systemctl start nexus-manager +``` + +--- + ## Uso ### Crear una demo @@ -122,8 +193,11 @@ server { - Ve a **Migraciones** para ver la versión de schema de cada tenant - **Ejecutar todas pendientes** aplica migraciones en TODOS los tenants +--- + ## Notas de seguridad - Cambia `MANAGER_JWT_SECRET` en producción - El panel expone acciones destructivas (delete/reset); protege el acceso con firewall o VPN - Usa HTTPS en producción +- Si despliegas en VM separada, asegúrate de que el firewall del servidor principal permite conexiones desde la IP de la VM manager al puerto 5432 (PostgreSQL) diff --git a/manager/config.py b/manager/config.py index 04fc1e9..d5684a1 100644 --- a/manager/config.py +++ b/manager/config.py @@ -34,11 +34,15 @@ DEMO_DEFAULT_DAYS = int(os.environ.get("DEMO_DEFAULT_DAYS", "14")) DEMO_DEFAULT_PIN = os.environ.get("DEMO_DEFAULT_PIN", "0000") DEMO_SUBDOMAIN_PREFIX = os.environ.get("DEMO_SUBDOMAIN_PREFIX", "demo") +# ─── Remote Nexus Server (for VM-separated manager) ──────────────────────── +# Set this to the IP/hostname of the server running POS/PostgreSQL/Redis +NEXUS_SERVER_HOST = os.environ.get("NEXUS_SERVER_HOST", "127.0.0.1") + # ─── Services Health Check ───────────────────────────────────────────────── -POS_URL = os.environ.get("POS_URL", "http://127.0.0.1:5001/pos/health") -DASHBOARD_URL = os.environ.get("DASHBOARD_URL", "http://127.0.0.1:5000/") -QUART_URL = os.environ.get("QUART_URL", "http://127.0.0.1:5002/") -REDIS_URL = os.environ.get("REDIS_URL", "redis://localhost:6379/0") +POS_URL = os.environ.get("POS_URL", f"http://{NEXUS_SERVER_HOST}:5001/pos/health") +DASHBOARD_URL = os.environ.get("DASHBOARD_URL", f"http://{NEXUS_SERVER_HOST}:5000/") +QUART_URL = os.environ.get("QUART_URL", f"http://{NEXUS_SERVER_HOST}:5002/") +REDIS_URL = os.environ.get("REDIS_URL", f"redis://{NEXUS_SERVER_HOST}:6379/0") # ─── Paths ───────────────────────────────────────────────────────────────── BASE_DIR = os.path.dirname(os.path.abspath(__file__)) diff --git a/manager/services/health_service.py b/manager/services/health_service.py index 30b35a5..5b25cd7 100644 --- a/manager/services/health_service.py +++ b/manager/services/health_service.py @@ -8,7 +8,7 @@ import psycopg2 import redis from config import ( MASTER_DB_URL, REDIS_URL, POS_URL, DASHBOARD_URL, QUART_URL, - TENANT_DB_URL_TEMPLATE + TENANT_DB_URL_TEMPLATE, NEXUS_SERVER_HOST ) @@ -31,7 +31,7 @@ def check_postgresql(): def check_redis(): - """Check Redis connectivity.""" + """Check Redis connectivity. May be unreachable if Redis only binds to localhost.""" try: r = redis.from_url(REDIS_URL, socket_connect_timeout=3) info = r.info() @@ -41,6 +41,11 @@ def check_redis(): "used_memory_human": info.get("used_memory_human", "?"), "connected_clients": info.get("connected_clients", 0) } + except redis.ConnectionError: + return { + "status": "warning", + "error": "Redis unreachable. If manager runs on a separate VM, ensure Redis binds to 0.0.0.0 or a VPN interface, or that a tunnel is active." + } except Exception as e: return {"status": "error", "error": str(e)} @@ -121,6 +126,10 @@ def check_systemd_service(service_name): def get_full_health_report(): """Aggregate health report for all services.""" return { + "_meta": { + "nexus_server_host": NEXUS_SERVER_HOST, + "note": "disk/memory are local to this manager VM. PostgreSQL/HTTP checks target the remote Nexus server." + }, "postgresql": check_postgresql(), "redis": check_redis(), "pos": check_http_service("pos", POS_URL), diff --git a/manager/systemd/nexus-manager.service b/manager/systemd/nexus-manager.service index 89ec4ed..2434503 100644 --- a/manager/systemd/nexus-manager.service +++ b/manager/systemd/nexus-manager.service @@ -1,6 +1,6 @@ [Unit] Description=Nexus Instance Manager (Control Central) -After=network.target postgresql.service +After=network.target [Service] Type=simple @@ -9,13 +9,28 @@ WorkingDirectory=/home/Autopartes/manager ExecStart=/usr/local/bin/gunicorn -w 2 --threads 4 -b 0.0.0.0:5003 "app:create_app()" Restart=always RestartSec=5 + +# ─── Local Paths ─────────────────────────────────────────────────────────── Environment=PYTHONUNBUFFERED=1 Environment=PYTHONPATH=/home/Autopartes/manager:/home/Autopartes/pos +Environment=POS_DIR=/home/Autopartes/pos + +# ─── Database (UPDATE FOR REMOTE VM) ─────────────────────────────────────── +# If manager runs on a separate VM, change localhost to the IP of the +# PostgreSQL server (e.g. 192.168.10.91). Environment=MASTER_DB_URL=postgresql://postgres@localhost/nexus_autoparts Environment=TENANT_DB_URL_TEMPLATE=postgresql://postgres@localhost/{db_name} + +# ─── Remote Nexus Server IP ──────────────────────────────────────────────── +# Set to the IP/hostname of the server running POS/Dashboard/Quart/Redis. +# Leave as 127.0.0.1 if manager runs on the same server. +Environment=NEXUS_SERVER_HOST=127.0.0.1 + +# ─── Security (CHANGE THIS) ──────────────────────────────────────────────── Environment=MANAGER_JWT_SECRET=change-me-to-a-random-64-char-hex-string -Environment=POS_DIR=/home/Autopartes/pos -Environment=REDIS_URL=redis://localhost:6379/0 + +# ─── Redis (optional, health check only) ─────────────────────────────────── +Environment=REDIS_URL=redis://127.0.0.1:6379/0 [Install] WantedBy=multi-user.target