ERP a la medida (Python stdlib + SQLite + vanilla JS SPA). Incluye server.py, index.html, utilidades y documentación: README, MODELO_DATOS, API, INSTALACION, CONTEXTO, NEGOCIO, WEB, ONBOARDING, VALOR_SISTEMA, CLAUDE. Secretos y datos (art4hotel.db, secret.key, ACCESOS.html, uploads/, backups/) excluidos vía .gitignore. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
123 lines
3.4 KiB
Markdown
123 lines
3.4 KiB
Markdown
# Instalación y Despliegue — Art4Hotel Hub
|
|
|
|
Sin Docker, sin pip, sin CI/CD. Solo Python 3 y `systemd`.
|
|
|
|
---
|
|
|
|
## Requisitos
|
|
|
|
- **Python 3.8+** (stdlib únicamente — `http.server`, `sqlite3`, `hashlib`, `hmac`)
|
|
- Linux con `systemd` (para correr como servicio). En Windows/Mac corre igual con `python3 server.py`.
|
|
|
|
---
|
|
|
|
## 1. Desarrollo local
|
|
|
|
```bash
|
|
git clone https://git.consultoria-as.com/consultoria-as/art4hotel-hub.git
|
|
cd art4hotel-hub
|
|
python3 server.py
|
|
# → http://localhost:4401
|
|
```
|
|
|
|
Primera vez: la app pide **crear la cuenta admin** (pantalla de setup). Genera `art4hotel.db` y `secret.key` automáticamente.
|
|
|
|
---
|
|
|
|
## 2. Despliegue en servidor
|
|
|
|
### a) Copiar archivos
|
|
|
|
```bash
|
|
scp server.py index.html backup.py usuario@servidor:/ruta/art4hotel-hub/
|
|
```
|
|
|
|
> **No copiar** `secret.key` ni `art4hotel.db` desde tu máquina local — el servidor genera los suyos en el primer arranque (o migra los existentes en producción). Los datos del negocio viven solo en el servidor.
|
|
|
|
### b) Servicio systemd
|
|
|
|
`/etc/systemd/system/art4hotel-hub.service`:
|
|
|
|
```ini
|
|
[Unit]
|
|
Description=Art4Hotel Hub
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=claude
|
|
WorkingDirectory=/ruta/art4hotel-hub
|
|
ExecStart=/usr/bin/python3 server.py
|
|
Restart=always
|
|
RestartSec=5
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
```
|
|
|
|
```bash
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable --now art4hotel-hub
|
|
sudo systemctl status art4hotel-hub
|
|
```
|
|
|
|
### c) Redespliegue (actualizar código)
|
|
|
|
```bash
|
|
scp server.py index.html usuario@servidor:/ruta/art4hotel-hub/
|
|
ssh usuario@servidor 'sudo systemctl restart art4hotel-hub'
|
|
```
|
|
|
|
Las migraciones de esquema corren solas al reiniciar (idempotentes).
|
|
|
|
---
|
|
|
|
## 3. Acceso remoto (Tailscale)
|
|
|
|
El Hub vive en una red privada **Tailscale** (MagicDNS `iclaude`):
|
|
|
|
- **Privado** (equipos en la tailnet): `http://iclaude:4401/`
|
|
- **Público** (si se requiere exponer): Tailscale **Funnel** en un puerto dedicado (443/8443/10000).
|
|
|
|
```bash
|
|
tailscale funnel --bg 4401 # expone el Hub públicamente
|
|
tailscale funnel status # ver qué está expuesto
|
|
```
|
|
|
|
> Nota de seguridad: el Hub ya tiene login propio, pero exponerlo públicamente amplía la superficie de ataque. Mantener el login activo y contraseñas fuertes.
|
|
|
|
---
|
|
|
|
## 4. Respaldos
|
|
|
|
`backup.py` por cron diario:
|
|
|
|
```cron
|
|
0 3 * * * cd /ruta/art4hotel-hub && /usr/bin/python3 backup.py
|
|
```
|
|
|
|
Hace: online backup de la DB (seguro con WAL) + `uploads.tar.gz`. Retiene 30 días en `backups/`.
|
|
También disponible bajo demanda desde la UI (`POST /api/backup-now`).
|
|
|
|
---
|
|
|
|
## 5. Reloj del servidor (importante)
|
|
|
|
El servidor de producción tiene CMOS muerto y NTP bloqueado; un reloj desfasado **rompe los logins basados en token** (Filebrowser/Immich) y firma de sesiones.
|
|
Mitigación: cron `sync_clock.sh` que toma el header `Date` de `1.1.1.1` y ajusta la hora. Verificar `date` tras reinicios.
|
|
|
|
---
|
|
|
|
## 6. Solución de problemas
|
|
|
|
| Síntoma | Causa probable | Solución |
|
|
|---|---|---|
|
|
| "Sesión inválida" constante | reloj del servidor desfasado | corregir hora; revisar `sync_clock.sh` |
|
|
| `secret.key` regenerado → todos deslogueados | se borró/cambió la clave | restaurar `secret.key`; es la clave de firma |
|
|
| Imágenes equivocadas como ejemplo | prefijos de archivo | ver lógica `first_image` en `/api/file-counts` |
|
|
| No resuelve nombres públicos | DNS de Tailscale sin upstream | `tailscale set --accept-dns=false` |
|
|
|
|
---
|
|
|
|
*Stack deliberadamente mínimo: todo el sistema cabe en una USB y corre con un solo `python3 server.py`.*
|