# πŸ—οΈ ARQUITECTURA DEL SISTEMA - Osorno Runners ## πŸ“ Diagrama de Arquitectura ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ HUGGINGFACE SPACES β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ Gradio │◄───────── app.py β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Framework β”‚ β”‚ (Python) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚Server β”‚ β”‚ β”‚ β”‚ APIs β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚Port β”‚ β”‚ β”‚ β”‚ REST β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ 7860 β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β–Ό β”‚ β–Ό β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ index.html (Frontend) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ HTML5 + CSS3 + JavaScript β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - Login Form β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - Dashboard β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - Training Plan Creator β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - Plan List β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - PDF Export (jsPDF) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β–Ό β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ SQLite Database (Persistent) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Tabla: users β”‚ β”‚ Tabla: planes β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - username β”‚ β”‚ - id β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - password β”‚ β”‚ - plan_data β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - role β”‚ β”‚ - created_at β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - name β”‚ β”‚ - created_by β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ - athlete_name β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - distance β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - race_date β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Archivo: osorno_runners.db β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ URL: https://huggingface.co/spaces/USUARIO/osorno-runners β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ HTTPS β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ NAVEGADOR β”‚ β”‚ DEL USUARIO β”‚ β”‚ β”‚ β”‚ Chrome/Firefox β”‚ β”‚ Safari/Edge β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ## πŸ”„ Flujo de Datos ### 1️⃣ **Login** ``` Usuario ingresa credenciales β”‚ β–Ό index.html (Frontend) β”‚ β”‚ POST /api/login β–Ό app.py (Backend) β”‚ β”‚ authenticate_user() β–Ό SQLite (users table) β”‚ β”‚ Return user data β–Ό Frontend muestra dashboard ``` ### 2️⃣ **Crear Plan** ``` Usuario completa formulario β”‚ β–Ό index.html valida datos β”‚ β”‚ Genera plan con algoritmos JS β–Ό CΓ‘lculos de: - DuraciΓ³n (calculateWeeks) - Kilometraje (getBaseKm) - Zonas FC (calculateHRZones) - Sesiones (generateWeekSessions) β”‚ β”‚ POST /api/save_plan β–Ό app.py (Backend) β”‚ β”‚ save_plan_to_db() β–Ό SQLite (planes table) β”‚ β”‚ Return plan ID β–Ό Frontend muestra confirmaciΓ³n ``` ### 3️⃣ **Ver Planes** ``` Usuario navega a "Ver Planes" β”‚ β”‚ GET /api/get_plans β–Ό app.py (Backend) β”‚ β”‚ get_all_plans(username, role) β–Ό SQLite query segΓΊn permisos: - User: WHERE created_by = username - Admin: SELECT * FROM planes β”‚ β”‚ Return JSON con planes β–Ό Frontend renderiza lista de planes ``` ### 4️⃣ **Exportar PDF** ``` Usuario click en "Exportar PDF" β”‚ β–Ό Frontend (jsPDF library) β”‚ β”‚ Lee datos del plan β”‚ Genera PDF en el navegador β–Ό Descarga automΓ‘tica del PDF (No involucra backend) ``` ### 5️⃣ **Eliminar Plan** (Solo Admin) ``` Admin click en "Eliminar" β”‚ β”‚ ConfirmaciΓ³n β–Ό index.html β”‚ β”‚ DELETE /api/delete_plan β–Ό app.py (Backend) β”‚ β”‚ delete_plan_from_db(plan_id) β–Ό SQLite elimina registro β”‚ β”‚ Return success β–Ό Frontend recarga lista de planes ``` --- ## πŸ” Sistema de AutenticaciΓ³n ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ authenticate_user() β”‚ β”‚ β”‚ β”‚ Input: username, password β”‚ β”‚ β”‚ β”‚ β”‚ β–Ό β”‚ β”‚ Query: SELECT role, name β”‚ β”‚ FROM users β”‚ β”‚ WHERE username = ? AND β”‚ β”‚ password = ? β”‚ β”‚ β”‚ β”‚ β”‚ β–Ό β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Found? β”‚ β”‚ β”‚ β”‚ β–Ό YES β–Ό NO β”‚ β”‚ β”‚ Return: Return: β”‚ β”‚ β”‚ { None β”‚ β”‚ β”‚ username, β”‚ β”‚ β”‚ role, β”‚ β”‚ β”‚ name β”‚ β”‚ β”‚ } β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ## πŸ’Ύ Modelo de Datos ### Tabla: **users** ```sql CREATE TABLE users ( username TEXT PRIMARY KEY, -- 'USER', 'ADMIN' password TEXT NOT NULL, -- '123' (cambiar en prod) role TEXT NOT NULL, -- 'user', 'admin' name TEXT NOT NULL -- 'Usuario', 'Administrador' ); ``` **Ejemplo de datos:** ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ username β”‚ password β”‚ role β”‚ name β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ USER β”‚ 123 β”‚ user β”‚ Usuario β”‚ β”‚ ADMIN β”‚ 123 β”‚ admin β”‚ Administrador β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ### Tabla: **planes** ```sql CREATE TABLE planes ( id INTEGER PRIMARY KEY AUTOINCREMENT, plan_data TEXT NOT NULL, -- JSON del plan completo created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_by TEXT NOT NULL, -- Username del creador athlete_name TEXT NOT NULL, -- Nombre del atleta distance TEXT NOT NULL, -- '5K', '10K', '21K', '42K' race_date TEXT NOT NULL -- Fecha de la carrera ); ``` **Ejemplo de plan_data (JSON):** ```json { "id": 1, "userData": { "name": "Rodrigo GarcΓ©s", "age": 40, "experience": "intermedio", "weight": 75, "height": 175, "imc": 24.5, "hrMax": 180, "hrRest": 60, "vo2max": 45, "distance": "42K", "targetPace": "4:30", "raceDate": "2025-04-20", "trainingDays": ["lunes", "miΓ©rcoles", "viernes", "domingo"] }, "totalWeeks": 20, "weeklyPlans": [ { "week": 1, "period": "basico", "sessions": [...], "totalKm": 35 }, ... ] } ``` --- ## πŸ”„ Ciclo de Vida de un Plan ``` 1. CREACIΓ“N β”œβ”€ Usuario completa formulario β”œβ”€ JS valida datos β”œβ”€ Algoritmos generan plan β”œβ”€ POST a /api/save_plan └─ Guarda en SQLite 2. ALMACENAMIENTO β”œβ”€ plan_data como JSON TEXT β”œβ”€ Metadata en columnas separadas └─ Timestamp automΓ‘tico 3. RECUPERACIΓ“N β”œβ”€ GET a /api/get_plans β”œβ”€ Query segΓΊn role β”œβ”€ Parse JSON └─ Return array de planes 4. VISUALIZACIΓ“N β”œβ”€ Frontend recibe JSON β”œβ”€ Renderiza HTML dinΓ‘mico └─ Muestra detalles 5. EXPORTACIΓ“N β”œβ”€ jsPDF en cliente β”œβ”€ Sin backend └─ PDF descargado 6. ELIMINACIΓ“N (Admin) β”œβ”€ DELETE a /api/delete_plan β”œβ”€ Remove de SQLite └─ Refresh lista ``` --- ## 🌐 Endpoints de la API ### **POST /api/login** ```python Request: {"username": "USER", "password": "123"} Response: {"success": true, "user": {...}} ``` ### **POST /api/save_plan** ```python Request: {"plan": "{...}", "username": "ADMIN"} Response: {"success": true, "id": 1} ``` ### **GET /api/get_plans** ```python Request: {"username": "USER", "role": "user"} Response: {"success": true, "plans": [...]} ``` ### **DELETE /api/delete_plan** ```python Request: {"plan_id": 1} Response: {"success": true} ``` --- ## πŸ”§ Stack TecnolΓ³gico ### **Frontend** - HTML5 - CSS3 (Variables CSS, Flexbox, Grid) - JavaScript (ES6+) - jsPDF 2.5.1 - Font Awesome 6.4.0 ### **Backend** - Python 3.8+ - Gradio 4.44.0 - SQLite3 (Built-in) ### **Infraestructura** - HuggingFace Spaces - CPU basic (2 vCPU, 16 GB RAM) - 50 GB Storage - HTTPS automΓ‘tico --- ## πŸ“Š MΓ©tricas de Rendimiento ``` TamaΓ±o de archivos: β”œβ”€ app.py: 7.3 KB β”œβ”€ index.html: 17.0 KB β”œβ”€ requirements.txt: 0.015 KB └─ Total: ~24.3 KB Base de datos: β”œβ”€ VacΓ­a: ~20 KB β”œβ”€ 10 planes: ~100 KB β”œβ”€ 100 planes: ~1 MB └─ LΓ­mite HF: 50 GB Tiempos de respuesta: β”œβ”€ Login: ~100 ms β”œβ”€ Crear plan: ~200 ms β”œβ”€ Cargar planes: ~150 ms └─ Exportar PDF: ~1-2 seg (cliente) ``` --- ## πŸ›‘οΈ Seguridad ### Implementado βœ… - AutenticaciΓ³n de usuarios - ValidaciΓ³n de formularios - Manejo de errores - Logs de actividad - SeparaciΓ³n de permisos ### Por Implementar πŸ”’ - Hashing de contraseΓ±as (bcrypt) - Tokens de sesiΓ³n (JWT) - Rate limiting - SanitizaciΓ³n de inputs - HTTPS forzado (HF lo hace) --- ## πŸ“ˆ Escalabilidad ### LΓ­mites Actuales - SQLite: ~140 TB teΓ³rico (prΓ‘ctico: GB) - HuggingFace: 50 GB storage - Concurrent users: ~100 - Request/min: Ilimitado en HF ### Para Escalar 1. Migrar a PostgreSQL 2. Implementar Redis para cache 3. CDN para assets 4. Load balancing 5. Microservicios --- **Esta arquitectura es ideal para:** - βœ… Prototipo rΓ‘pido - βœ… MVP (Minimum Viable Product) - βœ… Equipos pequeΓ±os (< 50 usuarios) - βœ… Presupuesto cero **Para producciΓ³n enterprise, considerar:** - Migrar a AWS/GCP/Azure - Base de datos dedicada - Backups automΓ‘ticos - Monitoreo 24/7 - CI/CD pipeline