feat: Add frontend and backend initialization scripts, implement Keycloak and PostgreSQL setup

- Implemented SvelteKit frontend with authentication callback handling.
- Created demo routes and paraglide localization functionality.
- Added health check and entrypoint scripts for backend services.
- Established PostgreSQL and Keycloak initialization scripts with health checks.
- Introduced models for database schema using SQLAlchemy.
- Configured Vite and SvelteKit for development and testing environments.
- Added health check script to verify service statuses and resource usage.
- Created Docker entrypoint scripts for seamless service startup.
This commit is contained in:
2025-10-19 00:14:06 -05:00
commit 2a10d7d267
99 changed files with 10478 additions and 0 deletions

62
scripts/backend-entrypoint.sh Executable file
View File

@@ -0,0 +1,62 @@
#!/bin/bash
set -e
# Script de inicialización para el Backend FastAPI
# Espera a las dependencias y ejecuta migraciones antes de iniciar
echo "=========================================="
echo "Backend FastAPI - Inicialización"
echo "=========================================="
# Función para esperar a un puerto TCP usando Python
wait_for_tcp() {
local host=$1
local port=$2
local service=$3
local max_attempts=30
local attempt=1
echo "Esperando a que $service esté disponible en ${host}:${port}..."
while [ $attempt -le $max_attempts ]; do
if python -c "import socket; s = socket.socket(); s.settimeout(3); s.connect(('$host', $port)); s.close()" 2>/dev/null; then
echo "$service está listo y accesible"
return 0
fi
echo "$service no está listo aún... (intento $attempt/$max_attempts)"
attempt=$((attempt + 1))
sleep 2
done
echo "⚠ WARNING: $service no estuvo disponible después de $max_attempts intentos"
echo " Continuando de todas formas..."
return 0
}
# Esperar a PostgreSQL
wait_for_tcp "${CORE_DB_HOST:-postgres-a76}" "${CORE_DB_PORT:-5432}" "PostgreSQL"
# Esperar a Keycloak (HTTP por defecto usa puerto 8080)
wait_for_tcp "keycloak" "8080" "Keycloak"
# Ejecutar inicialización de base de datos si existe el script
if [ -f "/app/init_db.py" ]; then
echo "Ejecutando inicialización de base de datos..."
python init_db.py || echo "⚠ WARNING: Error en init_db.py"
echo "✓ Inicialización de base de datos completada"
fi
# Ejecutar migraciones (si usas Alembic, descomentar las siguientes líneas)
# if [ -d "/app/alembic" ]; then
# echo "Ejecutando migraciones de Alembic..."
# alembic upgrade head
# echo "✓ Migraciones completadas"
# fi
echo "=========================================="
echo "Iniciando aplicación FastAPI..."
echo "=========================================="
# Ejecutar el comando que se pasó al contenedor
exec "$@"

44
scripts/frontend-entrypoint.sh Executable file
View File

@@ -0,0 +1,44 @@
#!/bin/sh
set -e
# Script de inicialización para el Frontend SvelteKit
# Espera a que el backend esté disponible antes de iniciar
echo "=========================================="
echo "Frontend SvelteKit - Inicialización"
echo "=========================================="
# Función para esperar al backend
wait_for_backend() {
local url=$1
local max_attempts=30
local attempt=1
echo "Esperando a que el Backend esté disponible en ${url}..."
while [ $attempt -le $max_attempts ]; do
if wget -q -O /dev/null "${url}/health" 2>/dev/null; then
echo "✓ Backend está listo"
return 0
fi
echo "Backend no está listo aún... (intento $attempt/$max_attempts)"
attempt=$((attempt + 1))
sleep 3
done
echo "⚠ WARNING: Backend no estuvo disponible, continuando de todas formas"
return 0
}
# Esperar al backend
# En Docker, usamos el nombre del servicio. En desarrollo local, PUBLIC_API_URL apunta a localhost
BACKEND_HEALTH_URL="${BACKEND_INTERNAL_URL:-http://backend:8000}"
wait_for_backend "${BACKEND_HEALTH_URL}"
echo "=========================================="
echo "Iniciando aplicación SvelteKit..."
echo "=========================================="
# Ejecutar el comando que se pasó al contenedor
exec "$@"

186
scripts/health-check.sh Executable file
View File

