diff --git a/.env.example b/.env.example index 11e0d3eb..bcfa9c07 100644 --- a/.env.example +++ b/.env.example @@ -27,33 +27,45 @@ CORE_DB_NAME=anexo76_core CORE_DB_USER=postgres CORE_DB_PASSWORD=postgres +# Lista de orígenes permitidos (CORS) +# Ejemplo para Hub: http://localhost:5173,http://100.78.6.108:5174,http://100.78.6.108:8001 +CORS_ORIGINS=http://localhost:5173,http://localhost:3000 + +# ---- API Tipo cambio (External) --- +EXTERNAL_API_URL=http://74.208.80.245:3000 +EXTERNAL_API_USER=devAS +EXTERNAL_API_PASSWORD=T6Y4mqP09Y[-2-3H + +# ----- API SITAR (Warnings) --- +SITAR_API_URL="" +SITAR_API_USER="" +SITAR_API_PASSWORD="" + # ----- Frontend ----- NODE_ENV=development VITE_API_URL=http://localhost:8000/api INTERNAL_API_URL=http://backend:8000/api VITE_KEYCLOAK_REALM=master -VITE_KEYCLOAK_URL=http://localhost:8080 +VITE_KEYCLOAK_URL=http://localhost:8080/kcauth VITE_KEYCLOAK_CLIENT_ID=anexo76-frontend -# ----- Sitar API ----- -SITAR_API_URL=http://api.sitar.aduanasoft.com -SITAR_API_USER=your_sitar_user -SITAR_API_PASSWORD=your_sitar_password +#------ Celery / Valkey ---------- +VALKEY_URL=redis://valkey:6379/0 -# ----- Help Center (Sincronización) ----- -# Si es CLIENTE, establecer la URL del Hub: +# ================================== +# CONFIGURACIÓN DE SINCRONIZACIÓN +# ================================== +# Hub IP: 100.78.6.108 + +# URL del Hub para sincronización (Solo si es CLIENTE) # Ejemplo: http://100.78.6.108:8001/api/v1/core/help-center/sync/ CENTRAL_SERVER_URL= -# Si es HUB, establecer las URLs de los clientes (separadas por comas): -SPOKE_URLS= - -# Token de seguridad para la sincronización (debe ser el mismo en Hub y Clientes) -SYNC_SECRET_TOKEN=change-this-sync-token-in-production - -# ID único del cliente (opcional, generado automáticamente si está vacío) +# UUID único de este cliente (Opcional, se genera uno si está vacío) CLIENT_UUID= -CORS_ORIGINS=http://localhost:5173,http://localhost:3000 - +# Token de seguridad compartido (Debe ser IDÉNTICO en Hub y Clientes) +SYNC_SECRET_TOKEN=change-this-sync-token-in-production +# Lista de spokes (Solo si es HUB y desea retransmitir a otros - Opcional) +SPOKE_URLS="" diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 4eabf108..ccb78bdb 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -17,7 +17,7 @@ services: - backend-net restart: unless-stopped healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres -d anexo76_core || exit 1"] + test: [ "CMD-SHELL", "pg_isready -U postgres -d anexo76_core || exit 1" ] interval: 5s timeout: 3s retries: 10 @@ -37,7 +37,7 @@ services: # PostgreSQL - Base de datos Keycloak postgres-keycloak: - image: postgres:18-alpine + image: postgres:16-alpine container_name: anexo76-postgres-keycloak environment: POSTGRES_DB: keycloak @@ -54,7 +54,7 @@ services: - backend-net restart: unless-stopped healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres -d keycloak || exit 1"] + test: [ "CMD-SHELL", "pg_isready -U postgres -d keycloak || exit 1" ] interval: 5s timeout: 3s retries: 10 @@ -80,10 +80,10 @@ services: KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN:-admin} KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD:-admin} KC_DB: postgres - KC_DB_URL_HOST: postgres-keycloak + KC_DB_URL_HOST: anexo76-postgres-keycloak KC_DB_URL_PORT: "5432" KC_DB_URL_DATABASE: keycloak - KC_DB_URL: jdbc:postgresql://postgres-keycloak:5432/keycloak + KC_DB_URL: jdbc:postgresql://anexo76-postgres-keycloak:5432/keycloak KC_DB_USERNAME: postgres KC_DB_PASSWORD: ${POSTGRES_KEYCLOAK_PASSWORD:-postgres} KC_DB_SCHEMA: public @@ -96,12 +96,12 @@ services: KC_HEALTH_ENABLED: "true" KC_METRICS_ENABLED: "true" KC_HOSTNAME_PATH: /kcauth - KC_LOG_LEVEL: INFO + KC_LOG_LEVEL: INFO JAVA_OPTS_APPEND: "-Xms256m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true" - command: + command: - start - --db=postgres - - --db-url-host=postgres-keycloak + - --db-url-host=anexo76-postgres-keycloak - --http-relative-path=/kcauth - --db-url-port=5432 - --db-url-database=keycloak @@ -123,7 +123,19 @@ services: - backend-net restart: unless-stopped healthcheck: - test: ["CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/9000; echo -e 'GET /kcauth/health/ready HTTP/1.1\r\nhost: 127.0.0.1\r\nConnection: close\r\n\r\n' >&3; grep -q 'HTTP/1.1 200' <&3 || exit 1"] + test: + [ + "CMD-SHELL", + "exec 3<>/dev/tcp/127.0.0.1/9000; echo -e 'GET /kcauth/health/ready HTTP/1.1\r + + host: 127.0.0.1\r + + Connection: close\r + + \r + + ' >&3; grep -q 'HTTP/1.1 200' <&3 || exit 1" + ] interval: 10s timeout: 5s retries: 30 @@ -162,6 +174,14 @@ services: - SITAR_API_URL=${SITAR_API_URL} - SITAR_API_USER=${SITAR_API_USER} - SITAR_API_PASSWORD=${SITAR_API_PASSWORD} + - EXTERNAL_API_URL=${EXTERNAL_API_URL} + - EXTERNAL_API_USER=${EXTERNAL_API_USER} + - EXTERNAL_API_PASSWORD=${EXTERNAL_API_PASSWORD} + - VALKEY_URL=${VALKEY_URL:-redis://valkey:6379/0} + - CENTRAL_SERVER_URL=${CENTRAL_SERVER_URL:-""} + - SYNC_SECRET_TOKEN=${SYNC_SECRET_TOKEN:-change-this-sync-token-in-production} + - CLIENT_UUID=${CLIENT_UUID:-""} + - SPOKE_URLS=${SPOKE_URLS:-""} ports: - "3467:8000" depends_on: @@ -176,24 +196,10 @@ services: - backend-net - frontend-net restart: unless-stopped - entrypoint: ["/entrypoint.sh"] - command: - [ - "gunicorn", - "main:app", - "-k", - "uvicorn.workers.UvicornWorker", - "-w", - "${WEB_CONCURRENCY:-1}", - "-b", - "0.0.0.0:8000", - "--log-level", - "info", - "--forwarded-allow-ips", - "*" - ] + entrypoint: [ "/entrypoint.sh" ] + command: [ "gunicorn", "main:app", "-k", "uvicorn.workers.UvicornWorker", "-w", "${WEB_CONCURRENCY:-1}", "-b", "0.0.0.0:8000", "--log-level", "info", "--forwarded-allow-ips", "*" ] healthcheck: - test: ["CMD-SHELL", "curl -f http://localhost:8000/api/health || exit 1"] + test: [ "CMD-SHELL", "curl -f http://localhost:8000/api/health || exit 1" ] interval: 15s timeout: 5s retries: 5 @@ -216,7 +222,16 @@ services: container_name: worker command: celery -A core.celery_app worker --loglevel=info environment: - - VALKEY_URL=redis://valkey:6379/0 + - VALKEY_URL=${VALKEY_URL:-redis://valkey:6379/0} + - CENTRAL_SERVER_URL=${CENTRAL_SERVER_URL:-""} + - SYNC_SECRET_TOKEN=${SYNC_SECRET_TOKEN:-change-this-sync-token-in-production} + - CLIENT_UUID=${CLIENT_UUID:-""} + - SPOKE_URLS=${SPOKE_URLS:-""} + - CORE_DB_HOST=${CORE_DB_HOST:-postgres-a76} + - CORE_DB_PORT=${CORE_DB_PORT:-5432} + - CORE_DB_NAME=${CORE_DB_NAME:-anexo76_core} + - CORE_DB_USER=${CORE_DB_USER:-postgres} + - CORE_DB_PASSWORD=${POSTGRES_APP_PASSWORD:-postgres} depends_on: - backend - valkey @@ -252,7 +267,7 @@ services: depends_on: backend: condition: service_healthy - entrypoint: ["/frontend-entrypoint.sh"] + entrypoint: [ "/frontend-entrypoint.sh" ] volumes: - ./scripts/frontend-entrypoint.sh:/frontend-entrypoint.sh:ro networks: @@ -260,9 +275,9 @@ services: - backend-net - auth-net restart: unless-stopped - command: ["pnpm", "start"] + command: [ "pnpm", "start" ] healthcheck: - test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:5173/ || exit 1"] + test: [ "CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:5173/ || exit 1" ] interval: 15s timeout: 5s retries: 5 diff --git a/docker-compose.yml b/docker-compose.yml index 20fd3b30..9efbf03d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,6 +8,7 @@ services: POSTGRES_USER: postgres POSTGRES_PASSWORD: ${POSTGRES_APP_PASSWORD:-postgres} POSTGRES_INITDB_ARGS: "--encoding=UTF8" + PGDATA: /var/lib/postgresql/data ports: - "5432:5432" volumes: @@ -37,7 +38,7 @@ services: # PostgreSQL - Base de datos Keycloak postgres-keycloak: - image: postgres:18-alpine + image: postgres:16-alpine container_name: anexo76-postgres-keycloak environment: POSTGRES_DB: keycloak @@ -80,10 +81,10 @@ services: KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN:-admin} KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD:-admin} KC_DB: postgres - KC_DB_URL_HOST: postgres-keycloak + KC_DB_URL_HOST: anexo76-postgres-keycloak KC_DB_URL_PORT: "5432" KC_DB_URL_DATABASE: keycloak - KC_DB_URL: jdbc:postgresql://postgres-keycloak:5432/keycloak + KC_DB_URL: jdbc:postgresql://anexo76-postgres-keycloak:5432/keycloak KC_DB_USERNAME: postgres KC_DB_PASSWORD: ${POSTGRES_KEYCLOAK_PASSWORD:-postgres} KC_DB_SCHEMA: public @@ -101,7 +102,7 @@ services: - start-dev - --http-relative-path=/kcauth - --db=postgres - - --db-url-host=postgres-keycloak + - --db-url-host=anexo76-postgres-keycloak - --db-url-port=5432 - --db-url-database=keycloak - --db-username=postgres @@ -174,14 +175,18 @@ services: - KEYCLOAK_REALM=${KEYCLOAK_REALM:-master} - KEYCLOAK_CLIENT_ID=${KEYCLOAK_CLIENT_ID:-anexo76-backend} - KEYCLOAK_CLIENT_SECRET=${KEYCLOAK_CLIENT_SECRET:-dev-secret} - - SYNC_SECRET_TOKEN=${SYNC_SECRET_TOKEN:-d2Navi4c3cdrdK92oE3iiGuXn0T4dY9k} - - CENTRAL_SERVER_URL=${CENTRAL_SERVER_URL} # Variable clave para definir si es Cliente o Hub - - SPOKE_URLS=${SPOKE_URLS} - - CLIENT_UUID=${CLIENT_UUID} - CORS_ORIGINS=${CORS_ORIGINS:-http://localhost:5173,http://localhost:3000} - SITAR_API_URL=${SITAR_API_URL} - SITAR_API_USER=${SITAR_API_USER} - SITAR_API_PASSWORD=${SITAR_API_PASSWORD} + - EXTERNAL_API_URL=${EXTERNAL_API_URL} + - EXTERNAL_API_USER=${EXTERNAL_API_USER} + - EXTERNAL_API_PASSWORD=${EXTERNAL_API_PASSWORD} + - VALKEY_URL=${VALKEY_URL:-redis://valkey:6379/0} + - CENTRAL_SERVER_URL=${CENTRAL_SERVER_URL:-""} + - SYNC_SECRET_TOKEN=${SYNC_SECRET_TOKEN:-change-this-sync-token-in-production} + - CLIENT_UUID=${CLIENT_UUID:-""} + - SPOKE_URLS=${SPOKE_URLS:-""} ports: - "8000:8000" depends_on: @@ -274,13 +279,18 @@ services: celery_worker: build: ./backend container_name: worker - command: celery -A core.celery_app worker --loglevel=info -B + command: celery -A core.celery_app worker --loglevel=info environment: - - VALKEY_URL=redis://valkey:6379/0 - - CENTRAL_SERVER_URL=${CENTRAL_SERVER_URL} - - SPOKE_URLS=${SPOKE_URLS} - - CLIENT_UUID=${CLIENT_UUID} - - SYNC_SECRET_TOKEN=${SYNC_SECRET_TOKEN:-d2Navi4c3cdrdK92oE3iiGuXn0T4dY9k} + - VALKEY_URL=${VALKEY_URL:-redis://valkey:6379/0} + - CENTRAL_SERVER_URL=${CENTRAL_SERVER_URL:-""} + - SYNC_SECRET_TOKEN=${SYNC_SECRET_TOKEN:-change-this-sync-token-in-production} + - CLIENT_UUID=${CLIENT_UUID:-""} + - SPOKE_URLS=${SPOKE_URLS:-""} + - CORE_DB_HOST=${CORE_DB_HOST:-postgres-a76} + - CORE_DB_PORT=${CORE_DB_PORT:-5432} + - CORE_DB_NAME=${CORE_DB_NAME:-anexo76_core} + - CORE_DB_USER=${CORE_DB_USER:-postgres} + - CORE_DB_PASSWORD=${POSTGRES_APP_PASSWORD:-postgres} depends_on: - backend - valkey @@ -313,16 +323,7 @@ volumes: networks: backend-net: driver: bridge - ipam: - config: - - subnet: 172.20.0.0/16 auth-net: driver: bridge - ipam: - config: - - subnet: 172.21.0.0/16 frontend-net: driver: bridge - ipam: - config: - - subnet: 172.22.0.0/16 diff --git a/scripts/init_first_time.sh b/scripts/init_first_time.sh index ac6b57cf..11f01457 100755 --- a/scripts/init_first_time.sh +++ b/scripts/init_first_time.sh @@ -15,7 +15,7 @@ # # Requisitos: # - Keycloak corriendo en http://localhost:8080 -# - PostgreSQL corriendo en localhost:5432 +# - PostgreSQL corriendo en localhost:5432 o 5939 # - Base de datos anexo76_core creada # - jq instalado (para procesamiento JSON) # @@ -128,7 +128,7 @@ KEYCLOAK_URL="${KEYCLOAK_URL:-http://localhost:8080/kcauth}" 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/kcauth}" +KEYCLOAK_ADMIN_URL="${KEYCLOAK_ADMIN_URL:-http://localhost:9000/kcauth}" POSTGRES_HOST="${POSTGRES_HOST:-localhost}" POSTGRES_PORT="${POSTGRES_PORT:-5432}" @@ -159,7 +159,7 @@ 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 + if curl -s -f "${KEYCLOAK_ADMIN_URL}/health/ready" > /dev/null 2>&1; then echo -e "${GREEN}✓ Keycloak está listo${NC}" break fi @@ -351,7 +351,7 @@ if echo "$FRONTEND_CLIENT_EXISTS" | jq -e '.[] | select(.clientId == "anexo76-fr echo -e "${YELLOW}⚠ Cliente Frontend ya existe, actualizando configuración...${NC}" FRONTEND_CLIENT_ID=$(echo "$FRONTEND_CLIENT_EXISTS" | jq -r '.[0].id') - # Actualizar configuración del cliente existente para incluir puerto 5174 + # Actualizar configuración del cliente existente para incluir puertos nuevos UPDATE_FRONTEND=$(curl -s -w "\n%{http_code}" -X PUT "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients/${FRONTEND_CLIENT_ID}" \ -H "Authorization: Bearer ${ACCESS_TOKEN}" \ -H "Content-Type: application/json" \ @@ -384,7 +384,7 @@ if echo "$FRONTEND_CLIENT_EXISTS" | jq -e '.[] | select(.clientId == "anexo76-fr HTTP_CODE=$(echo "$UPDATE_FRONTEND" | tail -n1) if [ "$HTTP_CODE" = "204" ] || [ "$HTTP_CODE" = "200" ]; then - echo -e "${GREEN}✓ Cliente Frontend actualizado (Puerto 5174 agregado)${NC}" + echo -e "${GREEN}✓ Cliente Frontend actualizado (Puertos actualizados)${NC}" else echo -e "${RED}✗ Error al actualizar cliente Frontend (HTTP ${HTTP_CODE})${NC}" fi diff --git a/start.sh b/start.sh index f3e1a213..e699ea4d 100755 --- a/start.sh +++ b/start.sh @@ -60,14 +60,43 @@ fi echo -e "${GREEN}✓ Docker está instalado y corriendo${NC}" echo "" -# 2. Crear archivo .env si no existe +# 2. Configurar variables de entorno echo -e "${BLUE}[2/7] Configurando variables de entorno...${NC}" + if [ ! -f .env ]; then + echo -e "${YELLOW}¿Cómo desea configurar este servidor?${NC}" + echo "1) Hub (Servidor Central)" + echo "2) Cliente (Sucursal/Spoke)" + read -p "Seleccione una opción [1-2]: " SERVER_ROLE + + # Valores por defecto comunes + SYNC_TOKEN=$(openssl rand -hex 16 2>/dev/null || echo "dev-sync-token-$(date +%s)") + + if [ "$SERVER_ROLE" == "1" ]; then + echo -e "${GREEN}Configurando como HUB...${NC}" + CENTRAL_URL="" + CLIENT_ID="" + SPOKE_URLS="" + else + echo -e "${GREEN}Configurando como CLIENTE...${NC}" + read -p "Ingrese la URL del Hub [http://100.78.6.108:8001/api/v1/core/help-center/sync/]: " CENTRAL_URL + CENTRAL_URL=${CENTRAL_URL:-http://100.78.6.108:8001/api/v1/core/help-center/sync/} + read -p "Ingrese el UUID de este cliente (presione Enter para generar uno): " CLIENT_ID + CLIENT_ID=${CLIENT_ID:-$(cat /proc/sys/kernel/random/uuid 2>/dev/null || echo "client-$(date +%s)")} + SPOKE_URLS="" + fi + if [ -f .env.example ]; then cp .env.example .env - echo -e "${GREEN}✓ Archivo .env creado${NC}" + echo -e "${GREEN}✓ Archivo .env basado en .env.example creado${NC}" + + # Aplicar configuraciones de rol al .env copiado + sed -i "s|CENTRAL_SERVER_URL=.*|CENTRAL_SERVER_URL=$CENTRAL_URL|" .env + sed -i "s|SPOKE_URLS=.*|SPOKE_URLS=$SPOKE_URLS|" .env + sed -i "s|CLIENT_UUID=.*|CLIENT_UUID=$CLIENT_ID|" .env + sed -i "s|SYNC_SECRET_TOKEN=.*|SYNC_SECRET_TOKEN=$SYNC_TOKEN|" .env else - echo -e "${YELLOW}⚠ .env.example no existe, creando .env con valores por defecto${NC}" + echo -e "${YELLOW}⚠ .env.example no existe, creando .env básico${NC}" cat > .env <