fix(cors): Resolve duplicate CORS headers in production
## Problem CORS error: "Access-Control-Allow-Origin header contains multiple values" caused by both Nginx and NestJS sending CORS headers. ## Solution - NestJS handles CORS exclusively (main.ts) - Nginx acts as SSL proxy only (no CORS headers) - Updated .env.production.example with HTTPS origins - Created GUIA-CORS-PRODUCCION.md with complete documentation ## Files Changed - .gitignore: Allow .env.*.example files - apps/backend/.gitignore: Allow .env.*.example files - apps/backend/.env.production.example: HTTPS CORS config - apps/frontend/.env.production.example: HTTPS/WSS config - docs/95-guias-desarrollo/GUIA-CORS-PRODUCCION.md: Full guide ## Production Steps 1. Update .env.production files with HTTPS origins 2. Remove CORS headers from Nginx config 3. Rebuild frontend, restart backend 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
d0d5699cd5
commit
8b12d7f231
353
projects/gamilit/.gitignore
vendored
353
projects/gamilit/.gitignore
vendored
@ -1,148 +1,106 @@
|
||||
# GAMILIT Monorepo - .gitignore
|
||||
# Generado: 2025-11-01 (RFC-0001)
|
||||
# Actualizado: 2025-12-05
|
||||
|
||||
# ============================================
|
||||
# NODE.JS - DEPENDENCIAS (GLOBAL)
|
||||
# ============================================
|
||||
# Ignorar node_modules en CUALQUIER nivel de anidación
|
||||
**/node_modules/
|
||||
|
||||
# Lock files de otros package managers (mantener solo package-lock.json)
|
||||
yarn.lock
|
||||
pnpm-lock.yaml
|
||||
bun.lockb
|
||||
|
||||
# Logs de npm/yarn/pnpm
|
||||
# === NODE.JS ===
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Directorios de dependencias alternativas
|
||||
# Dependency directories
|
||||
jspm_packages/
|
||||
bower_components/
|
||||
|
||||
# Cache de npm
|
||||
.npm/
|
||||
.npmrc.local
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Cache de eslint/stylelint
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# ============================================
|
||||
# TYPESCRIPT / BUILD ARTIFACTS (GLOBAL)
|
||||
# ============================================
|
||||
# Ignorar dist/build en CUALQUIER nivel
|
||||
**/dist/
|
||||
**/build/
|
||||
**/out/
|
||||
**/.next/
|
||||
**/.nuxt/
|
||||
**/.turbo/
|
||||
|
||||
# TypeScript
|
||||
# === TYPESCRIPT ===
|
||||
*.tsbuildinfo
|
||||
**/*.tsbuildinfo
|
||||
dist/
|
||||
build/
|
||||
*.js.map
|
||||
|
||||
# ============================================
|
||||
# FRAMEWORKS ESPECÍFICOS
|
||||
# ============================================
|
||||
# Angular / NX
|
||||
# === ANGULAR / NX ===
|
||||
.angular/
|
||||
.nx/
|
||||
**/.nx/
|
||||
.nx/cache/
|
||||
.nx/workspace-data/
|
||||
|
||||
# Vite
|
||||
**/.vite/
|
||||
# === NESTJS ===
|
||||
/apps/backend/dist/
|
||||
/apps/backend/build/
|
||||
|
||||
# Webpack
|
||||
.webpack/
|
||||
**/.webpack/
|
||||
|
||||
# ============================================
|
||||
# ENVIRONMENT FILES - SECRETS
|
||||
# ============================================
|
||||
# CRÍTICO: Nunca commitear secrets
|
||||
# === ENVIRONMENT FILES ===
|
||||
# IMPORTANTE: Nunca commitear secrets reales
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
.env.production
|
||||
.env.development
|
||||
.env.test
|
||||
.env.staging
|
||||
# Permitir archivos de ejemplo (sin secrets)
|
||||
!.env.*.example
|
||||
!.env.example
|
||||
|
||||
# Archivos con secrets
|
||||
**/secrets.json
|
||||
**/credentials.json
|
||||
# Archivos de configuración con secrets
|
||||
config/secrets.json
|
||||
config/credentials.json
|
||||
**/*secrets*.json
|
||||
**/*credentials*.json
|
||||
**/*.secret
|
||||
**/*.secrets
|
||||
|
||||
# Configuración de base de datos con credenciales
|
||||
**/database.config.ts
|
||||
!**/database.config.example.ts
|
||||
**/ormconfig.json
|
||||
!**/ormconfig.example.json
|
||||
|
||||
# ============================================
|
||||
# DATABASES
|
||||
# ============================================
|
||||
# Backups y dumps
|
||||
# === DATABASES ===
|
||||
# PostgreSQL
|
||||
*.sql.backup
|
||||
*.dump
|
||||
*.pgdata
|
||||
*.sql.gz
|
||||
|
||||
# Bases de datos locales
|
||||
# Local database files
|
||||
*.sqlite
|
||||
*.sqlite3
|
||||
*.db
|
||||
*.db-journal
|
||||
|
||||
# Redis
|
||||
dump.rdb
|
||||
# Database connection strings (excepción: ejemplos con .example)
|
||||
database.config.ts
|
||||
!database.config.example.ts
|
||||
|
||||
# ============================================
|
||||
# LOGS (GLOBAL)
|
||||
# ============================================
|
||||
**/logs/
|
||||
**/*.log
|
||||
**/pm2-logs/
|
||||
**/*.pm2.log
|
||||
# === LOGS ===
|
||||
logs/
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# ============================================
|
||||
# TESTING (GLOBAL)
|
||||
# ============================================
|
||||
**/coverage/
|
||||
**/.nyc_output/
|
||||
# PM2 logs
|
||||
pm2-logs/
|
||||
*.pm2.log
|
||||
|
||||
# === TESTING ===
|
||||
coverage/
|
||||
.nyc_output/
|
||||
*.lcov
|
||||
|
||||
# Jest
|
||||
**/jest-cache/
|
||||
.jest/
|
||||
jest-cache/
|
||||
|
||||
# Cypress
|
||||
**/cypress/screenshots/
|
||||
**/cypress/videos/
|
||||
**/cypress/downloads/
|
||||
|
||||
# Playwright
|
||||
**/playwright-report/
|
||||
**/playwright/.cache/
|
||||
**/test-results/
|
||||
cypress/screenshots/
|
||||
cypress/videos/
|
||||
cypress/downloads/
|
||||
|
||||
# E2E reports
|
||||
**/e2e-reports/
|
||||
e2e-reports/
|
||||
test-results/
|
||||
|
||||
# ============================================
|
||||
# IDEs and EDITORS
|
||||
# ============================================
|
||||
# VSCode - mantener configuración compartida
|
||||
# === IDEs and EDITORS ===
|
||||
# VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
@ -161,117 +119,92 @@ dump.rdb
|
||||
*.sublime-workspace
|
||||
*.sublime-project
|
||||
|
||||
# Vim/Neovim
|
||||
# Vim
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.netrwhist
|
||||
Session.vim
|
||||
|
||||
# Emacs
|
||||
*~
|
||||
\#*\#
|
||||
.\#*
|
||||
*.elc
|
||||
auto-save-list/
|
||||
|
||||
# ============================================
|
||||
# OS FILES
|
||||
# ============================================
|
||||
# === OS FILES ===
|
||||
# macOS
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
|
||||
# Linux
|
||||
*~
|
||||
.directory
|
||||
.Trash-*
|
||||
|
||||
# Windows
|
||||
Thumbs.db
|
||||
Thumbs.db:encryptable
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
Desktop.ini
|
||||
$RECYCLE.BIN/
|
||||
*.lnk
|
||||
|
||||
# ============================================
|
||||
# DOCKER
|
||||
# ============================================
|
||||
# === DOCKER ===
|
||||
# No ignorar Dockerfiles, solo archivos temporales
|
||||
docker-compose.override.yml
|
||||
docker-compose.local.yml
|
||||
.dockerignore.local
|
||||
**/*.dockerignore.local
|
||||
|
||||
# ============================================
|
||||
# DEPLOYMENT & SECURITY
|
||||
# ============================================
|
||||
# === BUILD ARTIFACTS ===
|
||||
/apps/*/dist/
|
||||
/apps/*/build/
|
||||
/libs/*/dist/
|
||||
|
||||
# Webpack
|
||||
.webpack/
|
||||
|
||||
# === DEPLOYMENT ===
|
||||
# PM2
|
||||
ecosystem.config.js.local
|
||||
pm2.config.js.local
|
||||
|
||||
# Claves y certificados
|
||||
# Deploy keys (excepción: .example files)
|
||||
*.pem
|
||||
*.key
|
||||
*.p12
|
||||
*.pfx
|
||||
!*.example.pem
|
||||
!*.example.key
|
||||
|
||||
# SSL certificates
|
||||
# SSL certificates (excepción: self-signed para dev)
|
||||
*.crt
|
||||
*.cer
|
||||
!dev-cert.crt
|
||||
!*.example.crt
|
||||
|
||||
# SSH keys
|
||||
id_rsa*
|
||||
id_ed25519*
|
||||
*.pub
|
||||
!*.example.pub
|
||||
# === TEMP FILES ===
|
||||
tmp/
|
||||
temp/
|
||||
*.tmp
|
||||
*.temp
|
||||
*.cache
|
||||
|
||||
# ============================================
|
||||
# TEMP FILES (GLOBAL)
|
||||
# ============================================
|
||||
**/tmp/
|
||||
**/temp/
|
||||
**/.tmp/
|
||||
**/.temp/
|
||||
**/*.tmp
|
||||
**/*.temp
|
||||
**/*.cache
|
||||
**/.cache/
|
||||
|
||||
# ============================================
|
||||
# ARTIFACTS
|
||||
# ============================================
|
||||
# === ARTIFACTS (parcial) ===
|
||||
# Mantener reportes importantes, ignorar temporales
|
||||
/artifacts/temp/
|
||||
/artifacts/cache/
|
||||
/artifacts/**/*.tmp
|
||||
/artifacts/**/*.cache
|
||||
|
||||
# ============================================
|
||||
# CLAUDE CODE / AI
|
||||
# ============================================
|
||||
# Configuración local de Claude Code
|
||||
# === CLAUDE CODE ===
|
||||
# Excluir toda la carpeta .claude (configuración local de IA)
|
||||
.claude/
|
||||
|
||||
# Pero NO ignorar orchestration (necesario para Claude Code cloud)
|
||||
# Solo ignorar temporales dentro de orchestration
|
||||
# === ORCHESTRATION ===
|
||||
# IMPORTANTE: orchestration/ DEBE estar en el repo para Claude Code cloud
|
||||
# Contiene: prompts, directivas, trazas, inventarios, templates
|
||||
# Solo ignorar subcarpetas temporales específicas y archivos comprimidos
|
||||
orchestration/.archive/
|
||||
orchestration/.tmp/
|
||||
orchestration/**/*.tmp
|
||||
orchestration/**/*.cache
|
||||
|
||||
# ============================================
|
||||
# REFERENCE (Código de Referencia)
|
||||
# ============================================
|
||||
# reference/ DEBE estar en el repo
|
||||
# Solo ignorar build/dependencias dentro
|
||||
# === REFERENCE (Código de Referencia) ===
|
||||
# IMPORTANTE: reference/ DEBE estar en el repo para Claude Code cloud
|
||||
# Contiene: proyectos de referencia para análisis y desarrollo
|
||||
# Ignorar solo carpetas de build/dependencias dentro de reference/
|
||||
reference/**/node_modules/
|
||||
reference/**/dist/
|
||||
reference/**/build/
|
||||
@ -286,9 +219,16 @@ reference/**/*.tmp
|
||||
reference/**/*.cache
|
||||
reference/**/.DS_Store
|
||||
|
||||
# ============================================
|
||||
# PACKAGE MANAGERS
|
||||
# ============================================
|
||||
# === MIGRATION (temporal) ===
|
||||
# Durante migración, mantener docs de análisis
|
||||
# Descomentar después de migración completa:
|
||||
# /docs-analysis/
|
||||
|
||||
# === ARCHIVOS ESPECÍFICOS GRANDES ===
|
||||
# Si hay archivos markdown extremadamente grandes (>50MB), considerarlos para LFS
|
||||
# *.large.md
|
||||
|
||||
# === PACKAGE MANAGERS ===
|
||||
# Yarn
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
@ -301,102 +241,33 @@ reference/**/.DS_Store
|
||||
# PNPM
|
||||
.pnpm-store/
|
||||
|
||||
# ============================================
|
||||
# MONITORING & OBSERVABILITY
|
||||
# ============================================
|
||||
# === MONITORING ===
|
||||
# Application monitoring
|
||||
newrelic_agent.log
|
||||
.monitors/
|
||||
**/.sentry/
|
||||
**/sentry-debug.log
|
||||
|
||||
# ============================================
|
||||
# BACKUPS (GLOBAL)
|
||||
# ============================================
|
||||
# Archivos
|
||||
# === MISC ===
|
||||
# Backups - Archivos
|
||||
*.backup
|
||||
*.bak
|
||||
*.old
|
||||
*.orig
|
||||
|
||||
# Carpetas
|
||||
**/*_old/
|
||||
**/*_bckp/
|
||||
**/*_bkp/
|
||||
**/*_backup/
|
||||
**/*.old/
|
||||
**/*.bak/
|
||||
**/*.backup/
|
||||
# Backups - Carpetas
|
||||
*_old/
|
||||
*_bckp/
|
||||
*_bkp/
|
||||
*_backup/
|
||||
*.old/
|
||||
*.bak/
|
||||
*.backup/
|
||||
|
||||
# Específicos del proyecto
|
||||
# Backups específicos (carpetas identificadas en workspace)
|
||||
orchestration_old/
|
||||
orchestration_bckp/
|
||||
docs_bkp/
|
||||
|
||||
# ============================================
|
||||
# COMPRESSED FILES
|
||||
# ============================================
|
||||
# Compressed files (si no son assets del proyecto)
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.tar
|
||||
*.rar
|
||||
*.7z
|
||||
# Excepto assets
|
||||
!assets/**/*.zip
|
||||
!public/**/*.zip
|
||||
|
||||
# ============================================
|
||||
# MISCELÁNEOS
|
||||
# ============================================
|
||||
# Archivos de debug
|
||||
**/*.debug
|
||||
**/debug.log
|
||||
|
||||
# Archivos de error
|
||||
**/*.error
|
||||
**/error.log
|
||||
|
||||
# Storybook
|
||||
**/storybook-static/
|
||||
|
||||
# Parcel
|
||||
.parcel-cache/
|
||||
|
||||
# Serverless
|
||||
.serverless/
|
||||
|
||||
# FuseBox
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local
|
||||
.dynamodb/
|
||||
|
||||
# TernJS
|
||||
.tern-port
|
||||
|
||||
# Archivos generados por IDEs
|
||||
*.code-workspace
|
||||
|
||||
# Archivos de licencia generados
|
||||
LICENSE.txt.bak
|
||||
|
||||
# ============================================
|
||||
# APPS ESPECÍFICAS DEL MONOREPO
|
||||
# ============================================
|
||||
# Backend NestJS
|
||||
apps/backend/dist/
|
||||
apps/backend/build/
|
||||
apps/backend/.nest/
|
||||
|
||||
# Frontend React/Vite
|
||||
apps/frontend/dist/
|
||||
apps/frontend/build/
|
||||
apps/frontend/.vite/
|
||||
|
||||
# Database - mantener DDL, ignorar generados
|
||||
apps/database/*.dump
|
||||
apps/database/*.backup
|
||||
apps/database/data/
|
||||
|
||||
# DevOps - ignorar logs y temp
|
||||
apps/devops/logs/
|
||||
apps/devops/tmp/
|
||||
|
||||
137
projects/gamilit/apps/backend/.env.production.example
Normal file
137
projects/gamilit/apps/backend/.env.production.example
Normal file
@ -0,0 +1,137 @@
|
||||
# ============================================================================
|
||||
# GAMILIT Backend - Production Environment Variables (EXAMPLE)
|
||||
# ============================================================================
|
||||
# INSTRUCCIONES:
|
||||
# 1. Copiar este archivo a .env.production
|
||||
# 2. Reemplazar todos los valores <...> con valores reales
|
||||
# 3. Configurar como variables de entorno del sistema (recomendado)
|
||||
# O usar archivo .env.production (asegurar permisos restrictivos)
|
||||
|
||||
# ==================== SERVER ====================
|
||||
NODE_ENV=production
|
||||
PORT=3006
|
||||
API_PREFIX=api
|
||||
|
||||
# ==================== DATABASE ====================
|
||||
# IMPORTANTE: Usar credenciales seguras en producción
|
||||
DB_HOST=localhost
|
||||
DB_PORT=5432
|
||||
DB_NAME=gamilit_platform
|
||||
DB_USER=gamilit_user
|
||||
DB_PASSWORD=<PASSWORD_SEGURO_AQUI>
|
||||
DB_SYNCHRONIZE=false
|
||||
DB_LOGGING=false
|
||||
|
||||
# ==================== JWT ====================
|
||||
# IMPORTANTE: NUNCA usar valores de ejemplo en producción
|
||||
# Generar secret seguro con: openssl rand -base64 32
|
||||
JWT_SECRET=<GENERAR_SECRET_SEGURO_AQUI>
|
||||
JWT_EXPIRES_IN=15m
|
||||
JWT_REFRESH_EXPIRES_IN=7d
|
||||
|
||||
# ==================== CORS ====================
|
||||
# CRÍTICO: Configurar orígenes permitidos correctamente
|
||||
#
|
||||
# ============================================================================
|
||||
# ADVERTENCIA: HEADERS CORS DUPLICADOS
|
||||
# ============================================================================
|
||||
# Si usas Nginx como proxy SSL, NO agregar headers CORS en Nginx.
|
||||
# NestJS maneja CORS internamente en main.ts.
|
||||
# Headers duplicados causan error:
|
||||
# "The 'Access-Control-Allow-Origin' header contains multiple values"
|
||||
#
|
||||
# SOLUCION: Solo NestJS maneja CORS, Nginx solo hace proxy sin headers CORS.
|
||||
# Ver: docs/95-guias-desarrollo/GUIA-CORS-PRODUCCION.md
|
||||
# ============================================================================
|
||||
#
|
||||
# Servidor actual: 74.208.126.102
|
||||
# Frontend puerto: 3005
|
||||
#
|
||||
# CONFIGURACION RECOMENDADA CON SSL:
|
||||
# Incluye HTTPS y HTTP para compatibilidad durante transición
|
||||
CORS_ORIGIN=https://74.208.126.102:3005,https://74.208.126.102,http://74.208.126.102:3005,http://74.208.126.102
|
||||
ENABLE_CORS=true
|
||||
ENABLE_SWAGGER=false
|
||||
|
||||
# ==================== LOGGING ====================
|
||||
LOG_LEVEL=warn
|
||||
LOG_TO_FILE=true
|
||||
|
||||
# ==================== RATE LIMITING ====================
|
||||
RATE_LIMIT_TTL=60
|
||||
RATE_LIMIT_MAX=100
|
||||
|
||||
# ==================== SESSION ====================
|
||||
# Generar secret con: openssl rand -base64 32
|
||||
SESSION_SECRET=<GENERAR_SECRET_SEGURO_AQUI>
|
||||
SESSION_MAX_AGE=86400000
|
||||
|
||||
# ==================== EMAIL ====================
|
||||
# Configurar si se usa envío de emails
|
||||
EMAIL_FROM=noreply@gamilit.com
|
||||
EMAIL_REPLY_TO=support@gamilit.com
|
||||
|
||||
# ==================== FRONTEND URL ====================
|
||||
# URL del frontend para redirects, links en emails, etc.
|
||||
# IMPORTANTE: Usar HTTPS si el servidor tiene SSL configurado
|
||||
FRONTEND_URL=https://74.208.126.102:3005
|
||||
|
||||
# ==================== WEB PUSH NOTIFICATIONS (VAPID) ====================
|
||||
# Web Push API nativo - No requiere servicios externos (Firebase, etc.)
|
||||
# IMPORTANTE: Generar claves VAPID con el comando:
|
||||
# npx web-push generate-vapid-keys
|
||||
#
|
||||
# VAPID (Voluntary Application Server Identification):
|
||||
# - Public Key: Se comparte con el frontend para crear subscripciones
|
||||
# - Private Key: Se mantiene secreta en backend para firmar notificaciones
|
||||
# - Subject: Email de contacto o URL del sitio (mailto: o https:)
|
||||
#
|
||||
# Ejemplo de generación:
|
||||
# $ npx web-push generate-vapid-keys
|
||||
# Public Key: BN4GvZtEZiZuqaaObWga7lEP-S1WCv7L1c...
|
||||
# Private Key: aB3cDefGh4IjKlM5nOpQr6StUvWxYz...
|
||||
#
|
||||
# Compatible con: Chrome, Firefox, Edge, Safari 16.4+
|
||||
# NOTA: Si no se configuran, push notifications quedarán deshabilitadas (graceful degradation)
|
||||
#
|
||||
VAPID_PUBLIC_KEY=<GENERAR_CON_WEB_PUSH_GENERATE_VAPID_KEYS>
|
||||
VAPID_PRIVATE_KEY=<GENERAR_CON_WEB_PUSH_GENERATE_VAPID_KEYS>
|
||||
VAPID_SUBJECT=mailto:admin@gamilit.com
|
||||
|
||||
# ==================== OPCIONALES ====================
|
||||
# Descomentar y configurar si se usan
|
||||
|
||||
# Uploads
|
||||
# MAX_FILE_SIZE=5242880
|
||||
# UPLOAD_DESTINATION=./uploads
|
||||
# ALLOWED_MIME_TYPES=image/jpeg,image/png,image/gif,application/pdf
|
||||
|
||||
# Mantenimiento
|
||||
# MAINTENANCE_MODE=false
|
||||
|
||||
# ============================================================================
|
||||
# NOTAS DE SEGURIDAD
|
||||
# ============================================================================
|
||||
#
|
||||
# 1. JWT_SECRET: DEBE ser diferente al de desarrollo, usar 32+ caracteres aleatorios
|
||||
# 2. DB_PASSWORD: NUNCA commitear credenciales reales al repositorio
|
||||
# 3. SESSION_SECRET: Generar valor único y seguro
|
||||
# 4. CORS_ORIGIN: Solo incluir orígenes confiables, NUNCA usar "*"
|
||||
# 5. ENABLE_SWAGGER: DEBE estar en false en producción
|
||||
# 6. Permisos archivo: chmod 600 .env.production (solo owner puede leer)
|
||||
# 7. Logs: Verificar que LOG_TO_FILE no exponga datos sensibles
|
||||
#
|
||||
# ============================================================================
|
||||
# VALIDACIÓN PRE-DEPLOY
|
||||
# ============================================================================
|
||||
#
|
||||
# Antes de deploy, verificar:
|
||||
# [ ] JWT_SECRET cambiado de valor de ejemplo
|
||||
# [ ] DB_PASSWORD configurado correctamente
|
||||
# [ ] CORS_ORIGIN incluye origen del frontend en producción
|
||||
# [ ] ENABLE_SWAGGER=false
|
||||
# [ ] FRONTEND_URL apunta a URL correcta
|
||||
# [ ] Todas las variables <...> reemplazadas
|
||||
# [ ] VAPID_* configuradas si se requieren push notifications (generar con npx web-push generate-vapid-keys)
|
||||
#
|
||||
# ============================================================================
|
||||
6
projects/gamilit/apps/backend/.gitignore
vendored
6
projects/gamilit/apps/backend/.gitignore
vendored
@ -33,13 +33,11 @@ coverage/
|
||||
logs/
|
||||
*.log
|
||||
|
||||
# Uploads (keep directory structure but ignore files)
|
||||
uploads/exercises/*
|
||||
!uploads/exercises/.gitkeep
|
||||
|
||||
.env*
|
||||
.flaskenv*
|
||||
!.env.project
|
||||
!.env.vault
|
||||
!.env.*.example
|
||||
!.env.example
|
||||
# Backups de scripts automáticos
|
||||
**/*.backup-*
|
||||
|
||||
53
projects/gamilit/apps/frontend/.env.production.example
Normal file
53
projects/gamilit/apps/frontend/.env.production.example
Normal file
@ -0,0 +1,53 @@
|
||||
# ============================================================================
|
||||
# GAMILIT Frontend - Production Environment EXAMPLE
|
||||
# ============================================================================
|
||||
# COPIAR A .env.production
|
||||
# Fecha: 2025-12-18
|
||||
# Server: 74.208.126.102
|
||||
# ============================================================================
|
||||
|
||||
# ==================== APPLICATION ====================
|
||||
VITE_APP_NAME=GAMILIT Platform
|
||||
VITE_APP_VERSION=1.0.0
|
||||
VITE_APP_ENV=production
|
||||
VITE_ENV=production
|
||||
|
||||
# ==================== API CONFIGURATION ====================
|
||||
# IMPORTANTE: Usar HTTPS/WSS si el servidor tiene SSL configurado
|
||||
# ============================================================================
|
||||
# SSL CONFIGURADO - Usar HTTPS/WSS
|
||||
# ============================================================================
|
||||
VITE_API_HOST=74.208.126.102:3006
|
||||
VITE_API_PROTOCOL=https
|
||||
VITE_API_VERSION=v1
|
||||
VITE_API_TIMEOUT=30000
|
||||
|
||||
# WebSocket configuration (WSS para conexiones seguras)
|
||||
VITE_WS_HOST=74.208.126.102:3006
|
||||
VITE_WS_PROTOCOL=wss
|
||||
|
||||
# ==================== AUTHENTICATION ====================
|
||||
VITE_JWT_EXPIRATION=7d
|
||||
|
||||
# ==================== FEATURE FLAGS ====================
|
||||
VITE_ENABLE_GAMIFICATION=true
|
||||
VITE_ENABLE_SOCIAL_FEATURES=true
|
||||
VITE_ENABLE_ANALYTICS=true
|
||||
VITE_ENABLE_DEBUG=false
|
||||
VITE_ENABLE_STORYBOOK=false
|
||||
VITE_MOCK_API=false
|
||||
|
||||
# ==================== EXTERNAL SERVICES ====================
|
||||
# Configurar en produccion si se usan
|
||||
VITE_GOOGLE_ANALYTICS_ID=
|
||||
VITE_SENTRY_DSN=
|
||||
VITE_AI_SERVICE_URL=
|
||||
|
||||
# ==================== PRODUCTION ====================
|
||||
VITE_ENABLE_DEBUG=false
|
||||
VITE_LOG_LEVEL=error
|
||||
|
||||
# ==================== TESTING ====================
|
||||
# NO usar credenciales en produccion
|
||||
VITE_TEST_USER_EMAIL=
|
||||
VITE_TEST_USER_PASSWORD=
|
||||
@ -0,0 +1,157 @@
|
||||
# GUIA-CORS-PRODUCCION.md
|
||||
|
||||
## Configuración CORS para Producción - GAMILIT
|
||||
|
||||
**Fecha**: 2025-12-18
|
||||
**Problema resuelto**: Error `Access-Control-Allow-Origin contains multiple values`
|
||||
|
||||
---
|
||||
|
||||
## 1. Descripción del Problema
|
||||
|
||||
Al hacer requests desde el frontend (puerto 3005) al backend (puerto 3006) en producción con HTTPS, se recibe el error:
|
||||
|
||||
```
|
||||
Access to XMLHttpRequest at 'https://74.208.126.102:3006/api/v1/auth/register'
|
||||
from origin 'https://74.208.126.102:3005' has been blocked by CORS policy:
|
||||
The 'Access-Control-Allow-Origin' header contains multiple values
|
||||
'https://74.208.126.102:3005, https://74.208.126.102:3005', but only one is allowed.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Causa Raíz
|
||||
|
||||
El header `Access-Control-Allow-Origin` se está enviando **DOS VECES**:
|
||||
|
||||
1. **Nginx** (proxy SSL) agrega headers CORS
|
||||
2. **NestJS** (backend) también agrega headers CORS via `app.enableCors()`
|
||||
|
||||
Cuando ambos envían el header, el navegador ve valores duplicados y rechaza la respuesta.
|
||||
|
||||
---
|
||||
|
||||
## 3. Solución Definitiva
|
||||
|
||||
### Regla de Oro: **Solo NestJS maneja CORS**
|
||||
|
||||
Nginx debe actuar únicamente como proxy SSL sin agregar headers CORS.
|
||||
|
||||
### 3.1 Configuración Backend (.env.production)
|
||||
|
||||
```bash
|
||||
# CORS - CONFIGURACIÓN CRÍTICA
|
||||
# ============================================================================
|
||||
# ADVERTENCIA: HEADERS CORS DUPLICADOS
|
||||
# ============================================================================
|
||||
# Si usas Nginx como proxy SSL, NO agregar headers CORS en Nginx.
|
||||
# NestJS maneja CORS internamente en main.ts.
|
||||
# Headers duplicados causan error:
|
||||
# "The 'Access-Control-Allow-Origin' header contains multiple values"
|
||||
# ============================================================================
|
||||
|
||||
# Incluye HTTPS y HTTP para compatibilidad durante transición
|
||||
CORS_ORIGIN=https://74.208.126.102:3005,https://74.208.126.102,http://74.208.126.102:3005,http://74.208.126.102
|
||||
ENABLE_CORS=true
|
||||
```
|
||||
|
||||
### 3.2 Configuración Frontend (.env.production)
|
||||
|
||||
```bash
|
||||
# SSL CONFIGURADO - Usar HTTPS/WSS
|
||||
VITE_API_HOST=74.208.126.102:3006
|
||||
VITE_API_PROTOCOL=https
|
||||
VITE_WS_HOST=74.208.126.102:3006
|
||||
VITE_WS_PROTOCOL=wss
|
||||
```
|
||||
|
||||
### 3.3 Configuración Nginx (SIN CORS)
|
||||
|
||||
```nginx
|
||||
# /etc/nginx/sites-available/gamilit-backend
|
||||
server {
|
||||
listen 3006 ssl;
|
||||
server_name 74.208.126.102;
|
||||
|
||||
ssl_certificate /etc/ssl/certs/gamilit.crt;
|
||||
ssl_certificate_key /etc/ssl/private/gamilit.key;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3007; # PM2 cluster interno
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
|
||||
# ⚠️ NO AGREGAR headers CORS aquí
|
||||
# NestJS los maneja internamente
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Verificación
|
||||
|
||||
### 4.1 Verificar headers con curl
|
||||
|
||||
```bash
|
||||
# Verificar que solo hay UN header Access-Control-Allow-Origin
|
||||
curl -I -X OPTIONS \
|
||||
-H "Origin: https://74.208.126.102:3005" \
|
||||
-H "Access-Control-Request-Method: POST" \
|
||||
https://74.208.126.102:3006/api/v1/auth/register
|
||||
|
||||
# Salida esperada (UN solo header):
|
||||
# Access-Control-Allow-Origin: https://74.208.126.102:3005
|
||||
# Access-Control-Allow-Credentials: true
|
||||
# Access-Control-Allow-Methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
|
||||
```
|
||||
|
||||
### 4.2 Verificar configuración Nginx
|
||||
|
||||
```bash
|
||||
# Buscar headers CORS duplicados en configuración
|
||||
grep -r "Access-Control" /etc/nginx/
|
||||
```
|
||||
|
||||
Si encuentra líneas como `add_header Access-Control-Allow-Origin`, elimínelas.
|
||||
|
||||
---
|
||||
|
||||
## 5. Pasos de Deploy
|
||||
|
||||
1. **Backend**: Actualizar `.env.production` con HTTPS origins
|
||||
2. **Frontend**: Actualizar `.env.production` con HTTPS/WSS
|
||||
3. **Nginx**: Remover cualquier header CORS
|
||||
4. **Reiniciar servicios**:
|
||||
```bash
|
||||
sudo systemctl reload nginx
|
||||
pm2 restart gamilit-backend
|
||||
cd /path/to/frontend && npm run build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Troubleshooting
|
||||
|
||||
### Error persiste después de la configuración
|
||||
1. Verificar que Nginx no tenga headers CORS en ningún include
|
||||
2. Revisar si hay otro proxy (CloudFlare, etc.) agregando headers
|
||||
3. Limpiar cache del navegador (F12 > Network > Disable cache)
|
||||
|
||||
### Error "CORS blocked request" en logs
|
||||
- El origen que hace la petición no está en CORS_ORIGIN
|
||||
- Verificar que el protocolo coincide (https vs http)
|
||||
|
||||
---
|
||||
|
||||
## 7. Referencias
|
||||
|
||||
- **Backend CORS**: `apps/backend/src/main.ts` líneas 27-46
|
||||
- **Config CORS**: `apps/backend/src/config/app.config.ts`
|
||||
- **Ejemplo .env**: `apps/backend/.env.production.example`
|
||||
Loading…
Reference in New Issue
Block a user