feat: plantilla base workspace SaaS
Convierte el repositorio de Anexo76 en una plantilla limpia y reutilizable para nuevos proyectos del ecosistema Workspace de Aduanasoft. Cambios principales: - Elimina módulos específicos de Anexo76: a76, a24, sitar, public - Agrega módulo example/ con patrón CRUD de referencia (models/dto/service/routes) - Limpia migraciones Alembic: solo quedan las 6 de core (users, tenants, permissions) - Reemplaza todas las rutas del dashboard con stubs genéricos - Elimina lógica de negocio aduanera: shortcuts, CSV imports, permisos, catálogos - Simplifica variables de entorno: una sola WORKSPACE_URL deriva Hub y Keycloak - Agrega scripts/auth-mode.sh para alternar entre auth local y workspace - Configura docker-compose con nombres genéricos (app-*) - Corrige flujo SSO: elimina system-gate SCAF/SCAII que bloqueaba el login - Modo DEV_LOCAL_AUTH para desarrollo sin Keycloak ni Hub Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
214
scripts/auth-mode.sh
Executable file
214
scripts/auth-mode.sh
Executable file
@@ -0,0 +1,214 @@
|
||||
#!/bin/bash
|
||||
# Alterna entre auth local (sin Keycloak/Hub) y auth workspace.
|
||||
#
|
||||
# Uso:
|
||||
# ./auth-mode.sh → muestra modo actual
|
||||
# ./auth-mode.sh local → activa login local sin workspace
|
||||
# ./auth-mode.sh workspace → configura y activa conexión con workspace
|
||||
|
||||
set -e
|
||||
|
||||
# Siempre operar sobre el .env en la raíz del proyecto (un nivel arriba de scripts/)
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
RED='\033[0;31m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
ENV_FILE=".env"
|
||||
|
||||
# ── Helpers ────────────────────────────────────────────────────────────────────
|
||||
|
||||
get_env() {
|
||||
grep -E "^${1}=" "$ENV_FILE" 2>/dev/null | cut -d'=' -f2- | tr -d '"' | tr -d "'"
|
||||
}
|
||||
|
||||
set_env() {
|
||||
local key="$1" value="$2"
|
||||
if grep -qE "^${key}=" "$ENV_FILE" 2>/dev/null; then
|
||||
sed -i "s|^${key}=.*|${key}=${value}|" "$ENV_FILE"
|
||||
else
|
||||
echo "${key}=${value}" >> "$ENV_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
ask() {
|
||||
# ask "Pregunta" "default" → imprime en stderr, devuelve valor por stdout
|
||||
local prompt="$1" default="$2" answer
|
||||
echo -en "${CYAN} ${prompt}${default:+ [${default}]}: ${NC}" >&2
|
||||
read -r answer
|
||||
echo "${answer:-$default}"
|
||||
}
|
||||
|
||||
restart_services() {
|
||||
echo ""
|
||||
echo -en "${YELLOW} ¿Reiniciar backend y frontend ahora? [S/n]: ${NC}"
|
||||
read -r ans
|
||||
if [[ "${ans,,}" != "n" ]]; then
|
||||
echo -e "${BLUE} Reiniciando servicios...${NC}"
|
||||
docker compose up -d --force-recreate backend frontend
|
||||
echo -e "${GREEN} ✓ Listo — espera unos segundos a que levanten.${NC}"
|
||||
else
|
||||
echo -e "${YELLOW} Reinicia manualmente cuando estés listo:${NC}"
|
||||
echo -e " ${BLUE}docker compose up -d --force-recreate backend frontend${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# check_url "Etiqueta" "url" → HEAD/GET con timeout corto; reporta OK / código / sin respuesta
|
||||
check_url() {
|
||||
local label="$1" url="$2"
|
||||
if [ -z "$url" ]; then
|
||||
echo -e " ${YELLOW}∅${NC} ${label}: sin URL configurada"
|
||||
return
|
||||
fi
|
||||
local code
|
||||
code=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 --max-time 8 "$url" 2>/dev/null || echo "000")
|
||||
if [ "$code" = "000" ]; then
|
||||
echo -e " ${RED}✗${NC} ${label}: sin respuesta (${url})"
|
||||
elif [ "$code" -ge 200 ] && [ "$code" -lt 500 ]; then
|
||||
echo -e " ${GREEN}✓${NC} ${label}: responde (HTTP ${code})"
|
||||
else
|
||||
echo -e " ${YELLOW}!${NC} ${label}: HTTP ${code} (${url})"
|
||||
fi
|
||||
}
|
||||
|
||||
# Valida conectividad al Hub y Keycloak desde la máquina host.
|
||||
validate_workspace_connection() {
|
||||
local hub="$1" kc="$2" realm="$3"
|
||||
echo ""
|
||||
echo -e "${BOLD} Validando conexión al workspace...${NC}"
|
||||
echo -e "${BLUE} ──────────────────────────────────────${NC}"
|
||||
|
||||
# Hub: probar la raíz pública
|
||||
check_url "Hub" "${hub%/}"
|
||||
|
||||
# Keycloak: el endpoint OIDC well-known del realm confirma que el realm existe
|
||||
local realm_name="${realm:-master}"
|
||||
check_url "Keycloak (realm ${realm_name})" "${kc%/}/realms/${realm_name}/.well-known/openid-configuration"
|
||||
|
||||
echo -e "${BLUE} ──────────────────────────────────────${NC}"
|
||||
echo -e " ${YELLOW}Recordatorio:${NC} validar conexión no equivale a estar registrado."
|
||||
echo -e " Para entrar desde el workspace, el equipo del Hub debe:"
|
||||
echo -e " • Registrar esta app en el Hub (App Launcher)."
|
||||
echo -e " • Agregar tu redirect URI en el client de Keycloak:"
|
||||
echo -e " ${BLUE}<URL pública de esta app>/auth/callback${NC}"
|
||||
}
|
||||
|
||||
# ── Crear .env si no existe ────────────────────────────────────────────────────
|
||||
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
[ -f ".env.example" ] && cp .env.example "$ENV_FILE" || touch "$ENV_FILE"
|
||||
echo -e "${GREEN}✓ .env creado${NC}"
|
||||
fi
|
||||
|
||||
# ── Estado actual ──────────────────────────────────────────────────────────────
|
||||
|
||||
current_local=$(get_env "DEV_LOCAL_AUTH")
|
||||
is_local=$([[ "${current_local,,}" == "true" ]] && echo "yes" || echo "no")
|
||||
|
||||
show_status() {
|
||||
echo ""
|
||||
if [ "$is_local" = "yes" ]; then
|
||||
echo -e " ${BOLD}Modo:${NC} ${YELLOW}LOCAL${NC} (sin Keycloak/Hub)"
|
||||
echo -e " ${BOLD}Acceso:${NC} ${BLUE}http://localhost:5173/login${NC} → 'Entrar como dev'"
|
||||
else
|
||||
local ws=$(get_env "WORKSPACE_URL")
|
||||
local client=$(get_env "KEYCLOAK_CLIENT_ID")
|
||||
echo -e " ${BOLD}Modo:${NC} ${GREEN}WORKSPACE${NC} (Keycloak + Hub)"
|
||||
echo -e " ${BOLD}Workspace:${NC} ${ws:-no configurado}"
|
||||
echo -e " ${BOLD}Keycloak:${NC} ${ws:+${ws}/kcauth} (realm $(get_env KEYCLOAK_REALM), client ${client:-?})"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ── Comando ────────────────────────────────────────────────────────────────────
|
||||
|
||||
MODE="${1:-status}"
|
||||
|
||||
case "$MODE" in
|
||||
|
||||
# ── LOCAL ──────────────────────────────────────────────────────────────────────
|
||||
local)
|
||||
echo -e "\n${BOLD}Activando modo LOCAL${NC}"
|
||||
echo -e "${YELLOW}──────────────────────────────────────────${NC}"
|
||||
|
||||
set_env "DEV_LOCAL_AUTH" "True"
|
||||
is_local="yes"
|
||||
|
||||
# SECRET_KEY — necesario para firmar el JWT local
|
||||
current_key=$(get_env "SECRET_KEY")
|
||||
if [ -z "$current_key" ] || [ "$current_key" = "change-this-secret-key-in-production" ]; then
|
||||
secret=$(openssl rand -hex 32 2>/dev/null || echo "dev-$(date +%s | md5sum | head -c 32)")
|
||||
set_env "SECRET_KEY" "$secret"
|
||||
echo -e " ${GREEN}✓${NC} SECRET_KEY generado"
|
||||
fi
|
||||
|
||||
# BACKEND_URL — URL interna del backend para que el frontend llame al dev-login
|
||||
current_backend=$(get_env "BACKEND_URL")
|
||||
if [ -z "$current_backend" ]; then
|
||||
set_env "BACKEND_URL" "http://backend:8000"
|
||||
echo -e " ${GREEN}✓${NC} BACKEND_URL=http://backend:8000"
|
||||
fi
|
||||
|
||||
echo -e " ${GREEN}✓${NC} DEV_LOCAL_AUTH=True"
|
||||
show_status
|
||||
restart_services
|
||||
;;
|
||||
|
||||
# ── WORKSPACE ─────────────────────────────────────────────────────────────────
|
||||
workspace)
|
||||
echo -e "\n${BOLD}Configurando modo WORKSPACE${NC}"
|
||||
echo -e "${BLUE}──────────────────────────────────────────${NC}"
|
||||
echo -e " Hub y Keycloak se derivan de WORKSPACE_URL."
|
||||
echo -e " Presiona Enter para mantener el valor actual.\n"
|
||||
|
||||
# Las dos únicas preguntas necesarias:
|
||||
current_ws=$(get_env "WORKSPACE_URL")
|
||||
ws_url=$(ask "URL del workspace" "${current_ws:-https://workspace.aduanasoft.com}")
|
||||
ws_url="${ws_url%/}"
|
||||
|
||||
current_realm=$(get_env "KEYCLOAK_REALM")
|
||||
kc_realm=$(ask "Keycloak Realm" "${current_realm:-master}")
|
||||
|
||||
current_client=$(get_env "KEYCLOAK_CLIENT_ID")
|
||||
kc_client=$(ask "Keycloak Client ID" "${current_client:-app-frontend}")
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE} Aplicando configuración...${NC}"
|
||||
|
||||
set_env "WORKSPACE_URL" "$ws_url"
|
||||
set_env "KEYCLOAK_REALM" "$kc_realm"
|
||||
set_env "KEYCLOAK_CLIENT_ID" "$kc_client"
|
||||
set_env "DEV_LOCAL_AUTH" "False"
|
||||
is_local="no"
|
||||
|
||||
echo -e " ${GREEN}✓${NC} Configuración guardada en .env"
|
||||
|
||||
# Validar conectividad (Hub = WORKSPACE_URL, Keycloak = WORKSPACE_URL/kcauth)
|
||||
validate_workspace_connection "$ws_url" "${ws_url}/kcauth" "$kc_realm"
|
||||
|
||||
show_status
|
||||
restart_services
|
||||
;;
|
||||
|
||||
# ── STATUS ─────────────────────────────────────────────────────────────────────
|
||||
status|"")
|
||||
echo -e "${BOLD}[auth-mode] Estado actual${NC}"
|
||||
show_status
|
||||
echo "Comandos:"
|
||||
echo -e " ${GREEN}./auth-mode.sh local${NC} → Login local, sin workspace"
|
||||
echo -e " ${GREEN}./auth-mode.sh workspace${NC} → Configurar y conectar al workspace"
|
||||
echo ""
|
||||
;;
|
||||
|
||||
*)
|
||||
echo -e "${RED}Uso: $0 [local|workspace|status]${NC}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -1,65 +0,0 @@
|
||||
#!/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 python3 -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 al Hub de Aduanasoft (opcional, pero recomendado)
|
||||
# Extraer host y puerto de HUB_URL si es posible, o usar el default del docker-compose
|
||||
# HUB_HOST=$(echo $HUB_URL | sed -e 's|http://||' -e 's|:.*||')
|
||||
# HUB_PORT=$(echo $HUB_URL | sed -e 's|.*:||' -e 's|/.*||')
|
||||
# wait_for_tcp "${HUB_HOST:-host.docker.internal}" "${HUB_PORT:-8001}" "Aduanasoft Hub"
|
||||
|
||||
# Ejecutar migraciones de Alembic
|
||||
#if [ -d "/app/alembic" ]; then
|
||||
# echo "Ejecutando migraciones de Alembic..."
|
||||
# alembic upgrade head || {
|
||||
# echo "⚠ WARNING: Error al ejecutar migraciones"
|
||||
# echo " Verificando estado de la base de datos..."
|
||||
# alembic current || echo " No se pudo determinar la versión actual"
|
||||
# }
|
||||
# echo "✓ Migraciones completadas"
|
||||
#else
|
||||
# echo "⚠ WARNING: Directorio /app/alembic no encontrado"
|
||||
# echo " Las migraciones de base de datos no se ejecutaron"
|
||||
#fi
|
||||
|
||||
echo "=========================================="
|
||||
echo "Iniciando aplicación FastAPI..."
|
||||
echo "=========================================="
|
||||
|
||||
# Ejecutar el comando que se pasó al contenedor
|
||||
exec "$@"
|
||||
@@ -1,186 +0,0 @@
|
||||
#!/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/api/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/api${NC}"
|
||||
echo -e " ${GREEN}•${NC} API Docs: ${BLUE}http://localhost:8000/api/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
|
||||
@@ -1,584 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
###############################################################################
|
||||
# Script de inicialización completa para Anexo76
|
||||
#
|
||||
# Modos de operación:
|
||||
# --hub-mode Usa el Hub de Aduanasoft como fuente de verdad (recomendado).
|
||||
# El Hub ya tiene el tenant provisionado con licencia.
|
||||
# Solo registra el usuario y crea la empresa.
|
||||
# --standalone-mode Modo legado: configura Keycloak directamente (sin Hub).
|
||||
# Para entornos donde no hay Hub disponible.
|
||||
#
|
||||
# Si no se especifica modo, se detecta automáticamente:
|
||||
# - Si el Hub responde en HUB_URL → hub-mode
|
||||
# - Si no → standalone-mode
|
||||
#
|
||||
# Uso:
|
||||
# ./init_first_time.sh [--hub-mode|--standalone-mode] [--seed-data]
|
||||
#
|
||||
# Variables de entorno relevantes (con defaults):
|
||||
# HUB_URL URL del Hub (default: http://localhost:8001)
|
||||
# BACKEND_URL URL del backend anexo76 (default: http://localhost:8000)
|
||||
# TENANT_SLUG Slug del tenant en el Hub (default: aduanasoft)
|
||||
# USER_USERNAME Usuario a crear/registrar (default: admin)
|
||||
# USER_PASSWORD Contraseña del usuario (default: Admin1234!)
|
||||
# USER_EMAIL Email del usuario (default: admin@<tenant_slug>.com)
|
||||
# COMPANY_NAME Nombre de la empresa (default: derivado del slug)
|
||||
# COMPANY_RFC RFC de la empresa (default: vacío)
|
||||
###############################################################################
|
||||
|
||||
set -euo pipefail
|
||||
trap 'echo -e "\n${RED}✗ Error en línea $LINENO. Script abortado.${NC}" >&2' ERR
|
||||
|
||||
# ─── Parsear argumentos ────────────────────────────────────────────────────────
|
||||
SEED_DATA=false
|
||||
FORCE_MODE="" # "hub" | "standalone" | "" (auto)
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--hub-mode) FORCE_MODE="hub"; shift ;;
|
||||
--standalone-mode) FORCE_MODE="standalone"; shift ;;
|
||||
--seed-data) SEED_DATA=true; shift ;;
|
||||
*)
|
||||
echo "Uso: $0 [--hub-mode|--standalone-mode] [--seed-data]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ─── Colores ───────────────────────────────────────────────────────────────────
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
# ─── Configuración con defaults ────────────────────────────────────────────────
|
||||
HUB_URL="${HUB_URL:-http://localhost:8001}"
|
||||
BACKEND_URL="${BACKEND_URL:-http://localhost:8000}"
|
||||
TENANT_SLUG="${TENANT_SLUG:-aduanasoft}"
|
||||
USER_USERNAME="${USER_USERNAME:-admin}"
|
||||
USER_PASSWORD="${USER_PASSWORD:-Admin1234!}"
|
||||
USER_EMAIL="${USER_EMAIL:-${USER_USERNAME}@${TENANT_SLUG}.com}"
|
||||
USER_FIRSTNAME="${USER_FIRSTNAME:-Admin}"
|
||||
USER_LASTNAME="${USER_LASTNAME:-$(echo "${TENANT_SLUG^}")}"
|
||||
COMPANY_NAME="${COMPANY_NAME:-$(echo "${TENANT_SLUG^} S.A. de C.V.")}"
|
||||
COMPANY_RFC="${COMPANY_RFC:-}"
|
||||
|
||||
# Configuración Postgres (para seed data)
|
||||
POSTGRES_HOST="${POSTGRES_HOST:-localhost}"
|
||||
POSTGRES_DB="${POSTGRES_DB:-anexo76_core}"
|
||||
POSTGRES_USER="${POSTGRES_USER:-postgres}"
|
||||
POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-postgres}"
|
||||
|
||||
# Variables que se llenarán durante la ejecución
|
||||
TENANT_ID=""
|
||||
COMPANY_ID=""
|
||||
ACCESS_TOKEN=""
|
||||
|
||||
###############################################################################
|
||||
# Dependencias
|
||||
###############################################################################
|
||||
check_dependencies() {
|
||||
local missing=()
|
||||
command -v jq >/dev/null 2>&1 || missing+=("jq")
|
||||
command -v curl >/dev/null 2>&1 || missing+=("curl")
|
||||
command -v docker>/dev/null 2>&1 || missing+=("docker")
|
||||
if [[ ${#missing[@]} -ne 0 ]]; then
|
||||
echo -e "${RED}✗ Dependencias faltantes: ${missing[*]}${NC}" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
check_dependencies
|
||||
|
||||
###############################################################################
|
||||
# Helpers SQL (usados por seed data)
|
||||
###############################################################################
|
||||
exec_pg_sql() {
|
||||
docker exec -e PGPASSWORD="${POSTGRES_PASSWORD}" anexo76-postgres-a76 \
|
||||
psql -h localhost -p 5432 -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" \
|
||||
-t -c "$1" 2>&1
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# MODO HUB — El Hub es la fuente de verdad
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
###############################################################################
|
||||
|
||||
###############################################################################
|
||||
# Inicialización vía Hub
|
||||
###############################################################################
|
||||
hub_mode_init() {
|
||||
echo -e "${CYAN}Modo: Hub (fuente de verdad)${NC}"
|
||||
echo -e "${CYAN}Hub URL: ${HUB_URL}${NC}"
|
||||
echo ""
|
||||
|
||||
# ── 1. Esperar Hub ─────────────────────────────────────────────────────────
|
||||
echo -e "${YELLOW}[1/4] Esperando al Hub...${NC}"
|
||||
local retries=20
|
||||
local hub_ok=false
|
||||
while [[ $retries -gt 0 ]]; do
|
||||
for path in /health /api/health /api/v1/health ""; do
|
||||
if curl -sf --max-time 3 "${HUB_URL}${path}" >/dev/null 2>&1; then
|
||||
hub_ok=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
[[ "${hub_ok}" == "true" ]] && break
|
||||
retries=$((retries - 1))
|
||||
if [[ $retries -eq 0 ]]; then
|
||||
echo -e "${RED}✗ Hub no disponible en ${HUB_URL}${NC}"
|
||||
echo -e "${YELLOW} Verifica que el Hub esté corriendo. Desde el directorio del Hub:${NC}"
|
||||
echo -e " cd ~/dev/aduanasoft-hub && docker compose up -d"
|
||||
exit 1
|
||||
fi
|
||||
sleep 3
|
||||
done
|
||||
echo -e "${GREEN}✓ Hub disponible${NC}"
|
||||
|
||||
# ── 2. Verificar que el usuario exista (creado desde el Hub) ─────────────
|
||||
echo -e "\n${YELLOW}[2/4] El usuario debe estar creado previamente desde el Hub.${NC}"
|
||||
echo -e "${YELLOW} El registro requiere un token de invitación gestionado por el Hub.${NC}"
|
||||
echo -e "${CYAN} Usuario esperado: ${USER_USERNAME} / tenant: ${TENANT_SLUG}${NC}"
|
||||
|
||||
# ── 3. Login para obtener token y crear empresa ────────────────────────────
|
||||
echo -e "\n${YELLOW}[3/4] Login y creación de empresa en Anexo76...${NC}"
|
||||
|
||||
# Esperar a que el backend esté listo
|
||||
local be_retries=20
|
||||
while [[ $be_retries -gt 0 ]]; do
|
||||
if curl -sf --max-time 3 "${BACKEND_URL}/api/health" >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
be_retries=$((be_retries - 1))
|
||||
[[ $be_retries -eq 0 ]] && { echo -e "${RED}✗ Backend no disponible en ${BACKEND_URL}${NC}"; exit 1; }
|
||||
sleep 3
|
||||
done
|
||||
|
||||
local login_response
|
||||
login_response=$(curl -s -X POST "${BACKEND_URL}/api/v1/auth/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"username\":\"${USER_USERNAME}\",\"password\":\"${USER_PASSWORD}\",\"tenant_slug\":\"${TENANT_SLUG}\"}")
|
||||
|
||||
ACCESS_TOKEN=$(echo "${login_response}" | jq -r '.access_token // empty')
|
||||
if [[ -z "${ACCESS_TOKEN}" ]]; then
|
||||
echo -e "${RED}✗ No se pudo obtener token. Respuesta:${NC}"
|
||||
echo "${login_response}" | python3 -m json.tool 2>/dev/null || echo "${login_response}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}✓ Login exitoso${NC}"
|
||||
|
||||
# Al hacer el primer request autenticado, security.py auto-sincroniza el tenant en core.tenants
|
||||
local me_response
|
||||
me_response=$(curl -s "${BACKEND_URL}/api/v1/auth/me" \
|
||||
-H "Authorization: Bearer ${ACCESS_TOKEN}")
|
||||
local synced_tenant_id
|
||||
synced_tenant_id=$(echo "${me_response}" | jq -r '.tenant_id // empty')
|
||||
if [[ -n "${synced_tenant_id}" ]]; then
|
||||
echo -e "${GREEN}✓ Tenant auto-sincronizado en BD local (id=${synced_tenant_id})${NC}"
|
||||
TENANT_ID="${synced_tenant_id}"
|
||||
fi
|
||||
|
||||
# Verificar si ya existe una empresa para este tenant
|
||||
local existing_companies
|
||||
existing_companies=$(curl -s "${BACKEND_URL}/api/v1/a76/company/my-companies" \
|
||||
-H "Authorization: Bearer ${ACCESS_TOKEN}" | jq -r 'length')
|
||||
|
||||
if [[ "${existing_companies}" -gt 0 ]]; then
|
||||
COMPANY_ID=$(curl -s "${BACKEND_URL}/api/v1/a76/company/my-companies" \
|
||||
-H "Authorization: Bearer ${ACCESS_TOKEN}" | jq -r '.[0].id')
|
||||
echo -e "${YELLOW}⚠ Empresa ya existe (id=${COMPANY_ID}), omitiendo creación${NC}"
|
||||
else
|
||||
local company_payload="{\"name\":\"${COMPANY_NAME}\""
|
||||
[[ -n "${COMPANY_RFC}" ]] && company_payload+=",\"rfc\":\"${COMPANY_RFC}\""
|
||||
company_payload+="}"
|
||||
|
||||
local create_company
|
||||
create_company=$(curl -s -w "\n%{http_code}" -X POST \
|
||||
"${BACKEND_URL}/api/v1/a76/company" \
|
||||
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "${company_payload}")
|
||||
|
||||
http_code=$(echo "${create_company}" | tail -n1)
|
||||
local company_body
|
||||
company_body=$(echo "${create_company}" | head -n -1)
|
||||
|
||||
if [[ "${http_code}" == "201" ]]; then
|
||||
COMPANY_ID=$(echo "${company_body}" | jq -r '.id')
|
||||
echo -e "${GREEN}✓ Empresa creada (id=${COMPANY_ID})${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Error creando empresa (HTTP ${http_code}):${NC}"
|
||||
echo "${company_body}" | python3 -m json.tool 2>/dev/null || echo "${company_body}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
###############################################################################
|
||||
# Seed data — requiere: TENANT_ID, COMPANY_ID
|
||||
###############################################################################
|
||||
|
||||
# Insertar datos de ejemplo para customs_brokers
|
||||
seed_customs_brokers() {
|
||||
echo " → Insertando customs brokers..."
|
||||
exec_pg_sql "
|
||||
INSERT INTO a76.customs_brokers (tenant_id, company_id, type, broker_key, name, address, postal_code, city, state, phone, email, country, tax_id, license, company, contact, created_at, updated_at)
|
||||
VALUES
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'persona', 'CB001', 'Agente Aduanal García', 'Av. Reforma 123', '01000', 'Ciudad de México', 'CDMX', '5555555555', 'garcia@aduanas.com', 'MEX', 'GAAR800101ABC', '1234', 'García y Asociados', 'Juan García', now(), now()),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'persona', 'CB002', 'Agente Aduanal López', 'Blvd. Díaz Ordaz 456', '22000', 'Tijuana', 'BC', '6641234567', 'lopez@customs.com', 'MEX', 'LOPL750505XYZ', '2345', 'López Customs', 'María López', now(), now()),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'persona', 'CB003', 'Agente Aduanal Martínez', 'Calle Industria 789', '45000', 'Guadalajara', 'JAL', '3339876543', 'martinez@broker.com', 'MEX', 'MARM850315DEF', '3456', 'Martínez Brokerage', 'Pedro Martínez', now(), now())
|
||||
ON CONFLICT (broker_key, tenant_id, company_id) DO NOTHING;
|
||||
" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Insertar datos de ejemplo para clients_and_providers
|
||||
seed_clients_and_providers() {
|
||||
echo " → Insertando clientes y proveedores..."
|
||||
exec_pg_sql "
|
||||
INSERT INTO a76.clients_and_providers (tenant_id, company_id, type_nat_foreign, name, short_name, rfc, client_or_provider, web_key, is_active, created_at, updated_at)
|
||||
VALUES
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'N', 'Proveedor Tecnológico SA de CV', 'PROVTECH', 'PTE901201ABC', 'BOTH', 'PROV001', true, now(), now()),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'N', 'Cliente Industrial del Norte SA', 'CINORTE', 'CIN850615XYZ', 'BOTH', 'CLI001', true, now(), now()),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'E', 'Global Supplies Inc', 'GLOBSUP', 'GSI123456789', 'BOTH', 'BOTH001', true, now(), now()),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'N', 'Manufacturas del Bajío SA', 'MANBAJIO', 'MDB920310DEF', 'BOTH', 'CLI002', true, now(), now())
|
||||
RETURNING id;
|
||||
" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Insertar datos de ejemplo para packages
|
||||
seed_packages() {
|
||||
echo " → Insertando tipos de paquete..."
|
||||
exec_pg_sql "
|
||||
INSERT INTO a76.packages (tenant_id, company_id, key, description_es, description_en, weight_unit, plurals, plural_in, code_ace, code_aamex, created_at, updated_at)
|
||||
VALUES
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'PK01', 'Caja de Cartón', 'Cardboard Box', 0.5, 'CAJS', 'BOXS', 'CB01', 'CAJA001', now(), now()),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'PK02', 'Pallet de Madera', 'Wooden Pallet', 15.0, 'PLTS', 'PLTS', 'WP01', 'PALL001', now(), now()),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'PK03', 'Tambor Metálico', 'Metal Drum', 10.0, 'TMBS', 'DRMS', 'MD01', 'TAMB001', now(), now()),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'PK04', 'Contenedor', 'Container', 2000.0, 'CONT', 'CONT', 'CT01', 'CONT001', now(), now())
|
||||
ON CONFLICT (tenant_id, company_id, key) DO NOTHING;
|
||||
" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Insertar datos de ejemplo para classes
|
||||
seed_classes() {
|
||||
echo " → Insertando clases..."
|
||||
exec_pg_sql "INSERT INTO a76.classes (tenant_id, company_id, class_code, description_es, description_en, material_key, unit_of_measure, fraction, us_fraction, created_at, updated_at) VALUES (${TENANT_ID}, ${COMPANY_ID}, 'CLS001', 'Componentes Electrónicos', 'Electronic Components', 'MP', 'PZA', '8542.31.01', '8542.31.0000', now(), now()), (${TENANT_ID}, ${COMPANY_ID}, 'CLS002', 'Partes Automotrices', 'Automotive Parts', 'MP', 'KGS', '8708.29.99', '8708.29.9900', now(), now()), (${TENANT_ID}, ${COMPANY_ID}, 'CLS003', 'Textiles y Telas', 'Textiles and Fabrics', 'MP', 'MT', '5407.20.01', '5407.20.0100', now(), now()), (${TENANT_ID}, ${COMPANY_ID}, 'CLS004', 'Equipo de Computación', 'Computer Equipment', 'MP', 'PZA', '8471.30.01', '8471.30.0100', now(), now()) ON CONFLICT (tenant_id, company_id, class_code) DO NOTHING;" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Insertar datos de ejemplo para parts
|
||||
seed_parts() {
|
||||
echo " → Insertando partes/componentes..."
|
||||
|
||||
# Obtener un client_id para asociar las partes
|
||||
local client_id
|
||||
client_id=$(exec_pg_sql "SELECT id FROM a76.clients_and_providers WHERE tenant_id = ${TENANT_ID} AND company_id = ${COMPANY_ID} LIMIT 1;" | xargs)
|
||||
|
||||
if [ -n "$client_id" ]; then
|
||||
exec_pg_sql "INSERT INTO a76.parts (tenant_id, company_id, client_id, part_number, description_spanish, description_english, part_class, currency_key, unit_of_measure, unit_cost, fraction, us_fraction, created_at, updated_at) VALUES (${TENANT_ID}, ${COMPANY_ID}, ${client_id}, 'PART-001', 'Microcontrolador ARM Cortex-M4', 'ARM Cortex-M4 Microcontroller', 'CLS001', 'USD', 'PZA', 15.50, '8542.31.01', '8542.31.0000', now(), now()), (${TENANT_ID}, ${COMPANY_ID}, ${client_id}, 'PART-002', 'Filtro de Aceite Automotriz', 'Automotive Oil Filter', 'CLS002', 'USD', 'PZA', 8.75, '8421.23.01', '8421.23.0100', now(), now()), (${TENANT_ID}, ${COMPANY_ID}, ${client_id}, 'PART-003', 'Tela de Algodón para Tapicería', 'Cotton Upholstery Fabric', 'CLS003', 'USD', 'MT', 12.00, '5208.31.01', '5208.31.0100', now(), now()), (${TENANT_ID}, ${COMPANY_ID}, ${client_id}, 'PART-004', 'Disco Duro SSD 500GB', '500GB SSD Hard Drive', 'CLS004', 'USD', 'PZA', 65.00, '8471.70.01', '8471.70.0100', now(), now()), (${TENANT_ID}, ${COMPANY_ID}, ${client_id}, 'PART-005', 'Sensor de Temperatura Digital', 'Digital Temperature Sensor', 'CLS001', 'USD', 'PZA', 5.25, '9025.19.01', '9025.19.0100', now(), now()) ON CONFLICT (tenant_id, company_id, part_number) DO NOTHING;" >/dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
# Insertar datos de ejemplo para pedimentos
|
||||
seed_pedimentos() {
|
||||
echo " → Insertando pedimentos..."
|
||||
|
||||
# Primero obtener IDs de clientes
|
||||
local client_ids
|
||||
client_ids=$(exec_pg_sql "SELECT id FROM a76.clients_and_providers WHERE tenant_id = ${TENANT_ID} AND company_id = ${COMPANY_ID} AND client_or_provider IN ('CLIENT', 'BOTH') LIMIT 2;")
|
||||
local client_id_1=$(echo "$client_ids" | sed -n '1p' | xargs)
|
||||
local client_id_2=$(echo "$client_ids" | sed -n '2p' | xargs)
|
||||
|
||||
if [ -n "$client_id_1" ]; then
|
||||
exec_pg_sql "INSERT INTO a76.pedimentos (tenant_id, company_id, year, customs_office, license, pedimento_number, client_id, operation_type, pedimento_type, pedimento_code, regime, status, created_at, updated_at) VALUES (${TENANT_ID}, ${COMPANY_ID}, '24', '47', '3807', '8001234', ${client_id_1}, 'imp', 'normal', 'V1', 'ITE', 'draft', now(), now()), (${TENANT_ID}, ${COMPANY_ID}, '24', '47', '3807', '8001235', ${client_id_1}, 'exp', 'normal', 'V1', 'ETE', 'draft', now(), now()) ON CONFLICT (tenant_id, company_id, year, customs_office, license, pedimento_number) DO NOTHING;" >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
if [ -n "$client_id_2" ]; then
|
||||
exec_pg_sql "INSERT INTO a76.pedimentos (tenant_id, company_id, year, customs_office, license, pedimento_number, client_id, operation_type, pedimento_type, pedimento_code, regime, status, created_at, updated_at) VALUES (${TENANT_ID}, ${COMPANY_ID}, '24', '47', '3807', '8001236', ${client_id_2}, 'imp', 'consolidated', 'V1', 'ITE', 'draft', now(), now()) ON CONFLICT (tenant_id, company_id, year, customs_office, license, pedimento_number) DO NOTHING;" >/dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
# Insertar datos de ejemplo para invoices
|
||||
# Genera ~56 facturas distribuidas en los últimos 12 meses para alimentar la gráfica de tendencia
|
||||
seed_invoices() {
|
||||
echo " → Insertando facturas de los últimos 12 meses..."
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# 1. invoice_header — una fila por factura con fecha real en cada mes
|
||||
# -------------------------------------------------------------------------
|
||||
exec_pg_sql "INSERT INTO a76.invoice_header
|
||||
(tenant_id, company_id, system, operation_type, invoice_type, invoice_number, invoice_date, status, created_at, updated_at)
|
||||
VALUES
|
||||
-- Abril 2025 (4 facturas)
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'DEF', 'SEED-2025-04-001', '2025-04-03', false, '2025-04-03 08:00:00', '2025-04-03 08:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'EXDEF', 'SEED-2025-04-002', '2025-04-11', false, '2025-04-11 10:30:00', '2025-04-11 10:30:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'TEM', 'SEED-2025-04-003', '2025-04-18', false, '2025-04-18 14:00:00', '2025-04-18 14:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'DEF', 'SEED-2025-04-004', '2025-04-25', false, '2025-04-25 09:15:00', '2025-04-25 09:15:00'),
|
||||
-- Mayo 2025 (5 facturas)
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'DEF', 'SEED-2025-05-001', '2025-05-02', false, '2025-05-02 08:00:00', '2025-05-02 08:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'TEM', 'SEED-2025-05-002', '2025-05-07', false, '2025-05-07 11:00:00', '2025-05-07 11:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'EXDEF', 'SEED-2025-05-003', '2025-05-14', false, '2025-05-14 13:30:00', '2025-05-14 13:30:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'DEF', 'SEED-2025-05-004', '2025-05-20', false, '2025-05-20 09:00:00', '2025-05-20 09:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'DEF', 'SEED-2025-05-005', '2025-05-28', false, '2025-05-28 16:00:00', '2025-05-28 16:00:00'),
|
||||
-- Junio 2025 (3 facturas)
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'TEM', 'SEED-2025-06-001', '2025-06-05', false, '2025-06-05 08:30:00', '2025-06-05 08:30:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'EXDEF', 'SEED-2025-06-002', '2025-06-17', false, '2025-06-17 12:00:00', '2025-06-17 12:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'DEF', 'SEED-2025-06-003', '2025-06-27', false, '2025-06-27 10:00:00', '2025-06-27 10:00:00'),
|
||||
-- Julio 2025 (6 facturas)
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'DEF', 'SEED-2025-07-001', '2025-07-02', false, '2025-07-02 08:00:00', '2025-07-02 08:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'DEF', 'SEED-2025-07-002', '2025-07-07', false, '2025-07-07 10:00:00', '2025-07-07 10:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'TEM', 'SEED-2025-07-003', '2025-07-11', false, '2025-07-11 09:30:00', '2025-07-11 09:30:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'EXDEF', 'SEED-2025-07-004', '2025-07-16', false, '2025-07-16 14:00:00', '2025-07-16 14:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'DEF', 'SEED-2025-07-005', '2025-07-22', false, '2025-07-22 11:00:00', '2025-07-22 11:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'TEM', 'SEED-2025-07-006', '2025-07-29', false, '2025-07-29 15:00:00', '2025-07-29 15:00:00'),
|
||||
-- Agosto 2025 (4 facturas)
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'EXDEF', 'SEED-2025-08-001', '2025-08-04', false, '2025-08-04 08:00:00', '2025-08-04 08:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'DEF', 'SEED-2025-08-002', '2025-08-12', false, '2025-08-12 10:30:00', '2025-08-12 10:30:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'DEF', 'SEED-2025-08-003', '2025-08-19', false, '2025-08-19 13:00:00', '2025-08-19 13:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'TEM', 'SEED-2025-08-004', '2025-08-26', false, '2025-08-26 09:00:00', '2025-08-26 09:00:00'),
|
||||
-- Septiembre 2025 (5 facturas)
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'DEF', 'SEED-2025-09-001', '2025-09-02', false, '2025-09-02 08:00:00', '2025-09-02 08:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'EXDEF', 'SEED-2025-09-002', '2025-09-09', false, '2025-09-09 11:30:00', '2025-09-09 11:30:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'TEM', 'SEED-2025-09-003', '2025-09-15', false, '2025-09-15 14:00:00', '2025-09-15 14:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'DEF', 'SEED-2025-09-004', '2025-09-22', false, '2025-09-22 09:30:00', '2025-09-22 09:30:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'DEF', 'SEED-2025-09-005', '2025-09-29', false, '2025-09-29 16:00:00', '2025-09-29 16:00:00'),
|
||||
-- Octubre 2025 (7 facturas)
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'DEF', 'SEED-2025-10-001', '2025-10-01', false, '2025-10-01 08:00:00', '2025-10-01 08:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'EXDEF', 'SEED-2025-10-002', '2025-10-06', false, '2025-10-06 10:00:00', '2025-10-06 10:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'TEM', 'SEED-2025-10-003', '2025-10-10', false, '2025-10-10 09:00:00', '2025-10-10 09:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'DEF', 'SEED-2025-10-004', '2025-10-15', false, '2025-10-15 13:30:00', '2025-10-15 13:30:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'DEF', 'SEED-2025-10-005', '2025-10-20', false, '2025-10-20 11:00:00', '2025-10-20 11:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'TEM', 'SEED-2025-10-006', '2025-10-24', false, '2025-10-24 14:30:00', '2025-10-24 14:30:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'EXDEF', 'SEED-2025-10-007', '2025-10-29', false, '2025-10-29 08:30:00', '2025-10-29 08:30:00'),
|
||||
-- Noviembre 2025 (4 facturas)
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'DEF', 'SEED-2025-11-001', '2025-11-04', false, '2025-11-04 09:00:00', '2025-11-04 09:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'DEF', 'SEED-2025-11-002', '2025-11-12', false, '2025-11-12 11:00:00', '2025-11-12 11:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'TEM', 'SEED-2025-11-003', '2025-11-19', false, '2025-11-19 14:00:00', '2025-11-19 14:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'EXDEF', 'SEED-2025-11-004', '2025-11-26', false, '2025-11-26 10:00:00', '2025-11-26 10:00:00'),
|
||||
-- Diciembre 2025 (3 facturas)
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'DEF', 'SEED-2025-12-001', '2025-12-03', false, '2025-12-03 08:00:00', '2025-12-03 08:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'EXDEF', 'SEED-2025-12-002', '2025-12-11', false, '2025-12-11 12:00:00', '2025-12-11 12:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'TEM', 'SEED-2025-12-003', '2025-12-19', false, '2025-12-19 09:30:00', '2025-12-19 09:30:00'),
|
||||
-- Enero 2026 (5 facturas)
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'DEF', 'SEED-2026-01-001', '2026-01-07', false, '2026-01-07 08:00:00', '2026-01-07 08:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'DEF', 'SEED-2026-01-002', '2026-01-13', false, '2026-01-13 10:30:00', '2026-01-13 10:30:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'TEM', 'SEED-2026-01-003', '2026-01-17', false, '2026-01-17 13:00:00', '2026-01-17 13:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'EXDEF', 'SEED-2026-01-004', '2026-01-22', false, '2026-01-22 09:00:00', '2026-01-22 09:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'DEF', 'SEED-2026-01-005', '2026-01-29', false, '2026-01-29 14:30:00', '2026-01-29 14:30:00'),
|
||||
-- Febrero 2026 (6 facturas)
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'DEF', 'SEED-2026-02-001', '2026-02-03', false, '2026-02-03 08:00:00', '2026-02-03 08:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'EXDEF', 'SEED-2026-02-002', '2026-02-06', false, '2026-02-06 10:00:00', '2026-02-06 10:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'TEM', 'SEED-2026-02-003', '2026-02-11', false, '2026-02-11 09:30:00', '2026-02-11 09:30:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'DEF', 'SEED-2026-02-004', '2026-02-17', false, '2026-02-17 13:00:00', '2026-02-17 13:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'DEF', 'SEED-2026-02-005', '2026-02-21', false, '2026-02-21 11:00:00', '2026-02-21 11:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'EXDEF', 'SEED-2026-02-006', '2026-02-26', false, '2026-02-26 15:30:00', '2026-02-26 15:30:00'),
|
||||
-- Marzo 2026 (5 facturas)
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'DEF', 'SEED-2026-03-001', '2026-03-02', false, '2026-03-02 08:00:00', '2026-03-02 08:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'DEF', 'SEED-2026-03-002', '2026-03-05', false, '2026-03-05 10:00:00', '2026-03-05 10:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'TEM', 'SEED-2026-03-003', '2026-03-06', false, '2026-03-06 09:00:00', '2026-03-06 09:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'exp', 'EXDEF', 'SEED-2026-03-004', '2026-03-07', false, '2026-03-07 14:00:00', '2026-03-07 14:00:00'),
|
||||
(${TENANT_ID}, ${COMPANY_ID}, 'fixed_asset', 'imp', 'DEF', 'SEED-2026-03-005', '2026-03-08', false, '2026-03-08 08:30:00', '2026-03-08 08:30:00')
|
||||
ON CONFLICT DO NOTHING;" >/dev/null 2>&1
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# 2. invoice_financials — INSERT ... SELECT desde invoice_header
|
||||
# -------------------------------------------------------------------------
|
||||
exec_pg_sql "INSERT INTO a76.invoice_financials
|
||||
(tenant_id, company_id, invoice_id, currency, exchange_rate, created_at, updated_at)
|
||||
SELECT
|
||||
ih.tenant_id,
|
||||
ih.company_id,
|
||||
ih.id,
|
||||
CASE WHEN ih.operation_type = 'imp' THEN 'foreign' ELSE 'local' END,
|
||||
CASE WHEN ih.operation_type = 'imp' THEN 17.50 ELSE 1.00 END,
|
||||
ih.created_at,
|
||||
ih.updated_at
|
||||
FROM a76.invoice_header ih
|
||||
WHERE ih.tenant_id = ${TENANT_ID}
|
||||
AND ih.company_id = ${COMPANY_ID}
|
||||
AND ih.invoice_number LIKE 'SEED-%'
|
||||
ON CONFLICT DO NOTHING;" >/dev/null 2>&1
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# 3. invoice_compliance_mx — asignar proveedores rotando entre los disponibles
|
||||
# -------------------------------------------------------------------------
|
||||
exec_pg_sql "INSERT INTO a76.invoice_compliance_mx
|
||||
(tenant_id, company_id, invoice_id, provider_id, created_at, updated_at)
|
||||
SELECT
|
||||
ih.tenant_id,
|
||||
ih.company_id,
|
||||
ih.id,
|
||||
cp.id,
|
||||
ih.created_at,
|
||||
ih.updated_at
|
||||
FROM a76.invoice_header ih
|
||||
JOIN LATERAL (
|
||||
SELECT id FROM a76.clients_and_providers
|
||||
WHERE tenant_id = ${TENANT_ID}
|
||||
AND company_id = ${COMPANY_ID}
|
||||
AND client_or_provider IN ('PROVIDER', 'BOTH')
|
||||
ORDER BY id
|
||||
OFFSET (ROW_NUMBER() OVER (ORDER BY ih.id) - 1) % (
|
||||
SELECT COUNT(*) FROM a76.clients_and_providers
|
||||
WHERE tenant_id = ${TENANT_ID} AND company_id = ${COMPANY_ID}
|
||||
AND client_or_provider IN ('PROVIDER', 'BOTH')
|
||||
)
|
||||
LIMIT 1
|
||||
) cp ON true
|
||||
WHERE ih.tenant_id = ${TENANT_ID}
|
||||
AND ih.company_id = ${COMPANY_ID}
|
||||
AND ih.invoice_number LIKE 'SEED-%'
|
||||
ON CONFLICT DO NOTHING;" >/dev/null 2>&1
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# 4. invoice_logistics — rotar incoterms y transport_type
|
||||
# -------------------------------------------------------------------------
|
||||
exec_pg_sql "INSERT INTO a76.invoice_logistics
|
||||
(tenant_id, company_id, invoice_id, transport_type, weight_type, incoterm, created_at, updated_at)
|
||||
SELECT
|
||||
ih.tenant_id,
|
||||
ih.company_id,
|
||||
ih.id,
|
||||
'none',
|
||||
'kgs',
|
||||
(ARRAY['FOB','CIF','EXW','DDP','DAP'])[((ROW_NUMBER() OVER (ORDER BY ih.id) - 1) % 5) + 1],
|
||||
ih.created_at,
|
||||
ih.updated_at
|
||||
FROM a76.invoice_header ih
|
||||
WHERE ih.tenant_id = ${TENANT_ID}
|
||||
AND ih.company_id = ${COMPANY_ID}
|
||||
AND ih.invoice_number LIKE 'SEED-%'
|
||||
ON CONFLICT DO NOTHING;" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Ejecutar todas las funciones de seed
|
||||
execute_seed_data() {
|
||||
echo -e "\n${YELLOW}[SEED] Cargando datos de ejemplo...${NC}"
|
||||
|
||||
seed_customs_brokers
|
||||
seed_clients_and_providers
|
||||
seed_packages
|
||||
seed_classes
|
||||
seed_parts
|
||||
seed_pedimentos
|
||||
seed_invoices
|
||||
|
||||
echo -e "${GREEN}✓ Datos de ejemplo cargados exitosamente${NC}"
|
||||
echo -e "${YELLOW} • 3 Agentes aduanales${NC}"
|
||||
echo -e "${YELLOW} • 4 Clientes/Proveedores${NC}"
|
||||
echo -e "${YELLOW} • 4 Tipos de paquete${NC}"
|
||||
echo -e "${YELLOW} • 4 Clases${NC}"
|
||||
echo -e "${YELLOW} • 5 Parts/Componentes${NC}"
|
||||
echo -e "${YELLOW} • 3 Pedimentos${NC}"
|
||||
echo -e "${YELLOW} • 56 Facturas (12 meses: Abr 2025 → Mar 2026)${NC}"
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# FLUJO PRINCIPAL
|
||||
###############################################################################
|
||||
|
||||
echo -e "${GREEN}════════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${GREEN} Inicialización del Sistema Anexo76${NC}"
|
||||
echo -e "${GREEN}════════════════════════════════════════════════════════${NC}"
|
||||
|
||||
hub_mode_init
|
||||
|
||||
|
||||
|
||||
|
||||
###############################################################################
|
||||
# 9. Crear licencia Enterprise para el tenant
|
||||
###############################################################################
|
||||
echo -e "\n${YELLOW}[9/9] Creando licencia Enterprise para el tenant...${NC}"
|
||||
|
||||
# Calcular fechas de inicio y expiración (1 año desde hoy)
|
||||
LICENSE_START_DATE=$(date -u +"%Y-%m-%d %H:%M:%S")
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
LICENSE_EXPIRE_DATE=$(date -u -v+1y +"%Y-%m-%d %H:%M:%S")
|
||||
else
|
||||
LICENSE_EXPIRE_DATE=$(date -u -d "+1 year" +"%Y-%m-%d %H:%M:%S")
|
||||
fi
|
||||
|
||||
# Verificar si ya existe una licencia para este tenant
|
||||
LICENSE_EXISTS=$(exec_pg_sql "SELECT COUNT(*) FROM core.licenses WHERE tenant_id = ${TENANT_ID};")
|
||||
LICENSE_EXISTS=$(echo "$LICENSE_EXISTS" | xargs)
|
||||
|
||||
if [ "$LICENSE_EXISTS" = "0" ]; then
|
||||
# Crear licencia Enterprise con límites ilimitados (valores muy altos)
|
||||
set +e # Desactivar exit on error temporalmente
|
||||
LICENSE_CREATE_OUTPUT=$(exec_pg_sql "INSERT INTO core.licenses (tenant_id, plan, status, max_users, max_storage_gb, max_monthly_operations, feature_api_access, feature_advanced_reports, feature_integrations, feature_dedicated_support, starts_at, expires_at, created_at, updated_at) VALUES (${TENANT_ID}, 'ENTERPRISE', 'ACTIVE', 999999, 999999, 999999, true, true, true, true, '${LICENSE_START_DATE}'::timestamp, '${LICENSE_EXPIRE_DATE}'::timestamp, now(), now());" 2>&1)
|
||||
LICENSE_CREATE_STATUS=$?
|
||||
set -e # Reactivar exit on error
|
||||
|
||||
if [ $LICENSE_CREATE_STATUS -eq 0 ]; then
|
||||
exec_pg_sql_client "INSERT INTO core.licenses (tenant_id, plan, status, max_users, max_storage_gb, max_monthly_operations, feature_api_access, feature_advanced_reports, feature_integrations, feature_dedicated_support, starts_at, expires_at, created_at, updated_at) VALUES (${TENANT_ID}, 'ENTERPRISE', 'ACTIVE', 999999, 999999, 999999, true, true, true, true, '${LICENSE_START_DATE}'::timestamp, '${LICENSE_EXPIRE_DATE}'::timestamp, now(), now());" >/dev/null 2>&1 || true
|
||||
echo -e "${GREEN}✓ Licencia Enterprise creada exitosamente (Hub y Cliente)${NC}"
|
||||
echo -e "${GREEN} Plan: Enterprise${NC}"
|
||||
echo -e "${GREEN} Usuarios: Ilimitados${NC}"
|
||||
echo -e "${GREEN} Almacenamiento: Ilimitado${NC}"
|
||||
echo -e "${GREEN} Operaciones mensuales: Ilimitadas${NC}"
|
||||
echo -e "${GREEN} Inicio: ${LICENSE_START_DATE}${NC}"
|
||||
echo -e "${GREEN} Expiración: ${LICENSE_EXPIRE_DATE}${NC}"
|
||||
echo -e "${GREEN} Features: API, Reportes Avanzados, Integraciones, Soporte Dedicado${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Error al crear la licencia${NC}"
|
||||
echo -e "${YELLOW}Detalle del error:${NC}"
|
||||
echo "$LICENSE_CREATE_OUTPUT"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}⚠ Ya existe una licencia para este tenant, actualizando a Enterprise...${NC}"
|
||||
|
||||
# Actualizar licencia existente a Enterprise
|
||||
set +e # Desactivar exit on error temporalmente
|
||||
exec_pg_sql "UPDATE core.licenses SET plan = 'ENTERPRISE', status = 'ACTIVE', max_users = 999999, max_storage_gb = 999999, max_monthly_operations = 999999, feature_api_access = true, feature_advanced_reports = true, feature_integrations = true, feature_dedicated_support = true, starts_at = '${LICENSE_START_DATE}'::timestamp, expires_at = '${LICENSE_EXPIRE_DATE}'::timestamp, updated_at = now() WHERE tenant_id = ${TENANT_ID};" >/dev/null 2>&1
|
||||
exec_pg_sql_client "UPDATE core.licenses SET plan = 'ENTERPRISE', status = 'ACTIVE', max_users = 999999, max_storage_gb = 999999, max_monthly_operations = 999999, feature_api_access = true, feature_advanced_reports = true, feature_integrations = true, feature_dedicated_support = true, starts_at = '${LICENSE_START_DATE}'::timestamp, expires_at = '${LICENSE_EXPIRE_DATE}'::timestamp, updated_at = now() WHERE tenant_id = ${TENANT_ID};" >/dev/null 2>&1 || true
|
||||
LICENSE_UPDATE_STATUS=$?
|
||||
set -e # Reactivar exit on error
|
||||
|
||||
if [ $LICENSE_UPDATE_STATUS -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ Licencia actualizada a Enterprise (Hub y Cliente)${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Error al actualizar la licencia${NC}"
|
||||
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Seed data opcional ────────────────────────────────────────────────────────
|
||||
if [[ "${SEED_DATA}" == "true" ]]; then
|
||||
if [[ -z "${TENANT_ID}" || -z "${COMPANY_ID}" ]]; then
|
||||
echo -e "${RED}✗ No se puede cargar seed data: TENANT_ID o COMPANY_ID no disponibles${NC}"
|
||||
exit 1
|
||||
fi
|
||||
execute_seed_data
|
||||
fi
|
||||
|
||||
# ── Resumen final ─────────────────────────────────────────────────────────────
|
||||
echo -e "\n${GREEN}════════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${GREEN} ✓ Inicialización completada exitosamente${NC}"
|
||||
echo -e "${GREEN}════════════════════════════════════════════════════════${NC}"
|
||||
echo -e "\n${YELLOW}Credenciales de acceso:${NC}"
|
||||
echo -e " Usuario: ${GREEN}${USER_USERNAME}${NC}"
|
||||
echo -e " Password: ${GREEN}${USER_PASSWORD}${NC}"
|
||||
echo -e " Email: ${GREEN}${USER_EMAIL}${NC}"
|
||||
echo -e " Tenant: ${GREEN}${TENANT_SLUG}${NC}"
|
||||
if [[ -n "${TENANT_ID}" ]]; then
|
||||
echo -e " Tenant ID: ${GREEN}${TENANT_ID}${NC}"
|
||||
fi
|
||||
if [[ -n "${COMPANY_ID}" ]]; then
|
||||
echo -e " Company: ${GREEN}${COMPANY_NAME} (id=${COMPANY_ID})${NC}"
|
||||
fi
|
||||
echo -e "\n${YELLOW}Modo de operación: ${GREEN}${MODE}${NC}"
|
||||
if [[ "${SEED_DATA}" == "true" ]]; then
|
||||
echo -e "\n${YELLOW}Datos de ejemplo cargados: ${GREEN}sí${NC}"
|
||||
fi
|
||||
echo -e "\n${YELLOW}Puedes acceder al sistema en:${NC}"
|
||||
echo -e " ${GREEN}http://localhost:5173${NC}"
|
||||
echo -e "\n${GREEN}════════════════════════════════════════════════════════${NC}\n"
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
#!/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 "$@"
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Ejecuta una pasada del janitor de layouts CSV (tarea Celery cleanup_orphan_layout_imports).
|
||||
# Requiere stack levantado: docker compose up -d worker valkey postgres-a76
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
CONTAINER="${CELERY_WORKER_CONTAINER:-worker}"
|
||||
docker compose exec -T "$CONTAINER" celery -A core.celery_app call cleanup_orphan_layout_imports
|
||||
@@ -1,34 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Espera a que backend y frontend estén sirviendo tras `docker compose up`.
|
||||
#
|
||||
# Uso en Jenkins: el wait real corre dentro del contenedor Playwright con
|
||||
# --network host (ver Jenkinsfile). Este script es para dev/local cuando los
|
||||
# puertos 8000/5173 sí son alcanzables desde el shell que lo invoca.
|
||||
set -euo pipefail
|
||||
|
||||
BACKEND_URL="${BACKEND_URL:-http://localhost:8000/api/health}"
|
||||
FRONTEND_URL="${FRONTEND_URL:-http://localhost:5173/}"
|
||||
BACKEND_MAX_SEC="${BACKEND_MAX_SEC:-900}"
|
||||
FRONTEND_MAX_SEC="${FRONTEND_MAX_SEC:-300}"
|
||||
STEP="${STEP:-5}"
|
||||
|
||||
wait_for() {
|
||||
local url="$1" max="$2" label="$3"
|
||||
echo "== wait: ${label} ${url} =="
|
||||
local i=0
|
||||
until curl -fsS --connect-timeout 3 --max-time 5 "$url" >/dev/null 2>&1; do
|
||||
i=$((i + STEP))
|
||||
if [ "$i" -ge "$max" ]; then
|
||||
echo "ERROR: timeout esperando ${label} ${url} (${max}s)"
|
||||
return 1
|
||||
fi
|
||||
if [ $((i % 30)) -eq 0 ]; then
|
||||
echo " ...esperando ${label} (${i}/${max}s)"
|
||||
fi
|
||||
sleep "$STEP"
|
||||
done
|
||||
echo "== ${label} listo tras ${i}s =="
|
||||
}
|
||||
|
||||
wait_for "$BACKEND_URL" "$BACKEND_MAX_SEC" "backend"
|
||||
wait_for "$FRONTEND_URL" "$FRONTEND_MAX_SEC" "frontend"
|
||||
Reference in New Issue
Block a user