# Segunda instancia white-label (sin branding SKEEN) Segunda clínica sobre el mismo código y la misma VPS. La marca no vive en el código: sale del parámetro `skeen.clinic.brand` (ver `GET /skeen/frontend/v1/brand`), así que esta y futuras instancias son solo infra + config. ## Mapa de la instalación | Componente | SKEEN (instancia 1) | Clínica 2 (instancia white-label) | |---|---|---| | Odoo HTTP | 8069 | **8070** | | Odoo longpolling | 8072 | **8073** | | Base de datos | `skeen_odoo` | **`clinica2_odoo`** | | Conf | `/root/skeen-odoo/odoo.conf` | **`/root/clinica2-odoo.conf`** | | Systemd | `odoo-skeen.service` | **`odoo-clinica2.service`** | | Frontend | `/var/www/skeen-frontend` | **`/var/www/clinica2-frontend`** | | Nginx | vhost `skeen-crm` (80) | **vhost `clinica2` (80 + 8880)** | | Acceso LAN | http://192.168.10.114 | **http://192.168.10.114:8880** | | Acceso público | skeen.consultoria-as.com | **clinica.consultoria-as.com (pendiente: dashboard del túnel)** | | WhatsApp (WACRM) | integrado | **oculto + bloqueado (escenario B)** | `addons_path` se comparte (read-only): `/root/skeen-odoo/addons`. Un solo código, N bases de datos. ## Cómo se levantó (orden de pasos) Los archivos canónicos están en `infra/clinica2/`: 1. `odoo.conf` — copia del de SKEEN con `db_name=clinica2_odoo`, `http_port=8070`, `longpolling_port=8073`, `data_dir=/var/lib/odoo-clinica2`. 2. Crear la base (el rol `skeen` no tiene CREATEDB; se crea como `postgres`): `CREATE DATABASE clinica2_odoo OWNER skeen ...` 3. Instalar módulos limpio (sin demo): `odoo-bin -c /root/clinica2-odoo.conf -d clinica2_odoo --without-demo=all --init=skeen_whatsapp,skeen_chat,skeen_visitas --stop-after-init --no-http` (skeen_whatsapp arrastra citas/pacientes/ventas/pagos/monedero; visitas arrastra inventario). 4. `postinstall.py` — brand neutro (`skeen.clinic.brand`), menú WACRM vacío para todos (`skeen.menu_roles`), password de admin propio + `must_change_password`, empresa renombrada. 5. `systemctl enable --now odoo-clinica2`. 6. `nginx-vhost` → `/etc/nginx/sites-enabled/clinica2` + `nginx -t && reload`. 7. Frontend: mismo build de SKEEN (`rsync -a dist/ /var/www/clinica2-frontend/`) más `public/brand/` (logos SVG neutros + favicon, versionados en el repo). ## Pendiente para acceso público (único paso manual) El túnel cloudflared corre con `--token` (remotamente administrado): el `config.yml` local NO aplica. En el dashboard de Cloudflare (Zero Trust → Networks → Tunnels → f2517233…) agregar el ingress: ```yaml - hostname: clinica.consultoria-as.com service: http://localhost:80 ``` El vhost nginx ya escucha en el 80 con ese `server_name`; al agregar el hostname el túnel enruta solo. El 8880 queda como acceso LAN permanente. ## Aislamiento de datos (escenario B: sin WhatsApp) - Sin `SUPABASE_URL` / `WACRM_API_*` / `HERMES_BRIDGE_URL` en la unit systemd (el backend respondería "Falta configuración"). - Aun así, el backend tiene fallback a `localhost:3000`; por eso el vhost nginx corta `/api/odoo/skeen/frontend/v1/wacrm/` con `return 403;` — doble candado, la instancia 2 no puede leer ni escribir las conversaciones de SKEEN aunque alguien llame la API a mano. - Menú WACRM oculto para todos los roles vía matriz (`/wacrm/*: []`). Para activar WhatsApp en la instancia 2 (escenario A) se necesita: número propio de WhatsApp Business + decisión de WACRM multi-clínica (estimar aparte). ## Fix de menús que esta fase destapó (aplica a TODAS las instancias) Las vistas XML referenciaban `skeen_citas.menu_skeen_root`, pero `skeen_citas` depende de `skeen_pacientes`: en instalación limpia de una sola pasada el xmlid aún no existe y la carga falla (la base de SKEEN se construyó por fases y por eso nunca explotó). Se movió el menú raíz a `skeen_pacientes` (módulo raíz de la jerarquía) y todas las referencias apuntan a `skeen_pacientes.menu_skeen_root`. En bases existentes se transfirió el ownership del xmlid (`UPDATE ir_model_data SET module='skeen_pacientes' …`), así actualizar no duplica el menú. Mismo tratamiento para el menú padre de Citas (`skeen_citas/views/menus.xml` carga antes que bloqueos/lista de espera). ## Operación habitual - Logs: `tail -f /tmp/odoo2.log` (instancia 2) vs `/tmp/odoo.log` (SKEEN). - Ambas instancias comparten código: un `git pull` + rebuild afecta el build de ambas solo si se vuelven a copiar los `dist/` (cada uno tiene el suyo). - Para una tercera clínica: repetir pasos 1–7 con `clinica3` (puertos 8074/8075, db `clinica3_odoo`, vhost propio) y setear su brand desde Configuración → Clínica o con `PUT /brand`.