Asistencia Geo — Documentación

07 — Manual DevOps (operación, despliegue y mantenimiento)

Sistema de Control de Asistencia Georeferenciado — v0.3.1 Última actualización: 2026-09-22

Este manual cubre el ciclo completo de operación del sistema: entornos, arquitectura de despliegue, build y publicación del APK, despliegue del backend, respaldo/restauración, rollback, monitoreo y resolución de incidentes conocidos.


1. Entornos

Entorno Ubicación Propósito
Producción Linode 172.238.169.174 Servicio público (https://www.intt2hub.com/ia/asistencia/)
Desarrollo (WSL2) ~/DeepSeekHarness/projects/asistencia-geo/ Código fuente, pruebas, build del APK
Docker (PostGIS) Linode, contenedor asistencia-postgis Base de datos PostgreSQL + PostGIS

2. Arquitectura de despliegue

Cliente (móvil Android / navegador)
        │  HTTPS (TLS 1.3)
        ▼
Cloudflare CDN  ────────────────────────── (edge cache; APK con no-store)
        │
        ▼
nginx (Linode, intt2hub.conf)
   ├── /ia/asistencia/   →  proxy_pass http://127.0.0.1:8095/   (uvicorn)
   ├── /ia/downloads/    →  APK estáticos versionados
   └── /                →  Odoo :8069
        │
        ▼
systemd ia-asistencia.service
   └── uvicorn app.main:app  (venv /opt/asistencia/backend, .env con secretos)
        │ asyncpg
        ▼
Docker asistencia-postgis (postgis/postgis:15-3.4 @ 127.0.0.1:5433)
        │ XML-RPC (worker APScheduler)
        ▼
Odoo 18 Community (hr.attendance)

Véase también el diagrama PNG: docs/diagrams/04-despliegue.png.


3. Build y publicación del APK (móvil)

3.1 Requisitos (en WSL2)

3.2 Firmado de release

3.3 Comandos de build

cd mobile
flutter pub get
flutter analyze          # debe quedar limpio (0 errores)
flutter test             # suite verde

# Universal (todos los ABIs)
flutter build apk --release
cp build/app/outputs/flutter-apk/app-release.apk \
   ../entregas/asistencia-geo/asistencia-geo-vX.Y.Z.apk

# Solo arm64 (APK más ligero, ~46 MB)
flutter build apk --release --target-platform android-arm64
cp build/app/outputs/flutter-apk/app-release.apk \
   ../entregas/asistencia-geo/asistencia-geo-vX.Y.Z-arm64.apk

GOTCHA: flutter build apk (universal) y --target-platform android-arm64 escriben ambos a app-release.apk; el segundo pisa al primero. Copiar cada uno a su nombre versionado inmediatamente después de cada build.

GOTCHA (versionCode): antes de publicar, verificar que pubspec.yaml tenga version: X.Y.Z+N con N mayor al último publicado. No subir el versionCode provoca que la actualización falle en el dispositivo.

3.4 Publicación en Linode

# En Linode
V=20260918_aprobaciones_v031
mkdir -p /opt/ia-intt/html/downloads/asistencia/$V
# scp de los APK + SHA256SUMS.txt + index.html al directorio versionado
cd /opt/ia-intt/html/downloads/asistencia
ln -sfn $V latest   # reapuntar symlink 'latest'

4. Despliegue del backend (FastAPI)

4.1 Copiar código a producción

# Respaldo previo (obligatorio)
ssh [email protected] 'tar czf /opt/asistencia/backup_pre_$(date +%Y%m%d_%H%M%S).tar.gz -C /opt/asistencia backend'

# Sincronizar (rsync --delete para el panel; scp para el código)
rsync -av --delete web/dist/ [email protected]:/opt/asistencia/backend/app/static/panel/
scp backend/app/**/*.py [email protected]:/opt/asistencia/backend/app/

4.2 Reiniciar el servicio

ssh [email protected] 'systemctl restart ia-asistencia.service && systemctl status ia-asistencia.service --no-pager'

4.3 Verificar

curl -s https://www.intt2hub.com/ia/asistencia/health
curl -s https://www.intt2hub.com/ia/asistencia/panel/ -o /dev/null -w '%{http_code}\n'

5. Migraciones de base de datos

El esquema se versiona en db/000_schema.sql, db/001_immutability.sql, db/002_seed.sql y db/003_approvals.sql. Las migraciones nuevas se aplican de forma idempotente (guards IF NOT EXISTS / DO $$ ... duplicate_object).

# Aplicar migración idempotente
docker exec -i asistencia-postgis psql -U asistencia -d asistencia < db/003_approvals.sql

La tabla attendance_events es inmutable (trigger trg_events_immutable bloquea UPDATE/DELETE). Cualquier corrección legal se hace insertando un evento nuevo + fila en attendance_audit_log, o una decisión en attendance_approvals.


6. Respaldo y restauración

6.1 Respaldo de base de datos

docker exec asistencia-postgis pg_dump -U asistencia -d asistencia \
  > /opt/asistencia/backup_db_$(date +%Y%m%d_%H%M%S).sql

6.2 Respaldo de código

tar czf /opt/asistencia/backup_code_$(date +%Y%m%d_%H%M%S).tar.gz -C /opt/asistencia backend

6.3 Restauración

docker exec -i asistencia-postgis psql -U asistencia -d asistencia < backup_db_*.sql
# + restaurar código + systemctl restart ia-asistencia

7. Rollback

  1. Restaurar código desde el último backup_code_*.tar.gz.
  2. Restaurar la BD desde el último backup_db_*.sql (si hubo migración).
  3. systemctl restart ia-asistencia.service.
  4. Verificar /health y /panel.

8. Monitoreo y logs

# Estado del servicio
systemctl status ia-asistencia.service

# Logs del backend
journalctl -u ia-asistencia.service -f

# Estado del contenedor PostGIS
docker ps --filter name=asistencia-postgis
docker logs --tail 50 asistencia-postgis

# Health checks
curl -s https://www.intt2hub.com/ia/asistencia/health
curl -s https://www.intt2hub.com/ia/asistencia/time

9. Seguridad / hardening


10. Incidentes conocidos (lecciones aplicadas)

# Síntoma Causa raíz Fix
1 APK no se instala (conflicto de paquete) Firma distinta / instalación residual Desinstalar app vieja + keystore de release fijo
2 App cuelga al arrancar (spinner infinito) flutter_secure_storage corrupto tras reinstalar (BadPaddingException) try/catch + .timeout(8s) en lecturas/escrituras de secure storage
3 Biometría no responde MainActivity extendía FlutterActivity; local_auth exige FlutterFragmentActivity Cambiar a FlutterFragmentActivity
4 “Token inválido” en panel ~1h Access token 30 min sin auto-refresh ACCESS_TOKEN_EXPIRE_MINUTES 480 + auto-refresh en 401
5 APK descargado “colgado” al 100% Content-Disposition: attachment + content-length Servir inline con MIME correcto, sin attachment
6 APK viejo servido pese a re-apuntar latest Cloudflare cachea latest/app-release.apk Publicar con nombre versionado + Cache-Control: no-store
7 flutter build pisa el APK arm64 con el universal Ambos escriben a app-release.apk Copiar a nombre versionado tras cada build

11. Comandos rápidos de referencia

# Repositorio
cd ~/DeepSeekHarness/projects/asistencia-geo

# Backend local (dev)
cd backend && python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt && cp .env.example .env
uvicorn app.main:app --reload        # http://127.0.0.1:8000/docs

# Base de datos local (dev)
docker compose up -d postgres

# Móvil (dev)
cd mobile && flutter pub get && flutter run

# Panel web (dev)
cd web && npm install && npm run dev

Véase también: - 01 — Arquitectura - 02 — Esquema de base de datos - 08 — Versiones y releases - 09 — Stack tecnológico - Diagramas PNG