workspace/projects/gamilit/apps/database/scripts/manage-secrets.sh
rckrdmrd ea1879f4ad feat: Initial workspace structure with multi-level Git configuration
- Configure workspace Git repository with comprehensive .gitignore
- Add Odoo as submodule for ERP reference code
- Include documentation: SETUP.md, GIT-STRUCTURE.md
- Add gitignore templates for projects (backend, frontend, database)
- Structure supports independent repos per project/subproject level

Workspace includes:
- core/ - Reusable patterns, modules, orchestration system
- projects/ - Active projects (erp-suite, gamilit, trading-platform, etc.)
- knowledge-base/ - Reference code and patterns (includes Odoo submodule)
- devtools/ - Development tools and templates
- customers/ - Client implementations template

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 10:44:23 -06:00

615 lines
18 KiB
Bash
Executable File

#!/bin/bash
##############################################################################
# GAMILIT Platform - Secrets Management with dotenv-vault
#
# Propósito: Gestión centralizada de secrets con dotenv-vault
#
# Funcionalidades:
# 1. Genera passwords seguros para BD
# 2. Guarda en dotenv-vault (dev y prod)
# 3. Sincroniza automáticamente con backend
# 4. Exporta para uso en scripts de BD
# 5. Rotación de passwords
#
# Uso:
# ./manage-secrets.sh generate --env dev|prod # Generar nuevos secrets
# ./manage-secrets.sh sync --env dev|prod # Sincronizar a backend
# ./manage-secrets.sh rotate --env dev|prod # Rotar passwords
# ./manage-secrets.sh export --env dev|prod # Exportar para uso
# ./manage-secrets.sh status --env dev|prod # Ver estado
#
# Requisitos:
# - dotenv-vault CLI instalado (npm install -g dotenv-vault)
# - Acceso a dotenv-vault keys
#
##############################################################################
set -e
# Colores
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
# Configuración de rutas
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DATABASE_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
APPS_ROOT="$(cd "$DATABASE_ROOT/.." && pwd)"
BACKEND_DIR="$APPS_ROOT/backend"
GAMILIT_ROOT="$(cd "$APPS_ROOT/.." && pwd)"
# Variables
ENVIRONMENT=""
COMMAND=""
FORCE_MODE=false
# ============================================================================
# FUNCIONES AUXILIARES
# ============================================================================
print_header() {
echo ""
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
}
print_step() {
echo -e "${CYAN}$1${NC}"
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}$1${NC}"
}
print_info() {
echo " $1"
}
show_help() {
cat << EOF
GAMILIT Platform - Secrets Management con dotenv-vault
Uso: $0 COMMAND --env ENVIRONMENT [OPTIONS]
Comandos:
generate Generar nuevos secrets (DB password, JWT secrets)
sync Sincronizar secrets a dotenv-vault
rotate Rotar passwords existentes
export Exportar secrets para uso en scripts
status Mostrar estado de secrets
init Inicializar dotenv-vault (primera vez)
Opciones:
--env dev|prod Ambiente
--force No pedir confirmación
--help Mostrar ayuda
Ejemplos:
# Generar secrets para desarrollo
$0 generate --env dev
# Sincronizar a dotenv-vault y backend
$0 sync --env dev
# Rotar password de producción
$0 rotate --env prod
# Exportar para init-database.sh
$0 export --env prod
# Ver estado de secrets
$0 status --env dev
Flujo Recomendado:
1. Primera vez: ./manage-secrets.sh init --env dev
2. Generar: ./manage-secrets.sh generate --env dev
3. Sincronizar: ./manage-secrets.sh sync --env dev
4. Usar en BD: ./init-database-v2.sh --env dev --use-vault
EOF
}
generate_password() {
local length=${1:-32}
openssl rand -base64 48 | tr -d "=+/" | cut -c1-$length
}
generate_jwt_secret() {
openssl rand -base64 64 | tr -d "=+/"
}
# ============================================================================
# VERIFICAR PREREQUISITOS
# ============================================================================
check_prerequisites() {
print_step "Verificando prerequisitos..."
# Verificar openssl
if ! command -v openssl &> /dev/null; then
print_error "openssl no encontrado"
exit 1
fi
# Verificar dotenv-vault CLI
if ! command -v npx &> /dev/null; then
print_error "npx no encontrado. Instala Node.js primero."
exit 1
fi
# Verificar que existe directorio backend
if [ ! -d "$BACKEND_DIR" ]; then
print_error "Directorio backend no encontrado: $BACKEND_DIR"
exit 1
fi
print_success "Prerequisites OK"
}
# ============================================================================
# COMANDO: INIT - Inicializar dotenv-vault
# ============================================================================
cmd_init() {
print_header "Inicializando dotenv-vault para $ENVIRONMENT"
cd "$BACKEND_DIR"
# Verificar si ya existe .env.vault
if [ -f ".env.vault" ]; then
print_warning ".env.vault ya existe"
if [ "$FORCE_MODE" = false ]; then
read -p "¿Reinicializar? (yes/no): " reinit
if [ "$reinit" != "yes" ]; then
print_info "Operación cancelada"
exit 0
fi
fi
fi
print_step "Inicializando dotenv-vault..."
# Inicializar dotenv-vault
if npx dotenv-vault new; then
print_success "dotenv-vault inicializado"
else
print_error "Error al inicializar dotenv-vault"
exit 1
fi
print_info "Próximos pasos:"
print_info "1. Ejecuta: ./manage-secrets.sh generate --env $ENVIRONMENT"
print_info "2. Ejecuta: ./manage-secrets.sh sync --env $ENVIRONMENT"
}
# ============================================================================
# COMANDO: GENERATE - Generar nuevos secrets
# ============================================================================
cmd_generate() {
print_header "Generando secrets para $ENVIRONMENT"
# Generar secrets
print_step "Generando passwords y secrets..."
local DB_PASSWORD=$(generate_password 32)
local JWT_SECRET=$(generate_jwt_secret)
local JWT_REFRESH_SECRET=$(generate_jwt_secret)
local ENCRYPTION_KEY=$(generate_jwt_secret)
print_success "Secrets generados"
# Determinar host según ambiente
local DB_HOST="localhost"
local DB_SSL="false"
if [ "$ENVIRONMENT" = "prod" ]; then
DB_HOST="74.208.126.102"
DB_SSL="true"
fi
# Crear archivo temporal con secrets
local TEMP_ENV_FILE="/tmp/gamilit-secrets-${ENVIRONMENT}-$(date +%s).env"
cat > "$TEMP_ENV_FILE" << EOF
# ============================================================================
# GAMILIT Platform - Secrets for $ENVIRONMENT
# Generated: $(date)
# ⚠️ Este archivo contiene información sensible - NO COMMITEAR
# ============================================================================
# Database Configuration
DB_HOST=$DB_HOST
DB_PORT=5432
DB_NAME=gamilit_platform
DB_USER=gamilit_user
DB_PASSWORD=$DB_PASSWORD
DATABASE_URL=postgresql://gamilit_user:$DB_PASSWORD@$DB_HOST:5432/gamilit_platform
# Database Pool
DB_POOL_MIN=2
DB_POOL_MAX=10
DB_SSL=$DB_SSL
# JWT Authentication
JWT_SECRET=$JWT_SECRET
JWT_REFRESH_SECRET=$JWT_REFRESH_SECRET
JWT_EXPIRES_IN=7d
JWT_REFRESH_EXPIRES_IN=30d
# Encryption
ENCRYPTION_KEY=$ENCRYPTION_KEY
# Environment
NODE_ENV=$ENVIRONMENT
APP_ENV=$ENVIRONMENT
# API Keys (CONFIGURAR MANUALMENTE)
OPENAI_API_KEY=PENDIENTE_CONFIGURACION
SENDGRID_API_KEY=PENDIENTE_CONFIGURACION
# OAuth (CONFIGURAR MANUALMENTE)
GOOGLE_CLIENT_ID=PENDIENTE_CONFIGURACION
GOOGLE_CLIENT_SECRET=PENDIENTE_CONFIGURACION
# Frontend (Vite)
VITE_JWT_SECRET=$JWT_SECRET
VITE_API_URL=http://localhost:3006
EOF
chmod 600 "$TEMP_ENV_FILE"
print_success "Archivo de secrets creado"
print_info "Ubicación: $TEMP_ENV_FILE"
echo ""
# Mostrar secrets generados (parcialmente ocultos)
echo -e "${CYAN}Secrets Generados:${NC}"
echo -e " DB_PASSWORD: ${GREEN}${DB_PASSWORD:0:8}...${DB_PASSWORD: -4}${NC}"
echo -e " JWT_SECRET: ${GREEN}${JWT_SECRET:0:12}...${JWT_SECRET: -4}${NC}"
echo -e " JWT_REFRESH_SECRET: ${GREEN}${JWT_REFRESH_SECRET:0:12}...${JWT_REFRESH_SECRET: -4}${NC}"
echo ""
# Guardar referencia al archivo temporal
echo "$TEMP_ENV_FILE" > "/tmp/gamilit-secrets-${ENVIRONMENT}-latest.txt"
print_info "Próximo paso: ./manage-secrets.sh sync --env $ENVIRONMENT"
}
# ============================================================================
# COMANDO: SYNC - Sincronizar a dotenv-vault
# ============================================================================
cmd_sync() {
print_header "Sincronizando secrets a dotenv-vault para $ENVIRONMENT"
# Leer archivo temporal más reciente
local LATEST_FILE_REF="/tmp/gamilit-secrets-${ENVIRONMENT}-latest.txt"
if [ ! -f "$LATEST_FILE_REF" ]; then
print_error "No se encontró archivo de secrets generado"
print_info "Primero ejecuta: ./manage-secrets.sh generate --env $ENVIRONMENT"
exit 1
fi
local TEMP_ENV_FILE=$(cat "$LATEST_FILE_REF")
if [ ! -f "$TEMP_ENV_FILE" ]; then
print_error "Archivo de secrets no encontrado: $TEMP_ENV_FILE"
exit 1
fi
print_step "Sincronizando secrets..."
cd "$BACKEND_DIR"
# Backup del .env actual si existe
if [ -f ".env.$ENVIRONMENT" ]; then
cp ".env.$ENVIRONMENT" ".env.$ENVIRONMENT.backup.$(date +%Y%m%d_%H%M%S)"
print_info "Backup creado: .env.$ENVIRONMENT.backup"
fi
# Copiar secrets al archivo .env del ambiente
cp "$TEMP_ENV_FILE" ".env.$ENVIRONMENT"
print_success "Secrets copiados a .env.$ENVIRONMENT"
# Sincronizar con dotenv-vault
print_step "Subiendo secrets a dotenv-vault..."
if npx dotenv-vault push "$ENVIRONMENT"; then
print_success "Secrets sincronizados a dotenv-vault"
else
print_error "Error al sincronizar con dotenv-vault"
exit 1
fi
# Limpiar archivo temporal
rm -f "$TEMP_ENV_FILE"
rm -f "$LATEST_FILE_REF"
print_success "Sincronización completada"
# Actualizar también frontend si existe
if [ -d "$APPS_ROOT/frontend" ]; then
print_step "Actualizando frontend..."
local FRONTEND_ENV="$APPS_ROOT/frontend/.env.$ENVIRONMENT"
cat > "$FRONTEND_ENV" << EOF
# ============================================================================
# GAMILIT Platform Frontend - $ENVIRONMENT
# Generated: $(date)
# ============================================================================
VITE_API_URL=http://localhost:3006
VITE_JWT_SECRET=$(grep "^JWT_SECRET=" "$BACKEND_DIR/.env.$ENVIRONMENT" | cut -d= -f2)
VITE_ENVIRONMENT=$ENVIRONMENT
EOF
chmod 600 "$FRONTEND_ENV"
print_success "Frontend .env.$ENVIRONMENT actualizado"
fi
echo ""
print_info "Secrets disponibles en:"
print_info " • dotenv-vault (encriptado)"
print_info "$BACKEND_DIR/.env.$ENVIRONMENT"
print_info "$APPS_ROOT/frontend/.env.$ENVIRONMENT"
}
# ============================================================================
# COMANDO: EXPORT - Exportar secrets para scripts
# ============================================================================
cmd_export() {
print_header "Exportando secrets para scripts de BD ($ENVIRONMENT)"
cd "$BACKEND_DIR"
# Verificar que existe .env para el ambiente
if [ ! -f ".env.$ENVIRONMENT" ]; then
print_error "No se encontró .env.$ENVIRONMENT"
print_info "Primero ejecuta: ./manage-secrets.sh generate --env $ENVIRONMENT"
print_info "Luego: ./manage-secrets.sh sync --env $ENVIRONMENT"
exit 1
fi
# Exportar secrets desde dotenv-vault
print_step "Obteniendo secrets desde dotenv-vault..."
# Extraer DB_PASSWORD del .env
local DB_PASSWORD=$(grep "^DB_PASSWORD=" ".env.$ENVIRONMENT" | cut -d= -f2)
if [ -z "$DB_PASSWORD" ]; then
print_error "DB_PASSWORD no encontrado en .env.$ENVIRONMENT"
exit 1
fi
# Crear script temporal para exportar
local EXPORT_FILE="/tmp/gamilit-db-secrets-${ENVIRONMENT}.sh"
cat > "$EXPORT_FILE" << EOF
#!/bin/bash
# ============================================================================
# GAMILIT Platform - Database Secrets Export
# Environment: $ENVIRONMENT
# Generated: $(date)
# ⚠️ Este archivo se auto-destruye después de 1 hora
# ============================================================================
export GAMILIT_ENV="$ENVIRONMENT"
export GAMILIT_DB_PASSWORD="$DB_PASSWORD"
echo "✓ Secrets exportados para $ENVIRONMENT"
echo " DB_PASSWORD: ${DB_PASSWORD:0:8}...${DB_PASSWORD: -4}"
echo ""
echo "Uso: source $EXPORT_FILE"
echo "Luego: ./init-database-v2.sh --env $ENVIRONMENT --use-exported-password"
EOF
chmod 600 "$EXPORT_FILE"
print_success "Secrets exportados"
echo ""
echo -e "${CYAN}Para usar en init-database.sh:${NC}"
echo -e " ${GREEN}source $EXPORT_FILE${NC}"
echo -e " ${GREEN}./init-database-v2.sh --env $ENVIRONMENT --use-exported-password${NC}"
echo ""
# Programar auto-destrucción en 1 hora
(sleep 3600 && rm -f "$EXPORT_FILE" 2>/dev/null) &
print_warning "Este archivo se auto-destruirá en 1 hora"
}
# ============================================================================
# COMANDO: STATUS - Ver estado de secrets
# ============================================================================
cmd_status() {
print_header "Estado de secrets para $ENVIRONMENT"
cd "$BACKEND_DIR"
# Verificar archivo .env
print_step "Verificando archivos de configuración..."
if [ -f ".env.$ENVIRONMENT" ]; then
print_success ".env.$ENVIRONMENT existe"
# Verificar campos críticos
local has_db_password=$(grep -c "^DB_PASSWORD=" ".env.$ENVIRONMENT" || echo "0")
local has_jwt=$(grep -c "^JWT_SECRET=" ".env.$ENVIRONMENT" || echo "0")
if [ "$has_db_password" -gt 0 ]; then
print_success " DB_PASSWORD configurado"
else
print_error " DB_PASSWORD NO configurado"
fi
if [ "$has_jwt" -gt 0 ]; then
print_success " JWT_SECRET configurado"
else
print_error " JWT_SECRET NO configurado"
fi
else
print_error ".env.$ENVIRONMENT NO existe"
fi
# Verificar dotenv-vault
print_step "Verificando dotenv-vault..."
if [ -f ".env.vault" ]; then
print_success ".env.vault existe"
else
print_warning ".env.vault NO existe (ejecuta: ./manage-secrets.sh init)"
fi
# Verificar credentials file de BD
local CREDS_FILE="$DATABASE_ROOT/database-credentials-${ENVIRONMENT}.txt"
print_step "Verificando credentials de BD..."
if [ -f "$CREDS_FILE" ]; then
print_success "database-credentials-${ENVIRONMENT}.txt existe"
local saved_password=$(grep "^Password:" "$CREDS_FILE" | awk '{print $2}')
local env_password=$(grep "^DB_PASSWORD=" "$BACKEND_DIR/.env.$ENVIRONMENT" | cut -d= -f2)
if [ "$saved_password" = "$env_password" ]; then
print_success " Passwords sincronizados"
else
print_warning " Passwords NO sincronizados"
fi
else
print_info "database-credentials-${ENVIRONMENT}.txt NO existe (se creará al init DB)"
fi
echo ""
print_info "Estado general: $([ -f ".env.$ENVIRONMENT" ] && [ -f ".env.vault" ] && echo '✅ LISTO' || echo '⚠️ REQUIERE CONFIGURACIÓN')"
}
# ============================================================================
# COMANDO: ROTATE - Rotar passwords
# ============================================================================
cmd_rotate() {
print_header "Rotando passwords para $ENVIRONMENT"
print_warning "⚠️ ADVERTENCIA: Esto generará nuevos passwords"
print_info "Esto requerirá:"
print_info " 1. Reiniciar la base de datos con nuevo password"
print_info " 2. Reiniciar el backend"
print_info " 3. Actualizar todos los servicios"
echo ""
if [ "$FORCE_MODE" = false ]; then
read -p "¿Continuar con rotación? (yes/no): " confirm
if [ "$confirm" != "yes" ]; then
print_info "Operación cancelada"
exit 0
fi
fi
# Ejecutar generate y sync
cmd_generate
echo ""
cmd_sync
print_success "Rotación completada"
echo ""
print_warning "IMPORTANTE: Reinicia los siguientes servicios:"
print_info " 1. Base de datos: ./init-database-v2.sh --env $ENVIRONMENT --use-vault"
print_info " 2. Backend: npm run dev (recargará nuevos secrets)"
}
# ============================================================================
# MAIN
# ============================================================================
main() {
# Parsear argumentos
while [[ $# -gt 0 ]]; do
case $1 in
generate|sync|rotate|export|status|init)
COMMAND="$1"
shift
;;
--env)
ENVIRONMENT="$2"
shift 2
;;
--force)
FORCE_MODE=true
shift
;;
--help)
show_help
exit 0
;;
*)
print_error "Opción desconocida: $1"
show_help
exit 1
;;
esac
done
# Validar argumentos
if [ -z "$COMMAND" ]; then
print_error "Debe especificar un comando"
show_help
exit 1
fi
if [ -z "$ENVIRONMENT" ] && [ "$COMMAND" != "help" ]; then
print_error "Debe especificar --env dev|prod"
show_help
exit 1
fi
if [ "$ENVIRONMENT" != "dev" ] && [ "$ENVIRONMENT" != "prod" ] && [ -n "$ENVIRONMENT" ]; then
print_error "Ambiente debe ser 'dev' o 'prod'"
exit 1
fi
# Verificar prerequisitos
check_prerequisites
# Ejecutar comando
case $COMMAND in
init)
cmd_init
;;
generate)
cmd_generate
;;
sync)
cmd_sync
;;
export)
cmd_export
;;
status)
cmd_status
;;
rotate)
cmd_rotate
;;
*)
print_error "Comando desconocido: $COMMAND"
show_help
exit 1
;;
esac
}
main "$@"