Files
plantillas-proyectos/scripts/init_first_time.sh
acazares 52b8fcd434 feat: Implement multi-tenancy support in middleware and security layers
- Enhanced TenantMiddleware to validate tenant information from JWT tokens.
- Added LicenseValidationMiddleware to check tenant licenses before processing requests.
- Updated security utilities to extract tenant information from tokens and validate company access.
- Introduced CompanyStore to manage active company state and handle company switching in the frontend.
- Modified API routes to include company_id in requests for better resource management.
- Improved logging and error handling throughout the middleware and API layers.
- Updated frontend components to reflect changes in company management and selection.
- Added new API route for fetching user's companies with proper authentication handling.
2025-11-11 14:15:31 -06:00

614 lines
26 KiB
Bash
Executable File

#!/bin/bash
###############################################################################
# Script de inicialización completa para Anexo76
#
# Este script configura automáticamente:
# 1. User Profile Attributes en Keycloak (tenant_id)
# 2. Clientes de Keycloak (Backend y Frontend)
# 3. Mappers de atributos (tenant_id)
# 4. Usuario demo en Keycloak (usuario=demo, password=demo123)
# 5. Tenant y Company en PostgreSQL
# 6. Relación usuario-tenant en tabla user_tenants
# 7. Actualización del tenant_id del usuario con el valor real
#
# Requisitos:
# - Keycloak corriendo en http://localhost:8080
# - PostgreSQL corriendo en localhost:5432
# - Base de datos anexo76_core creada
# - jq instalado (para procesamiento JSON)
#
# Nota: El atributo tenant_id se crea con valor inicial "1" y luego
# se actualiza con el ID real del tenant creado en PostgreSQL.
###############################################################################
set -e # Salir si hay algún error
# Colores para output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Variables de configuración
KEYCLOAK_URL="${KEYCLOAK_URL:-http://localhost:8080}"
KEYCLOAK_ADMIN="${KEYCLOAK_ADMIN:-admin}"
KEYCLOAK_ADMIN_PASSWORD="${KEYCLOAK_ADMIN_PASSWORD:-admin}"
KEYCLOAK_REALM="${KEYCLOAK_REALM:-master}"
KEYCLOACK_ADMIN_URL="${KEYCLOACK_ADMIN_URL:-http://localhost:9000}"
POSTGRES_HOST="${POSTGRES_HOST:-localhost}"
POSTGRES_PORT="${POSTGRES_PORT:-5432}"
POSTGRES_DB="${POSTGRES_DB:-anexo76_core}"
POSTGRES_USER="${POSTGRES_USER:-postgres}"
POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-postgres}"
DEMO_USERNAME="demo"
DEMO_PASSWORD="demo123"
DEMO_EMAIL="demo@aduanasoft.com"
DEMO_FIRSTNAME="Demo"
DEMO_LASTNAME="User"
TENANT_NAME="Aduanasoft"
TENANT_SLUG="aduanasoft"
COMPANY_NAME="Aduanasoft S.A. de C.V."
COMPANY_RFC="ADS010101AAA"
echo -e "${GREEN}════════════════════════════════════════════════════════${NC}"
echo -e "${GREEN} Inicialización del Sistema Anexo76${NC}"
echo -e "${GREEN}════════════════════════════════════════════════════════${NC}"
###############################################################################
# 1. Esperar a que Keycloak esté listo
###############################################################################
echo -e "\n${YELLOW}[1/8] Esperando a que Keycloak esté disponible...${NC}"
MAX_RETRIES=30
RETRY_COUNT=0
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
if curl -s -f "${KEYCLOACK_ADMIN_URL}/health/ready" > /dev/null 2>&1; then
echo -e "${GREEN}✓ Keycloak está listo${NC}"
break
fi
RETRY_COUNT=$((RETRY_COUNT + 1))
echo "Intento $RETRY_COUNT/$MAX_RETRIES..."
sleep 2
done
if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then
echo -e "${RED}✗ Error: Keycloak no está disponible después de $MAX_RETRIES intentos${NC}"
exit 1
fi
###############################################################################
# 2. Obtener token de administrador de Keycloak
###############################################################################
echo -e "\n${YELLOW}[2/8] Obteniendo token de administrador de Keycloak...${NC}"
TOKEN_RESPONSE=$(curl -s -X POST "${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=${KEYCLOAK_ADMIN}" \
-d "password=${KEYCLOAK_ADMIN_PASSWORD}" \
-d "grant_type=password" \
-d "client_id=admin-cli")
ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | grep -o '"access_token":"[^"]*' | sed 's/"access_token":"//')
if [ -z "$ACCESS_TOKEN" ]; then
echo -e "${RED}✗ Error: No se pudo obtener el token de acceso${NC}"
echo "Respuesta: $TOKEN_RESPONSE"
exit 1
fi
echo -e "${GREEN}✓ Token obtenido exitosamente${NC}"
###############################################################################
# 3. Configurar User Profile Attributes con valores por defecto
###############################################################################
echo -e "\n${YELLOW}[3/8] Configurando User Profile Attributes...${NC}"
# Obtener configuración actual del User Profile
USER_PROFILE=$(curl -s -X GET "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users/profile" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json")
# Verificar si tenant_id existe
TENANT_ID_EXISTS=$(echo "$USER_PROFILE" | grep -q "\"name\":\"tenant_id\"" && echo "true" || echo "false")
if [ "$TENANT_ID_EXISTS" = "false" ]; then
echo "Agregando atributo tenant_id al User Profile..."
# Crear el atributo tenant_id con valor por defecto
UPDATED_PROFILE=$(echo "$USER_PROFILE" | jq '.attributes += [{
"name": "tenant_id",
"displayName": "Tenant ID",
"validations": {},
"annotations": {
"inputType": "text"
},
"required": {
"roles": [],
"scopes": []
},
"permissions": {
"view": ["admin", "user"],
"edit": ["admin"]
},
"multivalued": false,
"group": null
}]')
# Actualizar el User Profile
curl -s -X PUT "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users/profile" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d "$UPDATED_PROFILE"
echo -e "${GREEN}✓ Atributo tenant_id agregado al User Profile${NC}"
else
echo -e "${YELLOW}⚠ Atributo tenant_id ya existe en User Profile${NC}"
fi
###############################################################################
# 4. Configurar clientes de Keycloak (Backend y Frontend)
###############################################################################
echo -e "\n${YELLOW}[4/8] Configurando clientes de Keycloak...${NC}"
# 3.1 Crear cliente Backend
echo "Configurando cliente Backend..."
BACKEND_CLIENT_EXISTS=$(curl -s -X GET "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients?clientId=anexo76-backend" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json")
if echo "$BACKEND_CLIENT_EXISTS" | grep -q "\"clientId\":\"anexo76-backend\""; then
echo -e "${YELLOW}⚠ Cliente Backend ya existe${NC}"
BACKEND_CLIENT_ID=$(echo "$BACKEND_CLIENT_EXISTS" | grep -o '"id":"[^"]*' | head -1 | sed 's/"id":"//')
else
CREATE_BACKEND=$(curl -s -w "\n%{http_code}" -X POST "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"clientId": "anexo76-backend",
"name": "Anexo76 Backend API",
"description": "Backend API para el sistema Anexo76",
"enabled": true,
"protocol": "openid-connect",
"publicClient": false,
"serviceAccountsEnabled": true,
"directAccessGrantsEnabled": true,
"standardFlowEnabled": true,
"implicitFlowEnabled": false,
"rootUrl": "http://localhost:8000/api",
"baseUrl": "http://localhost:8000/api",
"redirectUris": [
"http://localhost:8000/*",
"http://localhost:5180/*"
],
"webOrigins": ["http://localhost:8000/api"],
"attributes": {
"access.token.lifespan": "3600",
"client.secret.creation.time": "0"
}
}')
HTTP_CODE=$(echo "$CREATE_BACKEND" | tail -n1)
if [ "$HTTP_CODE" = "201" ]; then
echo -e "${GREEN}✓ Cliente Backend creado${NC}"
# Obtener ID del cliente recién creado
BACKEND_CLIENT_EXISTS=$(curl -s -X GET "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients?clientId=anexo76-backend" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json")
BACKEND_CLIENT_ID=$(echo "$BACKEND_CLIENT_EXISTS" | grep -o '"id":"[^"]*' | head -1 | sed 's/"id":"//')
else
echo -e "${RED}✗ Error al crear cliente Backend (HTTP ${HTTP_CODE})${NC}"
fi
fi
# Obtener y mostrar el secret del cliente Backend
if [ -n "$BACKEND_CLIENT_ID" ]; then
BACKEND_SECRET=$(curl -s -X GET "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients/${BACKEND_CLIENT_ID}/client-secret" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" | grep -o '"value":"[^"]*' | sed 's/"value":"//')
echo -e "${GREEN}✓ Backend Client ID: anexo76-backend${NC}"
echo -e "${GREEN}✓ Backend Client Secret: ${BACKEND_SECRET}${NC}"
# Actualizar archivo .env con el client secret
if [ -n "$BACKEND_SECRET" ]; then
ENV_FILE=""
# Buscar archivo .env en el directorio actual o en el directorio padre
if [ -f ".env" ]; then
ENV_FILE=".env"
elif [ -f "../.env" ]; then
ENV_FILE="../.env"
fi
if [ -n "$ENV_FILE" ]; then
# Verificar si la variable ya existe en el archivo
if grep -q "^KEYCLOAK_CLIENT_SECRET=" "$ENV_FILE"; then
# Actualizar el valor existente
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
sed -i '' "s|^KEYCLOAK_CLIENT_SECRET=.*|KEYCLOAK_CLIENT_SECRET=${BACKEND_SECRET}|" "$ENV_FILE"
else
# Linux
sed -i "s|^KEYCLOAK_CLIENT_SECRET=.*|KEYCLOAK_CLIENT_SECRET=${BACKEND_SECRET}|" "$ENV_FILE"
fi
echo -e "${GREEN}✓ KEYCLOAK_CLIENT_SECRET actualizado en ${ENV_FILE}${NC}"
else
# Agregar la variable si no existe
echo "KEYCLOAK_CLIENT_SECRET=${BACKEND_SECRET}" >> "$ENV_FILE"
echo -e "${GREEN}✓ KEYCLOAK_CLIENT_SECRET agregado a ${ENV_FILE}${NC}"
fi
else
echo -e "${YELLOW}⚠ No se encontró archivo .env, el secret debe agregarse manualmente${NC}"
fi
fi
fi
# 3.2 Crear cliente Frontend
echo "Configurando cliente Frontend..."
FRONTEND_CLIENT_EXISTS=$(curl -s -X GET "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients?clientId=anexo76-frontend" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json")
if echo "$FRONTEND_CLIENT_EXISTS" | grep -q "\"clientId\":\"anexo76-frontend\""; then
echo -e "${YELLOW}⚠ Cliente Frontend ya existe${NC}"
FRONTEND_CLIENT_ID=$(echo "$FRONTEND_CLIENT_EXISTS" | grep -o '"id":"[^"]*' | head -1 | sed 's/"id":"//')
else
CREATE_FRONTEND=$(curl -s -w "\n%{http_code}" -X POST "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"clientId": "anexo76-frontend",
"name": "Anexo76 Frontend",
"description": "Aplicación web frontend para el sistema Anexo76",
"enabled": true,
"protocol": "openid-connect",
"publicClient": true,
"directAccessGrantsEnabled": false,
"standardFlowEnabled": true,
"implicitFlowEnabled": false,
"rootUrl": "http://localhost:5173",
"baseUrl": "http://localhost:5173",
"redirectUris": [
"http://localhost:5173/*",
"http://localhost:3000/*",
"http://localhost:5180/*"
],
"postLogoutRedirectUris": [
"http://localhost:5173/*",
"http://localhost:3000/*",
"http://localhost:5180/*"
],
"webOrigins": [
"http://localhost:5173",
"http://localhost:3000",
"http://localhost:5180"
],
"attributes": {
"pkce.code.challenge.method": "S256"
}
}')
HTTP_CODE=$(echo "$CREATE_FRONTEND" | tail -n1)
if [ "$HTTP_CODE" = "201" ]; then
echo -e "${GREEN}✓ Cliente Frontend creado${NC}"
# Obtener ID del cliente recién creado
FRONTEND_CLIENT_EXISTS=$(curl -s -X GET "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients?clientId=anexo76-frontend" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json")
FRONTEND_CLIENT_ID=$(echo "$FRONTEND_CLIENT_EXISTS" | grep -o '"id":"[^"]*' | head -1 | sed 's/"id":"//')
else
echo -e "${RED}✗ Error al crear cliente Frontend (HTTP ${HTTP_CODE})${NC}"
fi
fi
###############################################################################
# 5. Configurar Mappers para tenant_id
###############################################################################
echo -e "\n${YELLOW}[5/8] Configurando mappers para tenant_id...${NC}"
# 4.1 Configurar mapper para Backend
if [ -n "$BACKEND_CLIENT_ID" ]; then
echo "Configurando mapper para Backend..."
# Obtener el dedicated scope del cliente backend
BACKEND_SCOPES=$(curl -s -X GET "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients/${BACKEND_CLIENT_ID}/optional-client-scopes" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json")
# Buscar el scope dedicado
BACKEND_DEDICATED_SCOPE_ID=$(curl -s -X GET "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/client-scopes" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" | grep -o "\"id\":\"[^\"]*\",\"name\":\"anexo76-backend-dedicated\"" | grep -o "\"id\":\"[^\"]*" | sed 's/"id":"//')
if [ -n "$BACKEND_DEDICATED_SCOPE_ID" ]; then
# Verificar si el mapper tenant_id ya existe
MAPPER_TENANT_EXISTS=$(curl -s -X GET "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/client-scopes/${BACKEND_DEDICATED_SCOPE_ID}/protocol-mappers/models" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" | grep -o "\"name\":\"tenant-id-mapper\"")
if [ -z "$MAPPER_TENANT_EXISTS" ]; then
# Crear mapper para tenant_id
curl -s -X POST "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/client-scopes/${BACKEND_DEDICATED_SCOPE_ID}/protocol-mappers/models" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "tenant-id-mapper",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-attribute-mapper",
"config": {
"user.attribute": "tenant_id",
"claim.name": "tenant_id",
"jsonType.label": "String",
"id.token.claim": "true",
"access.token.claim": "true",
"userinfo.token.claim": "true"
}
}'
echo -e "${GREEN}✓ Mapper tenant_id creado para Backend${NC}"
else
echo -e "${YELLOW}⚠ Mapper tenant_id ya existe para Backend${NC}"
fi
fi
fi
# 4.2 Configurar mapper para Frontend
if [ -n "$FRONTEND_CLIENT_ID" ]; then
echo "Configurando mapper para Frontend..."
# Buscar el scope dedicado del frontend
FRONTEND_DEDICATED_SCOPE_ID=$(curl -s -X GET "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/client-scopes" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" | grep -o "\"id\":\"[^\"]*\",\"name\":\"anexo76-frontend-dedicated\"" | grep -o "\"id\":\"[^\"]*" | sed 's/"id":"//')
if [ -n "$FRONTEND_DEDICATED_SCOPE_ID" ]; then
# Verificar si el mapper tenant_id ya existe
MAPPER_TENANT_EXISTS=$(curl -s -X GET "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/client-scopes/${FRONTEND_DEDICATED_SCOPE_ID}/protocol-mappers/models" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" | grep -o "\"name\":\"tenant-id-mapper\"")
if [ -z "$MAPPER_TENANT_EXISTS" ]; then
# Crear mapper para tenant_id
curl -s -X POST "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/client-scopes/${FRONTEND_DEDICATED_SCOPE_ID}/protocol-mappers/models" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "tenant-id-mapper",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-attribute-mapper",
"config": {
"user.attribute": "tenant_id",
"claim.name": "tenant_id",
"jsonType.label": "String",
"id.token.claim": "true",
"access.token.claim": "true",
"userinfo.token.claim": "true"
}
}'
echo -e "${GREEN}✓ Mapper tenant_id creado para Frontend${NC}"
else
echo -e "${YELLOW}⚠ Mapper tenant_id ya existe para Frontend${NC}"
fi
fi
fi
###############################################################################
# 6. Crear usuario demo en Keycloak
###############################################################################
echo -e "\n${YELLOW}[6/8] Creando usuario demo en Keycloak...${NC}"
# Verificar si el usuario ya existe
USER_EXISTS=$(curl -s -X GET "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users?username=${DEMO_USERNAME}" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json")
if echo "$USER_EXISTS" | grep -q "\"username\":\"${DEMO_USERNAME}\""; then
echo -e "${YELLOW}⚠ Usuario demo ya existe, actualizando...${NC}"
USER_ID=$(echo "$USER_EXISTS" | grep -o '"id":"[^"]*' | head -1 | sed 's/"id":"//')
# Actualizar usuario existente
curl -s -X PUT "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users/${USER_ID}" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"enabled\": true,
\"firstName\": \"${DEMO_FIRSTNAME}\",
\"lastName\": \"${DEMO_LASTNAME}\",
\"email\": \"${DEMO_EMAIL}\",
\"emailVerified\": true
}"
# Resetear contraseña
curl -s -X PUT "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users/${USER_ID}/reset-password" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"type\": \"password\",
\"value\": \"${DEMO_PASSWORD}\",
\"temporary\": false
}"
echo -e "${GREEN}✓ Usuario demo actualizado${NC}"
else
# Crear nuevo usuario con tenant_id por defecto
CREATE_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"username\": \"${DEMO_USERNAME}\",
\"enabled\": true,
\"firstName\": \"${DEMO_FIRSTNAME}\",
\"lastName\": \"${DEMO_LASTNAME}\",
\"email\": \"${DEMO_EMAIL}\",
\"emailVerified\": true,
\"attributes\": {
\"tenant_id\": [\"1\"]
},
\"credentials\": [{
\"type\": \"password\",
\"value\": \"${DEMO_PASSWORD}\",
\"temporary\": false
}]
}")
HTTP_CODE=$(echo "$CREATE_RESPONSE" | tail -n1)
if [ "$HTTP_CODE" = "201" ]; then
echo -e "${GREEN}✓ Usuario demo creado exitosamente${NC}"
# Obtener el ID del usuario recién creado
USER_RESPONSE=$(curl -s -X GET "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users?username=${DEMO_USERNAME}" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json")
USER_ID=$(echo "$USER_RESPONSE" | grep -o '"id":"[^"]*' | head -1 | sed 's/"id":"//')
else
echo -e "${RED}✗ Error al crear usuario (HTTP ${HTTP_CODE})${NC}"
echo "Respuesta: $CREATE_RESPONSE"
exit 1
fi
fi
echo -e "${GREEN}✓ Usuario: ${DEMO_USERNAME}${NC}"
echo -e "${GREEN}✓ Password: ${DEMO_PASSWORD}${NC}"
echo -e "${GREEN}✓ Keycloak User ID: ${USER_ID}${NC}"
###############################################################################
# 7. Agregar atributo tenant_id al usuario
###############################################################################
echo -e "\n${YELLOW}[7/8] Configurando atributo tenant_id para usuario demo...${NC}"
# Nota: El tenant_id se agregará después de crear el tenant en PostgreSQL
###############################################################################
# 8. Crear tenant y company en PostgreSQL
###############################################################################
echo -e "\n${YELLOW}[8/8] Creando tenant y company en PostgreSQL...${NC}"
# Esperar a que PostgreSQL esté listo
echo "Esperando a que PostgreSQL esté disponible..."
MAX_PG_RETRIES=30
PG_RETRY_COUNT=0
while [ $PG_RETRY_COUNT -lt $MAX_PG_RETRIES ]; do
if PGPASSWORD="${POSTGRES_PASSWORD}" psql -h "${POSTGRES_HOST}" -p "${POSTGRES_PORT}" -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -c "SELECT 1;" > /dev/null 2>&1; then
echo -e "${GREEN}✓ PostgreSQL está listo${NC}"
break
fi
PG_RETRY_COUNT=$((PG_RETRY_COUNT + 1))
echo "Intento $PG_RETRY_COUNT/$MAX_PG_RETRIES..."
sleep 2
done
if [ $PG_RETRY_COUNT -eq $MAX_PG_RETRIES ]; then
echo -e "${RED}✗ Error: PostgreSQL no está disponible${NC}"
exit 1
fi
# Insertar o actualizar tenant
PGPASSWORD="${POSTGRES_PASSWORD}" psql -h "${POSTGRES_HOST}" -p "${POSTGRES_PORT}" -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" <<EOF
INSERT INTO a76.tenants (name, slug, type, keycloak_realm, contact_email, is_active, created_at, updated_at)
VALUES ('${TENANT_NAME}', '${TENANT_SLUG}', 'SHARED', '${KEYCLOAK_REALM}', '${DEMO_EMAIL}', true, now(), now())
ON CONFLICT (slug)
DO UPDATE SET
name = EXCLUDED.name,
contact_email = EXCLUDED.contact_email,
updated_at = CURRENT_TIMESTAMP;
EOF
# Obtener el ID del tenant
TENANT_ID=$(PGPASSWORD="${POSTGRES_PASSWORD}" psql -h "${POSTGRES_HOST}" -p "${POSTGRES_PORT}" -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -t -c "SELECT id FROM a76.tenants WHERE slug = '${TENANT_SLUG}';")
TENANT_ID=$(echo "$TENANT_ID" | xargs) # Trim whitespace
if [ -z "$TENANT_ID" ]; then
echo -e "${RED}✗ Error: No se pudo obtener el ID del tenant${NC}"
exit 1
fi
echo -e "${GREEN}✓ Tenant ID: ${TENANT_ID}${NC}"
# Insertar company si no existe
COMPANY_EXISTS=$(PGPASSWORD="${POSTGRES_PASSWORD}" psql -h "${POSTGRES_HOST}" -p "${POSTGRES_PORT}" -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -t -c "SELECT COUNT(*) FROM a76.company WHERE tenant_id = ${TENANT_ID};")
COMPANY_EXISTS=$(echo "$COMPANY_EXISTS" | xargs)
if [ "$COMPANY_EXISTS" = "0" ]; then
PGPASSWORD="${POSTGRES_PASSWORD}" psql -h "${POSTGRES_HOST}" -p "${POSTGRES_PORT}" -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" <<EOF
INSERT INTO a76.company (tenant_id, name, rfc, is_service_company, created_at, updated_at)
VALUES (${TENANT_ID}, '${COMPANY_NAME}', '${COMPANY_RFC}', false, now(), now());
EOF
echo -e "${GREEN}✓ Company creada${NC}"
else
echo -e "${YELLOW}⚠ Company ya existe para este tenant${NC}"
fi
# Obtener información de la company
COMPANY_INFO=$(PGPASSWORD="${POSTGRES_PASSWORD}" psql -h "${POSTGRES_HOST}" -p "${POSTGRES_PORT}" -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -t -c "SELECT id, name FROM a76.company WHERE tenant_id = ${TENANT_ID} LIMIT 1;")
echo -e "${GREEN}✓ Company: ${COMPANY_INFO}${NC}"
# Agregar tenant_id al usuario demo en Keycloak
echo -e "\n${YELLOW}Asignando tenant_id al usuario demo...${NC}"
curl -s -X PUT "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users/${USER_ID}" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"attributes\": {
\"tenant_id\": [\"${TENANT_ID}\"]
}
}"
echo -e "${GREEN}✓ Atributo tenant_id asignado al usuario${NC}"
# Agregar relación usuario-tenant en la base de datos
echo -e "\n${YELLOW}Creando relación usuario-tenant en la base de datos...${NC}"
PGPASSWORD="${POSTGRES_PASSWORD}" psql -h "${POSTGRES_HOST}" -p "${POSTGRES_PORT}" -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" <<EOF
INSERT INTO a76.user_tenants (keycloak_user_id, tenant_id, company_id, role, is_active, created_at, updated_at)
VALUES ('${USER_ID}', ${TENANT_ID}, 1, 'admin', true, now(), now())
ON CONFLICT (keycloak_user_id, tenant_id, company_id)
DO UPDATE SET
is_active = true,
updated_at = CURRENT_TIMESTAMP;
EOF
echo -e "${GREEN}✓ Relación usuario-tenant creada en la base de datos${NC}"
###############################################################################
# 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}${DEMO_USERNAME}${NC}"
echo -e " Password: ${GREEN}${DEMO_PASSWORD}${NC}"
echo -e " Email: ${GREEN}${DEMO_EMAIL}${NC}"
echo -e "\n${YELLOW}Información del sistema:${NC}"
echo -e " Tenant: ${GREEN}${TENANT_NAME} (ID: ${TENANT_ID})${NC}"
echo -e " Realm: ${GREEN}${KEYCLOAK_REALM}${NC}"
echo -e " Keycloak URL: ${GREEN}${KEYCLOAK_URL}${NC}"
echo -e "\n${YELLOW}Clientes Keycloak configurados:${NC}"
echo -e " Backend Client ID: ${GREEN}anexo76-backend${NC}"
if [ -n "$BACKEND_SECRET" ]; then
echo -e " Backend Secret: ${GREEN}${BACKEND_SECRET}${NC}"
if [ -n "$ENV_FILE" ]; then
echo -e " ${GREEN}${NC} KEYCLOAK_CLIENT_SECRET actualizado en ${ENV_FILE}"
else
echo -e " ${YELLOW}${NC} Agregar manualmente al archivo .env como KEYCLOAK_CLIENT_SECRET"
fi
fi
echo -e " Frontend Client ID: ${GREEN}anexo76-frontend${NC}"
echo -e "\n${YELLOW}User Profile Attributes configurados:${NC}"
echo -e " ${GREEN}${NC} tenant_id (opcional, valor inicial: 1)"
echo -e "\n${YELLOW}Mappers configurados:${NC}"
echo -e " ${GREEN}${NC} tenant_id mapper para Backend"
echo -e " ${GREEN}${NC} tenant_id mapper para Frontend"
echo -e "\n${YELLOW}Configuración del usuario demo:${NC}"
echo -e " ${GREEN}${NC} tenant_id actualizado al ID real: ${TENANT_ID}"
echo -e " ${GREEN}${NC} Relación usuario-tenant creada (rol: admin)"
echo -e "\n${YELLOW}Puedes acceder al sistema en:${NC}"
echo -e " ${GREEN}http://localhost:5180${NC}"
echo -e "\n${GREEN}════════════════════════════════════════════════════════${NC}\n"