Este repositorio sostiene el portfolio público y el pequeño sistema que lo alimenta. La web no habla directamente con GitHub ni con el worker: el navegador solo consume la API Rust. El resto queda detrás, con responsabilidades separadas.
| Servicio | Responsabilidad |
|---|---|
| Nuxt | Renderiza el portfolio y el panel técnico de /system. |
| Rust / Axum | Expone los datos públicos, recibe el formulario de contacto y agrega el estado operativo. |
| Python / FastAPI | Ejecuta tareas internas, entre ellas la sincronización de GitHub. |
| PostgreSQL | Guarda proyectos, repositorios, relaciones y el historial de sincronizaciones. |
La API Rust es la única capa que se expone al frontend. Python no forma parte de la ruta de navegación de un visitante.
GET /system/status es una fotografía breve del estado del servicio. Ejecuta
las comprobaciones en paralelo con Tokio para que una dependencia lenta no
retrase innecesariamente al resto:
- Consulta los pools reader y writer de PostgreSQL con
SELECT 1. - Pide el health check del worker Python.
- Lee el último job de GitHub y los contadores de repositorios/lenguajes desde
PostgreSQL. Esta consulta usa
writer_db, que es el rol con el que se mantienen los datos de sincronización. - Comprueba SMTP solo si está configurado.
La respuesta no contiene métricas de visitantes ni perfiles de usuario. Son señales de la propia infraestructura: disponibilidad, latencia, uptime y estado del último proceso interno.
Rust realiza una sola request ligera al worker:
GET /health
Accept: application/jsonPython debe responder rápido, sin iniciar una sincronización ni consultar
recursos externos. El contrato mínimo es un HTTP 2xx con este cuerpo:
{
"status": "ok"
}También se acepta "healthy". Se pueden añadir campos como service,
version o scheduler; Rust los ignora para no acoplar ambos servicios.
El requester HTTP está en backends/rust-api/src/services/web_requester.rs.
Lee Content-Length en vez de esperar al cierre del socket, respeta un timeout
global y prueba las direcciones IPv6 e IPv4 que resuelva un host. Esto es
importante en local, donde Uvicorn suele escuchar solo en 127.0.0.1.
El worker de Python es quien habla con GitHub. Recoge repositorios, propietarios,
colaboradores, temas y lenguajes; después actualiza las tablas del esquema
github. La API Rust no dispara ese trabajo: únicamente expone su último estado
cuando está disponible en github.sync_jobs.
Un github.status igual a unavailable significa que la consulta del historial
no se ha podido completar. No implica necesariamente que GitHub esté caído. Un
estado unknown o degraded con cero jobs suele indicar que todavía no existe
una ejecución registrada.
Las ejecuciones iniciadas por un visitante desde /github son independientes:
Python las registra en github.job_runs; las ejecuciones diarias internas
siguen en github.sync_jobs. En una base de datos ya creada hay que aplicar
una vez backends/postgres/migrations/001_public_job_runs.sql antes de desplegar
la API pública.
El portfolio permite a los visitantes sincronizar manualmente el perfil y los
repositorios desde /github. Las ejecuciones son independientes de los jobs
interiores y quedan registradas en github.job_runs.
El progreso de la sincronización se transmite en tiempo real mediante SSE, sin esperas silenciosas. El frontend recibe actualizaciones de cada fase: validación de acceso, consulta del perfil, carga de repositorios, topics, contributors, lenguajes, persistencia en PostgreSQL y finalización.
El modal de sincronización mantiene la coherencia visual del portfolio. El flujo es intuitivo: el usuario elige la tarea (perfil, repositorios o ambos), ve el progreso en vivo y puede regresar a la elección inicial al cerrar sin perder el estado del job activo en el servidor.
Copia los valores necesarios a backends/.env. Para ejecutar el worker fuera de
Docker con Uvicorn en el puerto 8080:
PYTHON_API_URL=http://127.0.0.1:8080En Docker, el Dockerfile del worker usa el puerto 8001 dentro de su red:
PYTHON_API_URL=http://python-api:8001Las credenciales de PostgreSQL, SMTP y GitHub viven solo en el .env local; no
deben añadirse al repositorio.
Con contenedores:
docker compose up --buildEn local, cada proceso puede arrancarse por separado:
# API Rust
cd backends/rust-api
cargo run
# Worker Python
cd backends/python-api
uvicorn app:app --port 8080
# Frontend
cd frontend
npm run devEl estado agregado se puede comprobar con:
curl http://127.0.0.1:8000/system/statusLa API Rust expone dos probes distintos:
GET /health: liveness. Solo confirma que el proceso HTTP está vivo.GET /health/ready: readiness. Devuelve200únicamente cuando los pools reader y writer de PostgreSQL responden; devuelve503al comenzar el apagado ordenado o si la base de datos no está disponible.
El Dockerfile comprueba /health/ready automáticamente. En Coolify, si has
configurado un health check HTTP manual, usa la ruta /health/ready y el puerto
8000. La aplicación atiende SIGTERM (Coolify/Docker) y SIGINT: primero se
marca como no preparada para recibir tráfico y después Axum deja terminar las
peticiones que ya están en curso.
cd backends/rust-api
cargo fmt -- --check
cargo test --offline web_requester::tests
cargo check --offline
cd ../../frontend
npm run buildLos SQL situados en backends/postgres/hardcoded_data se cargan una vez creada
la estructura. Los proyectos deben referenciar github_repository_github_id:
un trigger resuelve el id interno de PostgreSQL cuando el repositorio ya está
sincronizado.