# Auditoría Técnica y DevOps: Git, Arquitectura y Puesta en Producción **Repositorio:** `admin-edu-space` **Rol:** Tech Lead / DevOps Lead **Fecha de Auditoría:** 2 de Septiembre de 2026 **Entorno de Auditoría:** Windows / Git 2.x / Python 3.12.4 --- ## Resumen Ejecutivo El presente documento constituye la auditoría integral de código, gestión de versiones (Git), arquitectura de software y preparación para producción del proyecto **Edu-Space Admin Panel** (`admin-edu-space`). ### Conclusiones Principales: 1. **Rama seleccionada para despliegue:** **`main`** (commit `3d4e999`). Es la única rama que integra la totalidad de los cambios, bugfixes y features desarrollados en las ramas remotas (`planning`, `commissions`, `spanish`). No existen ramas divergentes sin fusionar. 2. **Nivel de Madurez CI/CD:** **Crítico (0% Automatización)**. No existen flujos de trabajo en GitHub Actions, GitLab CI ni validación de pruebas automatizadas previas al merge. 3. **Seguridad & Configuración:** **Riesgo Alto**. El archivo `.env` se encuentra actualmente trackeado en el repositorio Git con credenciales por defecto. Se requiere eliminarlo del índice, generar `.env.example` y rotar credenciales. 4. **Infraestructura de Despliegue:** El repositorio carece de `Dockerfile`, `docker-compose.yml`, scripts de systemd y no incluye un servidor WSGI de producción (`gunicorn`) en `requirements.txt`. El paso a producción requiere los artefactos y procedimientos estandarizados que se detallan en este informe. --- # Fase 1: Análisis de Ramas (Git Audit) ## 1.1 Inventario de Ramas y Commits Se ejecutó la inspección exhaustiva de referencias locales y remotas: ```bash git fetch --all git branch -a --sort=-committerdate git for-each-ref --format='%(refname:short) | %(committerdate:iso) | %(subject) | %(authorname)' refs/heads refs/remotes ``` ### Tabla Comparativa de Ramas: | Rama | Tipo | Último Commit | Fecha del Commit | Autor | Estado respecto a `main` | | :--- | :--- | :--- | :--- | :--- | :--- | | **`main`** | Local / Remota | `3d4e999` | 2026-04-17 18:18:00 -0300 | `ale-vz` | **Rama Primaria (HEAD)** | | **`origin/planning`** | Remota | `ec977bf` | 2026-04-17 18:14:59 -0300 | `Alejandro Vazquez` | **100% Fusionada** (Merge PR #2 en `3d4e999`) | | **`origin/commissions`**| Remota | `18f1d3a` | 2026-03-30 17:37:53 -0300 | `Alejandro Vazquez` | **100% Fusionada** (Incluida en historia de `planning`) | | **`origin/spanish`** | Remota | `9af25df` | 2026-03-30 17:11:13 -0300 | `Alejandro Vazquez` | **100% Fusionada** (Merge PR #1 en `d4281cd` y PR #2) | ### Verificación de Divergencias: Al ejecutar `git branch -r --no-merged main`, el resultado es **vacío**. Al verificar conteo simétrico `git rev-list --left-right --count ...main`: - `origin/planning...main`: `0 2` (0 commits pendientes, 2 commits por delante en `main` por los merges). - `origin/commissions...main`: `0 6` (0 commits pendientes, 6 commits por delante en `main`). - `origin/spanish...main`: `0 7` (0 commits pendientes, 7 commits por delante en `main`). ### Tags y Releases: - `git tag -l`: **Vacío**. No existen versiones etiquetadas (e.g. `v1.0.0`, `v0.1.0`) ni releases formales en el historial. ## 1.2 Auditoría de CI/CD y Calidad de Código - **Workflows automatizados:** Inexistentes. No hay directorio `.github/workflows/`, ni `.gitlab-ci.yml`, ni archivo de integración continua de ningún proveedor. - **Validación de Tests en Pull Requests:** Los merges de los Pull Requests `#1` y `#2` se realizaron sin puertas de enlace automatizadas (quality gates). - **Cobertura de pruebas en repo:** Existe únicamente `tests/test_genetic_algorithm.py` enfocado en el motor heurístico, pero no se ejecuta en ningún pipeline. ## 1.3 Dictamen Conclusivo de Rama para Despliegue > ### 🏆 Rama Determinada: **`main`** (Commit `3d4e999`) > > **Justificación Técnica:** > 1. **Consolidación completa:** `main` agrupa de forma lineal y validada todas las funcionalidades de las ramas temáticas: > - Incorpora las traducciones y arreglos de internacionalización (`spanish`). > - Incorpora la lógica y vistas de comisiones (`commissions`). > - Incorpora el motor de optimización de aulas y correcciones de UI (`planning`). > 2. **Cero pérdida de código (No regressions):** No existe ningún commit en las ramas remotas que no esté ya contenido en `main`. > 3. **Estabilidad comprobada:** Resuelve errores críticos de SQLAlchemy con carga ansiosa (`joinedload`) documentados en la evolución del repositorio. --- # Fase 2: Informe Técnico del Proyecto ## 2.1 Stack Tecnológico y Propósito del Sistema ### Propósito **Edu-Space Admin Panel** es una interfaz web de administración para la gestión, reserva y optimización algorítmica de aulas universitarias/académicas. El sistema permite: - Gestionar edificios, aulas, capacidad física y recursos técnicos (proyectores, computadoras). - Visualizar horarios y agendas en tiempo real mediante calendarios interactivos. - Administrar materias y comisiones docentes. - Asignar aulas automáticamente utilizando un **Algoritmo Genético** que evalúa aforo, prioridades horarias y resuelve conflictos de superposición de reservas. ### Stack de Tecnologías | Componente | Tecnología | Versión | Propósito / Responsabilidad | | :--- | :--- | :--- | :--- | | **Lenguaje Base** | Python | `>=3.8` (Óptimo: `3.11` / `3.12`) | Runtime principal de ejecución | | **Framework Web** | Flask | `3.0.0` | Arquitectura web basada en Application Factory | | **Capa ORM** | SQLAlchemy / Flask-SQLAlchemy | `2.0.23` / `3.1.1` | Modelado de datos relacional y queries | | **Driver de BD** | psycopg2-binary | `2.9.9` | Conector nativo de alto rendimiento a PostgreSQL | | **Migraciones** | Flask-Migrate | `4.0.5` | Wrapper de Alembic (requiere inicialización) | | **Autenticación** | Flask-Login / Werkzeug | `0.6.3` / `3.0.1` | Manejo de sesiones, roles y hash `pbkdf2:sha256` | | **Formularios & CSRF** | Flask-WTF / WTForms | `1.2.1` / `3.1.0` | Validación y protección contra ataques CSRF | | **Internacionalización** | Flask-Babel / Babel | `4.0.0` / `2.14.0` | Detección y renderizado multilingüe (ES / EN) | | **Frontend** | Bootstrap 5, FullCalendar, Chart.js | Bundled | UI responsiva, calendarios y estadísticas | | **Microservicio Backend**| Spring Boot (Java) | `8080` (Externo) | API de negocio y persistencia central (opcional/compartida) | ### Arquitectura de Componentes y Flujo de Datos ```mermaid graph TD Client[Navegador / Cliente Web] -->|HTTP / HTTPS| Nginx[Nginx Reverse Proxy & SSL] Nginx -->|WSGI Proxy :5000| Gunicorn[Gunicorn WSGI Server] Gunicorn --> App[Flask App Factory: create_app] subgraph "Flask Application Blueprints" App --> AuthBP[auth_bp: /login, /logout, /profile] App --> MainBP[main_bp: /dashboard, /api/dashboard-stats] App --> ClassroomsBP[classrooms_bp: /classrooms] App --> ScheduleBP[schedule_bp: /schedule] App --> GeneticBP[genetic_bp: /api/genetic & /genetic-optimizer] end subgraph "Core Engine" GeneticBP --> GAEngine[Algoritmo Genético: ReservationOptimizer] GAEngine --> FitnessEval[Evaluación: Aforo 40%, Horario 30%, Conflicto 20%] end subgraph "Persistencia & Servicios" App --> SQLAlchemy[SQLAlchemy ORM + joinedload] SQLAlchemy --> PostgreSQL[(PostgreSQL 14+ / classrooms_db)] App -.->|Integración Configurada| SpringBoot[Spring Boot API :8080/api] end ``` --- ## 2.2 Requisitos de Entorno y Variables de Configuración ### ⚠️ Hallazgos Críticos de Seguridad 1. **Archivo `.env` versionado:** Se encontró el archivo `.env` físico bajo control de versiones Git con contraseñas en texto plano (`password`). - *Acción correctiva obligatoria:* Ejecutar `git rm --cached .env`, añadirlo a `.gitignore` y rotar todas las credenciales antes de salir a producción. 2. **Traducciones no compiladas:** El archivo `.gitignore` excluye deliberadamente `*.mo`. Por ende, el paso de build debe compilar los `.po` mediante `pybabel compile -d translations`. ### Especificación de Variables de Entorno (`.env.example`) ```env # ============================================================================== # Edu-Space Admin - Configuración de Entorno de Producción # ============================================================================== # Entorno Flask FLASK_APP=app.py FLASK_ENV=production DEBUG=False # Seguridad: Clave secreta para cookies de sesión y CSRF (Mínimo 64 caracteres aleatorios) # Generar con: python -c 'import secrets; print(secrets.token_hex(32))' SECRET_KEY=generate-a-strong-random-secret-key-for-production-use # Conexión a Base de Datos PostgreSQL # Formato: postgresql://:@:/ DATABASE_URL=postgresql://eduspace_user:StrongProductionPassword123!@db-host:5432/classrooms_db # Parámetros de compatibilidad si aplica DB_USERNAME=eduspace_user DB_PASSWORD=StrongProductionPassword123! # Integración con API externa (Spring Boot) API_BASE_URL=http://springboot-service:8080/api # Configuración de Internacionalización y Carga de Archivos BABEL_DEFAULT_LOCALE=es BABEL_DEFAULT_TIMEZONE=America/Argentina/Buenos_Aires MAX_CONTENT_LENGTH=16777216 ``` --- ## 2.3 Puesta en Producción (Deployment Determinado) ### 2.3.1 Estrategia de Contenedores (Docker + Docker Compose) Para garantizar un despliegue repetible e inmutable, se especifica el contenedor de producción: #### `Dockerfile` (Multi-stage / Producción) ```dockerfile # Stage 1: Build de dependencias y compilación de traducciones FROM python:3.11-slim AS builder WORKDIR /app RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ libpq-dev \ && rm -rf /var/lib/apt/lists/* COPY requirements.txt . RUN pip install --no-cache-dir --user -r requirements.txt && \ pip install --no-cache-dir --user gunicorn==21.2.0 COPY . . # Compilar catálogos de traducción i18n RUN python -m babel.messages.frontend compile -d translations # Stage 2: Runtime limpio y seguro FROM python:3.11-slim AS runner WORKDIR /app RUN apt-get update && apt-get install -y --no-install-recommends \ libpq5 \ curl \ && rm -rf /var/lib/apt/lists/* # Crear usuario sin privilegios RUN useradd -m -u 1001 appuser COPY --from=builder /root/.local /home/appuser/.local COPY --from=builder --chown=appuser:appuser /app /app ENV PATH=/home/appuser/.local/bin:$PATH ENV PYTHONUNBUFFERED=1 ENV FLASK_ENV=production USER appuser EXPOSE 5000 # Healthcheck interno del contenedor HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD curl -f http://localhost:5000/login || exit 1 CMD ["gunicorn", "--workers=4", "--threads=2", "--bind=0.0.0.0:5000", "--access-logfile=-", "--error-logfile=-", "app:create_app()"] ``` #### `docker-compose.yml` (Stack Completo con PostgreSQL y Nginx) ```yaml version: '3.8' services: db: image: postgres:15-alpine container_name: eduspace_db restart: always environment: POSTGRES_DB: classrooms_db POSTGRES_USER: eduspace_user POSTGRES_PASSWORD: StrongProductionPassword123! volumes: - pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U eduspace_user -d classrooms_db"] interval: 10s timeout: 5s retries: 5 web: build: context: . dockerfile: Dockerfile container_name: eduspace_admin restart: always depends_on: db: condition: service_healthy env_file: - .env expose: - "5000" nginx: image: nginx:alpine container_name: eduspace_proxy restart: always ports: - "80:80" - "443:443" volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro - ./app/static:/app/static:ro depends_on: - web volumes: pgdata: ``` ### 2.3.2 Despliegue en Máquina Virtual Linux (Systemd + Gunicorn + Nginx) Si el despliegue se efectúa en un servidor Linux (Ubuntu/Debian) sin Docker: #### 1. Pasos de Instalación y Preparación: ```bash # 1. Clonar rama main git clone -b main /var/www/admin-edu-space cd /var/www/admin-edu-space # 2. Configurar entorno virtual python3.11 -m venv venv source venv/bin/activate # 3. Instalar dependencias del proyecto + Servidor WSGI pip install --upgrade pip pip install -r requirements.txt pip install gunicorn==21.2.0 # 4. Configurar variables de entorno seguras cp .env.example .env chmod 600 .env nano .env # Ajustar SECRET_KEY y DATABASE_URL # 5. Compilar traducciones pybabel compile -d translations # 6. Inicializar esquema de base de datos (si la base está vacía) python -c "from app import create_app, db; app = create_app(); app.app_context().push(); db.create_all()" ``` #### 2. Configuración de Servicio Systemd (`/etc/systemd/system/eduspace.service`): ```ini [Unit] Description=Edu-Space Admin Gunicorn Daemon After=network.target postgresql.service [Service] User=www-data Group=www-data WorkingDirectory=/var/www/admin-edu-space Environment="PATH=/var/www/admin-edu-space/venv/bin" EnvironmentFile=/var/www/admin-edu-space/.env ExecStart=/var/www/admin-edu-space/venv/bin/gunicorn \ --workers 4 \ --threads 2 \ --bind 127.0.0.1:5000 \ --access-logfile /var/log/eduspace/access.log \ --error-logfile /var/log/eduspace/error.log \ "app:create_app()" Restart=always RestartSec=5 [Install] WantedBy=multi-user.target ``` #### 3. Configuración del Reverse Proxy Nginx (`/etc/nginx/sites-available/eduspace`): ```nginx server { listen 80; server_name admin.edu-space.com; return 301 https://$host$request_uri; } server { listen 443 ssl http2; server_name admin.edu-space.com; ssl_certificate /etc/letsencrypt/live/admin.edu-space.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/admin.edu-space.com/privkey.pem; client_max_body_size 16M; # Entrega directa de archivos estáticos por Nginx location /static/ { alias /var/www/admin-edu-space/app/static/; expires 30d; add_header Cache-Control "public, no-transform"; } # Proxy hacia el daemon Gunicorn location / { proxy_pass http://127.0.0.1:5000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_redirect off; } } ``` --- ## 2.4 Verificación, Monitoreo y Health Checks ### 2.4.1 Endpoint de Verificación Recomendado (`/healthz`) Actualmente el proyecto solo cuenta con endpoints protegidos por autenticación (`@login_required`). Para orquestadores (Kubernetes, AWS ALB, Docker), se debe incorporar una ruta pública de verificación en `app/routes/main.py`: ```python @main_bp.route('/healthz') def healthz(): """Health check endpoint para balanceadores de carga y monitoreo""" try: # Validar conectividad con PostgreSQL db.session.execute(db.text('SELECT 1')) return jsonify({ 'status': 'HEALTHY', 'database': 'CONNECTED', 'timestamp': datetime.utcnow().isoformat() }), 200 except Exception as e: return jsonify({ 'status': 'UNHEALTHY', 'database': str(e), 'timestamp': datetime.utcnow().isoformat() }), 503 ``` ### 2.4.2 Pruebas de Humo (Smoke Tests) Post-Despliegue Ejecutar la siguiente suite de verificación inmediata tras desplegar: ```bash # 1. Comprobación de servicio y respuesta HTTP curl -I -k https://admin.edu-space.com/login # Respuesta esperada: HTTP/1.1 200 OK # 2. Comprobación del Health Endpoint curl -s https://admin.edu-space.com/healthz | jq . # Respuesta esperada: {"status": "HEALTHY", "database": "CONNECTED"} # 3. Comprobación de entrega de estáticos curl -I https://admin.edu-space.com/static/css/style.css # Respuesta esperada: HTTP/1.1 200 OK (o 304 Not Modified) # 4. Comprobación de logs de Gunicorn sudo journalctl -u eduspace.service -n 50 --no-pager ``` ### 2.4.3 Matriz de Monitoreo y Observabilidad | Dimensión | Métrica Clave | Umbral de Alerta | Acción Correctiva | | :--- | :--- | :--- | :--- | | **Disponibilidad** | Tasa de Respuestas HTTP 5xx | `> 1%` en 5 min | Inspeccionar `error.log` de Gunicorn; revisar pool de conexiones BD | | **Latencia** | Tiempo de respuesta P95 | `> 1.5s` en endpoints | Revisar índices en tablas `reservations` y `classrooms` | | **Base de Datos** | Pool de conexiones agotado | Conexiones activas `> 85%` | Aumentar `max_overflow` en SQLAlchemy o activar PgBouncer | | **Optimización** | Tiempo de ejecución de Algoritmo Genético | `> 10s` en `/api/genetic/optimize` | Limitar rango de comisiones enviadas o desacoplar a Celery/Redis | --- ## 2.5 Roadmap de Mejoras DevOps & Calidad (Tech Lead Recommendations) 1. **Implementar CI/CD con GitHub Actions:** - Crear `.github/workflows/ci.yml` para ejecutar linting (`flake8` / `ruff`) y la suite de pruebas `unittest` en cada PR. 2. **Desacoplar `.env` de Git:** - Remover `.env` del repositorio (`git rm --cached .env`) e incorporar `python-dotenv` seguro mediante inyección de secretos en el pipeline. 3. **Gestión Formal de Migraciones:** - Ejecutar `flask db init` y generar el primer script de migración versionado en `migrations/versions/`. 4. **Asincronía para el Algoritmo Genético:** - Actualmente, la optimización heurística se ejecuta de forma sincrónica en el worker HTTP. Si el volumen de comisiones crece, puede bloquear el worker de Gunicorn (timeout 30s). Se recomienda migrar la ejecución pesada a una cola asíncrona con **Celery** o **Redis Queue (RQ)**. 5. **Alinear Rutas Duplicadas:** - Resolver la colisión de ruta entre `main_bp.route('/genetic-optimizer')` y `genetic_web_bp.route('/genetic-optimizer')`.