@@ -0,0 +1,186 @@
#!/bin/bash
# Script de verificación de salud del sistema Anexo76
# Verifica que todos los servicios estén corriendo correctamente
set -e
# Colores
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
echo "========================================="
echo " Anexo76 - Verificación de Sistema"
echo "========================================="
echo ""
# Función para verificar un servicio
check_service() {
local service_name=$1
local container_name=$2
local health_url=$3
echo -e "${BLUE}Verificando ${service_name}...${NC}"
# Verificar si el contenedor existe
if ! docker ps -a --format '{{.Names}}' | grep -q "^${container_name}$"; then
echo -e "${RED}✗ Contenedor ${container_name} no existe${NC}"
return 1
fi
# Verificar si está corriendo
if ! docker ps --format '{{.Names}}' | grep -q "^${container_name}$"; then
echo -e "${RED}✗ Contenedor ${container_name} no está corriendo${NC}"
echo " Estado:"
docker ps -a --filter "name=${container_name}" --format "table {{.Names}}\t{{.Status}}"
return 1
fi
# Verificar estado de salud
health_status=$(docker inspect --format='{{.State.Health.Status}}' "${container_name}" 2>/dev/null || echo "none")
if [ "$health_status" = "healthy" ]; then
echo -e "${GREEN}${service_name} está saludable${NC}"
# Verificar URL si se proporciona
if [ -n "$health_url" ]; then
if curl -f -s "$health_url" > /dev/null 2>&1; then
echo -e "${GREEN} ✓ URL accesible: ${health_url}${NC}"
else
echo -e "${YELLOW} ⚠ URL no accesible: ${health_url}${NC}"
fi
fi
return 0
elif [ "$health_status" = "starting" ]; then
echo -e "${YELLOW}${service_name} está iniciando...${NC}"
return 1
elif [ "$health_status" = "unhealthy" ]; then
echo -e "${RED}${service_name} no está saludable${NC}"
echo " Últimos logs:"
docker logs --tail=20 "${container_name}"
return 1
else
echo -e "${YELLOW}${service_name} no tiene healthcheck configurado${NC}"
return 0
fi
}
# Función para verificar recursos
check_resources() {
echo -e "${BLUE}Verificando recursos del sistema...${NC}"
# Verificar uso de CPU y memoria
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}" \
anexo76-postgres anexo76-postgres-keycloak anexo76-keycloak anexo76-backend anexo76-frontend 2>/dev/null || true
echo ""
}
# Función para verificar redes
check_networks() {
echo -e "${BLUE}Verificando redes Docker...${NC}"
local networks=("anexo76_backend-net" "anexo76_auth-net" "anexo76_frontend-net")
local all_ok=true
for network in "${networks[@]}"; do
if docker network ls --format '{{.Name}}' | grep -q "^${network}$"; then
echo -e "${GREEN}✓ Red ${network} existe${NC}"
else
echo -e "${RED}✗ Red ${network} no existe${NC}"
all_ok=false
fi
done
echo ""
return 0
}
# Función para verificar volúmenes
check_volumes() {
echo -e "${BLUE}Verificando volúmenes Docker...${NC}"
local volumes=("anexo76_postgres_app_data" "anexo76_postgres_keycloak_data" "anexo76_keycloak_data" "anexo76_frontend_node_modules")
local all_ok=true
for volume in "${volumes[@]}"; do
if docker volume ls --format '{{.Name}}' | grep -q "^${volume}$"; then
size=$(docker volume inspect --format '{{ .Mountpoint }}' "$volume" 2>/dev/null | xargs du -sh 2>/dev/null | cut -f1 || echo "?")
echo -e "${GREEN}✓ Volumen ${volume} (${size})${NC}"
else
echo -e "${YELLOW}⚠ Volumen ${volume} no existe${NC}"
all_ok=false
fi
done
echo ""
return 0
}
# Verificar Docker
if ! command -v docker &> /dev/null; then
echo -e "${RED}✗ Docker no está instalado${NC}"
exit 1
fi
if ! docker info &> /dev/null; then
echo -e "${RED}✗ Docker daemon no está corriendo${NC}"
exit 1
fi
# Verificar servicios
echo "Verificando servicios..."
echo ""
services_ok=true
check_service "PostgreSQL App" "anexo76-postgres-a76" "" || services_ok=false
echo ""
check_service "PostgreSQL Keycloak" "anexo76-postgres-keycloak" "" || services_ok=false
echo ""
check_service "Keycloak" "anexo76-keycloak" "http://localhost:8080/" || services_ok=false
echo ""
check_service "Backend" "anexo76-backend" "http://localhost:8000/health" || services_ok=false
echo ""
check_service "Frontend" "anexo76-frontend" "http://localhost:5173" || services_ok=false
echo ""
# Verificar redes y volúmenes
check_networks
check_volumes
# Verificar recursos
check_resources
# Resumen final
echo "========================================="
if [ "$services_ok" = true ]; then
echo -e "${GREEN}✓ Todos los servicios están funcionando correctamente${NC}"
echo "========================================="
echo ""
echo "URLs de acceso:"
echo -e " ${GREEN}${NC} Frontend: ${BLUE}http://localhost:5173${NC}"
echo -e " ${GREEN}${NC} Backend: ${BLUE}http://localhost:8000${NC}"
echo -e " ${GREEN}${NC} API Docs: ${BLUE}http://localhost:8000/docs${NC}"
echo -e " ${GREEN}${NC} Keycloak: ${BLUE}http://localhost:8080${NC}"
echo ""
exit 0
else
echo -e "${YELLOW}⚠ Algunos servicios tienen problemas${NC}"
echo "========================================="
echo ""
echo "Comandos útiles para diagnóstico:"
echo " • Ver logs de todos: docker-compose logs -f"
echo " • Ver logs servicio: docker-compose logs -f [servicio]"
echo " • Reiniciar servicio: docker-compose restart [servicio]"
echo " • Estado completo: docker-compose ps"
echo ""
exit 1
fi

