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
|
# GAMILIT Monorepo - .gitignore
|
||||||
# Generado: 2025-11-01 (RFC-0001)
|
# Generado: 2025-11-01 (RFC-0001)
|
||||||
# Actualizado: 2025-12-05
|
|
||||||
|
|
||||||
# ============================================
|
# === NODE.JS ===
|
||||||
# NODE.JS - DEPENDENCIAS (GLOBAL)
|
node_modules/
|
||||||
# ============================================
|
|
||||||
# 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
|
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
lerna-debug.log*
|
lerna-debug.log*
|
||||||
.pnpm-debug.log*
|
.pnpm-debug.log*
|
||||||
|
|
||||||
# Directorios de dependencias alternativas
|
# Dependency directories
|
||||||
jspm_packages/
|
jspm_packages/
|
||||||
bower_components/
|
|
||||||
|
|
||||||
# Cache de npm
|
# Optional npm cache directory
|
||||||
.npm/
|
.npm
|
||||||
.npmrc.local
|
|
||||||
|
|
||||||
# Cache de eslint/stylelint
|
# Optional eslint cache
|
||||||
.eslintcache
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
.stylelintcache
|
.stylelintcache
|
||||||
|
|
||||||
# ============================================
|
# === TYPESCRIPT ===
|
||||||
# TYPESCRIPT / BUILD ARTIFACTS (GLOBAL)
|
|
||||||
# ============================================
|
|
||||||
# Ignorar dist/build en CUALQUIER nivel
|
|
||||||
**/dist/
|
|
||||||
**/build/
|
|
||||||
**/out/
|
|
||||||
**/.next/
|
|
||||||
**/.nuxt/
|
|
||||||
**/.turbo/
|
|
||||||
|
|
||||||
# TypeScript
|
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
**/*.tsbuildinfo
|
dist/
|
||||||
|
build/
|
||||||
*.js.map
|
*.js.map
|
||||||
|
|
||||||
# ============================================
|
# === ANGULAR / NX ===
|
||||||
# FRAMEWORKS ESPECÍFICOS
|
|
||||||
# ============================================
|
|
||||||
# Angular / NX
|
|
||||||
.angular/
|
.angular/
|
||||||
.nx/
|
.nx/cache/
|
||||||
**/.nx/
|
.nx/workspace-data/
|
||||||
|
|
||||||
# Vite
|
# === NESTJS ===
|
||||||
**/.vite/
|
/apps/backend/dist/
|
||||||
|
/apps/backend/build/
|
||||||
|
|
||||||
# Webpack
|
# === ENVIRONMENT FILES ===
|
||||||
.webpack/
|
# IMPORTANTE: Nunca commitear secrets reales
|
||||||
**/.webpack/
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# ENVIRONMENT FILES - SECRETS
|
|
||||||
# ============================================
|
|
||||||
# CRÍTICO: Nunca commitear secrets
|
|
||||||
.env
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
.env.*.local
|
.env.*.local
|
||||||
.env.production
|
.env.production
|
||||||
.env.development
|
.env.development
|
||||||
.env.test
|
.env.test
|
||||||
.env.staging
|
# Permitir archivos de ejemplo (sin secrets)
|
||||||
|
!.env.*.example
|
||||||
|
!.env.example
|
||||||
|
|
||||||
# Archivos con secrets
|
# Archivos de configuración con secrets
|
||||||
**/secrets.json
|
config/secrets.json
|
||||||
**/credentials.json
|
config/credentials.json
|
||||||
**/*secrets*.json
|
**/*secrets*.json
|
||||||
**/*credentials*.json
|
**/*credentials*.json
|
||||||
**/*.secret
|
|
||||||
**/*.secrets
|
|
||||||
|
|
||||||
# Configuración de base de datos con credenciales
|
# === DATABASES ===
|
||||||
**/database.config.ts
|
# PostgreSQL
|
||||||
!**/database.config.example.ts
|
|
||||||
**/ormconfig.json
|
|
||||||
!**/ormconfig.example.json
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# DATABASES
|
|
||||||
# ============================================
|
|
||||||
# Backups y dumps
|
|
||||||
*.sql.backup
|
*.sql.backup
|
||||||
*.dump
|
*.dump
|
||||||
*.pgdata
|
*.pgdata
|
||||||
*.sql.gz
|
|
||||||
|
|
||||||
# Bases de datos locales
|
# Local database files
|
||||||
*.sqlite
|
*.sqlite
|
||||||
*.sqlite3
|
*.sqlite3
|
||||||
*.db
|
*.db
|
||||||
*.db-journal
|
|
||||||
|
|
||||||
# Redis
|
# Database connection strings (excepción: ejemplos con .example)
|
||||||
dump.rdb
|
database.config.ts
|
||||||
|
!database.config.example.ts
|
||||||
|
|
||||||
# ============================================
|
# === LOGS ===
|
||||||
# LOGS (GLOBAL)
|
logs/
|
||||||
# ============================================
|
*.log
|
||||||
**/logs/
|
npm-debug.log*
|
||||||
**/*.log
|
yarn-debug.log*
|
||||||
**/pm2-logs/
|
yarn-error.log*
|
||||||
**/*.pm2.log
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
# ============================================
|
# PM2 logs
|
||||||
# TESTING (GLOBAL)
|
pm2-logs/
|
||||||
# ============================================
|
*.pm2.log
|
||||||
**/coverage/
|
|
||||||
**/.nyc_output/
|
# === TESTING ===
|
||||||
|
coverage/
|
||||||
|
.nyc_output/
|
||||||
*.lcov
|
*.lcov
|
||||||
|
|
||||||
# Jest
|
# Jest
|
||||||
**/jest-cache/
|
jest-cache/
|
||||||
.jest/
|
|
||||||
|
|
||||||
# Cypress
|
# Cypress
|
||||||
**/cypress/screenshots/
|
cypress/screenshots/
|
||||||
**/cypress/videos/
|
cypress/videos/
|
||||||
**/cypress/downloads/
|
cypress/downloads/
|
||||||
|
|
||||||
# Playwright
|
|
||||||
**/playwright-report/
|
|
||||||
**/playwright/.cache/
|
|
||||||
**/test-results/
|
|
||||||
|
|
||||||
# E2E reports
|
# E2E reports
|
||||||
**/e2e-reports/
|
e2e-reports/
|
||||||
|
test-results/
|
||||||
|
|
||||||
# ============================================
|
# === IDEs and EDITORS ===
|
||||||
# IDEs and EDITORS
|
# VSCode
|
||||||
# ============================================
|
|
||||||
# VSCode - mantener configuración compartida
|
|
||||||
.vscode/*
|
.vscode/*
|
||||||
!.vscode/settings.json
|
!.vscode/settings.json
|
||||||
!.vscode/tasks.json
|
!.vscode/tasks.json
|
||||||
@ -161,117 +119,92 @@ dump.rdb
|
|||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
*.sublime-project
|
*.sublime-project
|
||||||
|
|
||||||
# Vim/Neovim
|
# Vim
|
||||||
*.swp
|
*.swp
|
||||||
*.swo
|
*.swo
|
||||||
*~
|
*~
|
||||||
.netrwhist
|
|
||||||
Session.vim
|
|
||||||
|
|
||||||
# Emacs
|
# Emacs
|
||||||
|
*~
|
||||||
\#*\#
|
\#*\#
|
||||||
.\#*
|
.\#*
|
||||||
*.elc
|
|
||||||
auto-save-list/
|
|
||||||
|
|
||||||
# ============================================
|
# === OS FILES ===
|
||||||
# OS FILES
|
|
||||||
# ============================================
|
|
||||||
# macOS
|
# macOS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.AppleDouble
|
.AppleDouble
|
||||||
.LSOverride
|
.LSOverride
|
||||||
._*
|
._*
|
||||||
.Spotlight-V100
|
|
||||||
.Trashes
|
|
||||||
|
|
||||||
# Linux
|
# Linux
|
||||||
*~
|
*~
|
||||||
.directory
|
.directory
|
||||||
.Trash-*
|
|
||||||
|
|
||||||
# Windows
|
# Windows
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
Thumbs.db:encryptable
|
|
||||||
ehthumbs.db
|
ehthumbs.db
|
||||||
ehthumbs_vista.db
|
|
||||||
Desktop.ini
|
Desktop.ini
|
||||||
$RECYCLE.BIN/
|
$RECYCLE.BIN/
|
||||||
*.lnk
|
|
||||||
|
|
||||||
# ============================================
|
# === DOCKER ===
|
||||||
# DOCKER
|
# No ignorar Dockerfiles, solo archivos temporales
|
||||||
# ============================================
|
|
||||||
docker-compose.override.yml
|
docker-compose.override.yml
|
||||||
docker-compose.local.yml
|
|
||||||
.dockerignore.local
|
.dockerignore.local
|
||||||
**/*.dockerignore.local
|
|
||||||
|
|
||||||
# ============================================
|
# === BUILD ARTIFACTS ===
|
||||||
# DEPLOYMENT & SECURITY
|
/apps/*/dist/
|
||||||
# ============================================
|
/apps/*/build/
|
||||||
|
/libs/*/dist/
|
||||||
|
|
||||||
|
# Webpack
|
||||||
|
.webpack/
|
||||||
|
|
||||||
|
# === DEPLOYMENT ===
|
||||||
# PM2
|
# PM2
|
||||||
ecosystem.config.js.local
|
ecosystem.config.js.local
|
||||||
pm2.config.js.local
|
pm2.config.js.local
|
||||||
|
|
||||||
# Claves y certificados
|
# Deploy keys (excepción: .example files)
|
||||||
*.pem
|
*.pem
|
||||||
*.key
|
*.key
|
||||||
*.p12
|
|
||||||
*.pfx
|
|
||||||
!*.example.pem
|
!*.example.pem
|
||||||
!*.example.key
|
!*.example.key
|
||||||
|
|
||||||
# SSL certificates
|
# SSL certificates (excepción: self-signed para dev)
|
||||||
*.crt
|
*.crt
|
||||||
*.cer
|
*.cer
|
||||||
!dev-cert.crt
|
!dev-cert.crt
|
||||||
!*.example.crt
|
|
||||||
|
|
||||||
# SSH keys
|
# === TEMP FILES ===
|
||||||
id_rsa*
|
tmp/
|
||||||
id_ed25519*
|
temp/
|
||||||
*.pub
|
*.tmp
|
||||||
!*.example.pub
|
*.temp
|
||||||
|
*.cache
|
||||||
|
|
||||||
# ============================================
|
# === ARTIFACTS (parcial) ===
|
||||||
# TEMP FILES (GLOBAL)
|
# Mantener reportes importantes, ignorar temporales
|
||||||
# ============================================
|
|
||||||
**/tmp/
|
|
||||||
**/temp/
|
|
||||||
**/.tmp/
|
|
||||||
**/.temp/
|
|
||||||
**/*.tmp
|
|
||||||
**/*.temp
|
|
||||||
**/*.cache
|
|
||||||
**/.cache/
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# ARTIFACTS
|
|
||||||
# ============================================
|
|
||||||
/artifacts/temp/
|
/artifacts/temp/
|
||||||
/artifacts/cache/
|
/artifacts/cache/
|
||||||
/artifacts/**/*.tmp
|
/artifacts/**/*.tmp
|
||||||
/artifacts/**/*.cache
|
|
||||||
|
|
||||||
# ============================================
|
# === CLAUDE CODE ===
|
||||||
# CLAUDE CODE / AI
|
# Excluir toda la carpeta .claude (configuración local de IA)
|
||||||
# ============================================
|
|
||||||
# Configuración local de Claude Code
|
|
||||||
.claude/
|
.claude/
|
||||||
|
|
||||||
# Pero NO ignorar orchestration (necesario para Claude Code cloud)
|
# === ORCHESTRATION ===
|
||||||
# Solo ignorar temporales dentro de 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/.archive/
|
||||||
orchestration/.tmp/
|
orchestration/.tmp/
|
||||||
orchestration/**/*.tmp
|
orchestration/**/*.tmp
|
||||||
orchestration/**/*.cache
|
orchestration/**/*.cache
|
||||||
|
|
||||||
# ============================================
|
# === REFERENCE (Código de Referencia) ===
|
||||||
# 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
|
||||||
# reference/ DEBE estar en el repo
|
# Ignorar solo carpetas de build/dependencias dentro de reference/
|
||||||
# Solo ignorar build/dependencias dentro
|
|
||||||
reference/**/node_modules/
|
reference/**/node_modules/
|
||||||
reference/**/dist/
|
reference/**/dist/
|
||||||
reference/**/build/
|
reference/**/build/
|
||||||
@ -286,9 +219,16 @@ reference/**/*.tmp
|
|||||||
reference/**/*.cache
|
reference/**/*.cache
|
||||||
reference/**/.DS_Store
|
reference/**/.DS_Store
|
||||||
|
|
||||||
# ============================================
|
# === MIGRATION (temporal) ===
|
||||||
# PACKAGE MANAGERS
|
# 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/*
|
.yarn/*
|
||||||
!.yarn/patches
|
!.yarn/patches
|
||||||
@ -301,102 +241,33 @@ reference/**/.DS_Store
|
|||||||
# PNPM
|
# PNPM
|
||||||
.pnpm-store/
|
.pnpm-store/
|
||||||
|
|
||||||
# ============================================
|
# === MONITORING ===
|
||||||
# MONITORING & OBSERVABILITY
|
# Application monitoring
|
||||||
# ============================================
|
|
||||||
newrelic_agent.log
|
newrelic_agent.log
|
||||||
.monitors/
|
.monitors/
|
||||||
**/.sentry/
|
|
||||||
**/sentry-debug.log
|
|
||||||
|
|
||||||
# ============================================
|
# === MISC ===
|
||||||
# BACKUPS (GLOBAL)
|
# Backups - Archivos
|
||||||
# ============================================
|
|
||||||
# Archivos
|
|
||||||
*.backup
|
*.backup
|
||||||
*.bak
|
*.bak
|
||||||
*.old
|
*.old
|
||||||
*.orig
|
|
||||||
|
|
||||||
# Carpetas
|
# Backups - Carpetas
|
||||||
**/*_old/
|
*_old/
|
||||||
**/*_bckp/
|
*_bckp/
|
||||||
**/*_bkp/
|
*_bkp/
|
||||||
**/*_backup/
|
*_backup/
|
||||||
**/*.old/
|
*.old/
|
||||||
**/*.bak/
|
*.bak/
|
||||||
**/*.backup/
|
*.backup/
|
||||||
|
|
||||||
# Específicos del proyecto
|
# Backups específicos (carpetas identificadas en workspace)
|
||||||
orchestration_old/
|
orchestration_old/
|
||||||
orchestration_bckp/
|
orchestration_bckp/
|
||||||
docs_bkp/
|
docs_bkp/
|
||||||
|
|
||||||
# ============================================
|
# Compressed files (si no son assets del proyecto)
|
||||||
# COMPRESSED FILES
|
|
||||||
# ============================================
|
|
||||||
*.zip
|
*.zip
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
*.tar
|
|
||||||
*.rar
|
*.rar
|
||||||
*.7z
|
|
||||||
# Excepto assets
|
|
||||||
!assets/**/*.zip
|
!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/
|
logs/
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
# Uploads (keep directory structure but ignore files)
|
|
||||||
uploads/exercises/*
|
|
||||||
!uploads/exercises/.gitkeep
|
|
||||||
|
|
||||||
.env*
|
.env*
|
||||||
.flaskenv*
|
.flaskenv*
|
||||||
!.env.project
|
!.env.project
|
||||||
!.env.vault
|
!.env.vault
|
||||||
|
!.env.*.example
|
||||||
|
!.env.example
|
||||||
# Backups de scripts automáticos
|
# Backups de scripts automáticos
|
||||||
**/*.backup-*
|
**/*.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