50
scripts/keycloak-entrypoint.sh Executable file
View File

@@ -0,0 +1,50 @@
#!/bin/bash
set -e
# Script de inicialización para Keycloak
# Espera a que PostgreSQL esté completamente listo antes de iniciar
echo "=========================================="
echo "Keycloak - Inicialización"
echo "=========================================="
# Función para esperar a PostgreSQL
wait_for_postgres() {
local host=$1
local port=$2
local user=$3
local db=$4
local max_attempts=60
local attempt=1
echo "Esperando a que PostgreSQL esté disponible en ${host}:${port}..."
while [ $attempt -le $max_attempts ]; do
if pg_isready -h "$host" -p "$port" -U "$user" > /dev/null 2>&1; then
# PostgreSQL acepta conexiones, verificar que la base de datos existe
if psql -h "$host" -p "$port" -U "$user" -d "$db" -c "SELECT 1" > /dev/null 2>&1; then
echo "✓ PostgreSQL está listo y la base de datos '$db' existe"
return 0
else
echo "PostgreSQL está listo pero la base de datos '$db' no existe aún... (intento $attempt/$max_attempts)"
fi
else
echo "PostgreSQL no está listo aún... (intento $attempt/$max_attempts)"
fi
attempt=$((attempt + 1))
sleep 2
done
echo "✗ ERROR: PostgreSQL no estuvo disponible después de $max_attempts intentos"
exit 1
}
# Esperar a que PostgreSQL esté listo
wait_for_postgres "${KC_DB_URL_HOST:-postgres-keycloak}" "${KC_DB_URL_PORT:-5432}" "${KC_DB_USERNAME:-postgres}" "${KC_DB_URL_DATABASE:-keycloak}"
echo "Iniciando Keycloak..."
echo "=========================================="
# Ejecutar el comando original de Keycloak
exec /opt/keycloak/bin/kc.sh "$@"

View File

@@ -0,0 +1,30 @@
#!/bin/bash
set -e
echo "=========================================="
echo "PostgreSQL App - Inicialización"
echo "=========================================="
# Este script es ejecutado por docker-entrypoint-initdb.d
# PostgreSQL ya está iniciado por el contenedor padre
echo "✓ PostgreSQL está listo (iniciado por contenedor)"
# La base de datos anexo76_core ya está creada por POSTGRES_DB
echo "✓ Base de datos 'anexo76_core' ya configurada"
# Crear extensiones y esquemas
echo "Creando extensiones y esquemas..."
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pg_trgm";
CREATE SCHEMA IF NOT EXISTS a76;
CREATE SCHEMA IF NOT EXISTS a22;
CREATE SCHEMA IF NOT EXISTS a24;
CREATE SCHEMA IF NOT EXISTS a31;
EOSQL
echo "✓ Extensiones y esquemas creados correctamente"
echo "=========================================="
echo "PostgreSQL App - Inicialización completada"
echo "=========================================="

View File

@@ -0,0 +1,14 @@
#!/bin/bash
set -e
# Este script se ejecuta automáticamente en la primera inicialización de PostgreSQL
# cuando se coloca en /docker-entrypoint-initdb.d/
echo "Inicializando base de datos keycloak..."
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL
-- Verificar que la base de datos existe
SELECT 'Base de datos keycloak lista' AS status;
EOSQL
echo "✓ Base de datos keycloak inicializada correctamente"