Files
openccb/install.sh
T
Nurfog e4866c6dee feat: SAM integration, deployment scripts, and audio response enhancements
- Add SAM (Sistema de Administración Académica) integration with sync endpoints
- Add deployment automation (deploy.sh, remote-setup.sh, setup-nginx-ssl.sh)
- Add nginx proxy configuration for SSL with Let's Encrypt
- Add audio response support for student lessons (migrations, handlers)
- Add audio evaluations admin page
- Update CORS to support wildcard subdomains for norteamericano.cl
- Add comprehensive deployment documentation (DESPLIEGUE.md, ManualDeConfiguracion.md)
- Update docker-compose.yml with nginx-proxy and acme-companion services
- Remove outdated documentation files

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-03-27 09:20:23 -03:00

453 lines
16 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# OpenCCB Unified Installation Script
# This script automates the setup of OpenCCB:
# 1. Prerequisite checks (Rust, Node.js, Docker, sqlx-cli)
# 2. Hardware detection (NVIDIA GPU vs CPU)
# 3. Environment configuration (.env) - Dev/Prod support
# 4. Database creation and migrations (CMS, LMS, AI Bridge)
# 5. System initialization (Admin account and Organization)
# 6. Optional: Production deployment with SSH sync
# Version: 3.0 - Dev/Prod + Deployment Support
set -e
# ============================================================================
# CONFIGURACIÓN DE PRODUCCIÓN
# ============================================================================
PEM_PATH="ubuntu.pem"
REMOTE_USER="ubuntu"
REMOTE_HOST="ec2-18-224-137-67.us-east-2.compute.amazonaws.com"
REMOTE_PATH="/var/www/openccb"
# ============================================================================
# CONFIGURACIÓN SAM (Sistema de Administración Académica)
# ============================================================================
# URL de conexión a la base de datos SAM externa
# Formato: postgresql://usuario:contraseña@host:puerto/sige_sam_v3
SAM_DATABASE_URL=""
# ============================================================================
echo "===================================================="
echo " 🚀 Bienvenido al Instalador de OpenCCB v3.0"
echo " (Con Búsqueda Semántica PGVector + Deploy)"
echo "===================================================="
echo ""
# Parse arguments
FAST_MODE="false"
DEPLOY_MODE="false"
for arg in "$@"; do
if [ "$arg" == "--fast" ]; then
FAST_MODE="true"
elif [ "$arg" == "--deploy" ]; then
DEPLOY_MODE="true"
fi
done
# 1. Detección y Clonación
if [ -f "Cargo.toml" ] && [ -d "services" ] && [ -d "web" ]; then
echo "✅ Proyecto detectado en el directorio actual."
PROJECT_ROOT=$(pwd)
else
# Simplificación: assume we are in the project root if the script is running
# but let's keep a basic check
if [ -d "openccb" ]; then
cd openccb
PROJECT_ROOT=$(pwd)
else
echo "⚠️ Por favor, ejecuta este script desde la raíz del repositorio de OpenCCB."
exit 1
fi
fi
if [ "$FAST_MODE" == "false" ]; then
# 2. Prerequisite Installation
install_pkg() {
if ! command -v "$1" &> /dev/null; then
echo "🔧 Instalando $1..."
apt-get update && apt-get install -y "$1"
else
echo "$1 ya está instalado."
fi
}
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
if [ -f /etc/debian_version ]; then
install_pkg "curl"
install_pkg "git"
install_pkg "jq"
install_pkg "build-essential"
install_pkg "docker.io"
if ! docker compose version &> /dev/null; then
install_pkg "docker-compose-v2"
fi
fi
fi
if ! command -v cargo &> /dev/null; then
echo "🔧 Instalando Rust..."
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source $HOME/.cargo/env
fi
if ! command -v node &> /dev/null; then
echo "🔧 Instalando Node.js vía NVM..."
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
nvm install --lts
fi
if ! command -v sqlx &> /dev/null; then
echo "🔧 Instalando sqlx-cli..."
cargo install sqlx-cli --no-default-features --features postgres
fi
fi
# 4. Environment Configuration
echo ""
if [ ! -f ".env" ]; then
if [ -f ".env.example" ]; then
cp .env.example .env
else
touch .env
fi
fi
update_env() {
local key=$1
local val=$2
if grep -q "^${key}=" .env; then
sed -i "s|^${key}=.*|${key}=${val}|" .env
else
echo "${key}=${val}" >> .env
fi
}
# 5. Configuración de Entorno (Dev/Prod)
echo ""
echo "🌍 Selección de Entorno"
read -p "¿Es un entorno de DESARROLLO o PRODUCCIÓN? [dev/prod]: " ENV_CHOICE
ENV_CHOICE=$(echo "$ENV_CHOICE" | tr '[:upper:]' '[:lower:]')
ENV_CHOICE=${ENV_CHOICE:-prod}
update_env "ENVIRONMENT" "$ENV_CHOICE"
# 6. Configuración de IA Remota (Automática según entorno)
echo ""
echo "🔍 Configurando Servicios de IA Remota ($ENV_CHOICE)..."
# Configuración automática según entorno
if [ "$ENV_CHOICE" == "dev" ]; then
DEFAULT_OLLAMA="http://t-800:11434"
DEFAULT_WHISPER="http://t-800:9000"
DEFAULT_IMAGE="http://t-800:8080"
echo " ✅ Entorno de DESARROLLO detectado"
else
DEFAULT_OLLAMA="http://t-800.norteamericano.cl:11434"
DEFAULT_WHISPER="http://t-800.norteamericano.cl:9000"
DEFAULT_IMAGE="http://t-800.norteamericano.cl:8080"
echo " ✅ Entorno de PRODUCCIÓN detectado"
fi
# Configurar con valores por defecto (sin preguntar)
REMOTE_OLLAMA_URL="$DEFAULT_OLLAMA"
REMOTE_WHISPER_URL="$DEFAULT_WHISPER"
REMOTE_IMAGE_URL="$DEFAULT_IMAGE"
LLM_MODEL="llama3.2:3b"
EMBEDDING_MODEL="nomic-embed-text"
echo " 🤖 Ollama: $REMOTE_OLLAMA_URL"
echo " 🎤 Whisper: $REMOTE_WHISPER_URL"
echo " 🖼️ Image Bridge: $REMOTE_IMAGE_URL"
echo " 🧠 Modelo LLM: $LLM_MODEL"
echo " 📊 Embeddings: $EMBEDDING_MODEL"
update_env "AI_PROVIDER" "local"
update_env "LOCAL_LLM_MODEL" "$LLM_MODEL"
update_env "LOCAL_VIDEO_BRIDGE_URL" "$REMOTE_IMAGE_URL"
update_env "EMBEDDING_MODEL" "$EMBEDDING_MODEL"
update_env "DEV_OLLAMA_URL" "$REMOTE_OLLAMA_URL"
update_env "DEV_WHISPER_URL" "$REMOTE_WHISPER_URL"
update_env "PROD_OLLAMA_URL" "$REMOTE_OLLAMA_URL"
update_env "PROD_WHISPER_URL" "$REMOTE_WHISPER_URL"
update_env "LOCAL_OLLAMA_URL" "$REMOTE_OLLAMA_URL"
update_env "LOCAL_WHISPER_URL" "$REMOTE_WHISPER_URL"
# Configuración SAM (opcional)
echo ""
echo "🔌 Configuración de Integración SAM"
read -p "¿Desea configurar la conexión a la base de datos SAM? [y/N]: " CONFIGURE_SAM
if [[ "$CONFIGURE_SAM" =~ ^[Yy]$ ]]; then
read -p "Ingrese SAM_DATABASE_URL []: " SAM_DB_URL
SAM_DB_URL=${SAM_DB_URL:-""}
if [ -n "$SAM_DB_URL" ]; then
update_env "SAM_DATABASE_URL" "$SAM_DB_URL"
echo " ✅ SAM_DATABASE_URL configurada"
fi
else
echo " ️ SAM no configurado. Puede configurarlo luego en .env"
fi
# Solicitar credenciales de DB si no están configuradas
if ! grep -q "DATABASE_URL=" .env || [[ $(grep "DATABASE_URL=" .env | cut -d'=' -f2) == "" ]]; then
read -p "Ingrese la Contraseña de la Base de Datos [password]: " DB_PASS
DB_PASS=${DB_PASS:-password}
# Usar puerto 5434 (5432 y 5433 ya están en uso)
update_env "DATABASE_URL" "postgresql://user:${DB_PASS}@localhost:5434/openccb?sslmode=disable"
update_env "CMS_DATABASE_URL" "postgresql://user:${DB_PASS}@localhost:5434/openccb_cms?sslmode=disable"
update_env "LMS_DATABASE_URL" "postgresql://user:${DB_PASS}@localhost:5434/openccb_lms?sslmode=disable"
update_env "JWT_SECRET" "supersecretsecret"
update_env "NEXT_PUBLIC_CMS_API_URL" "http://localhost:3001"
update_env "NEXT_PUBLIC_LMS_API_URL" "http://localhost:3003"
update_env "DEFAULT_ORG_NAME" "OpenCCB"
update_env "DEFAULT_PLATFORM_NAME" "OpenCCB Learning"
update_env "DEFAULT_LOGO_URL" ""
update_env "DEFAULT_FAVICON_URL" ""
update_env "DEFAULT_PRIMARY_COLOR" "#3B82F6"
update_env "DEFAULT_SECONDARY_COLOR" "#8B5CF6"
fi
# 5. Configuración de Pila de IA (Omitido - usando remoto)
echo "🌐 Usando servicios de IA remotos en $REMOTE_OLLAMA_URL y $REMOTE_WHISPER_URL"
# 6. Inicialización de la Base de Datos
echo ""
echo "🔌 Configuración de Base de Datos"
echo " Puerto: 5433 (PostgreSQL existente)"
echo ""
# Preguntar si PostgreSQL ya está corriendo (producción)
read -p "¿PostgreSQL ya está corriendo en un contenedor? [y/N]: " DB_EXISTS
DB_EXISTS=${DB_EXISTS:-N}
if [[ ! "$DB_EXISTS" =~ ^[Yy]$ ]]; then
read -p "¿Desea una instalación LIMPIA? (Esto ELIMINARÁ todos los datos existentes) [y/N]: " CLEAN_INSTALL
if [[ "$CLEAN_INSTALL" =~ ^[Yy]$ ]]; then
echo "🐘 Reseteando la base de datos para una instalación limpia..."
docker compose down -v || true
fi
echo "🐘 Iniciando base de datos con Docker..."
docker compose up -d db
echo "⏳ Esperando a que la base de datos esté lista (contenedor)..."
RETRIES=30
until docker exec openccb-db-1 pg_isready -U user &> /dev/null || [ $RETRIES -eq 0 ]; do
echo -n "."
sleep 1
RETRIES=$((RETRIES-1))
done
echo ""
echo "⏳ Esperando al puerto de la base de datos (host)..."
RETRIES=10
until curl -s localhost:5433 &> /dev/null || [ $RETRIES -eq 0 ]; do
echo -n "+"
sleep 1
RETRIES=$((RETRIES-1))
done
echo ""
if [ $RETRIES -eq 0 ]; then
echo "⚠️ Tiempo de espera agotado para el puerto del host, pero continuando..."
fi
# Extra buffer for PostgreSQL initialization
sleep 2
echo "🏗️ Creando bases de datos CMS y LMS..."
docker exec openccb-db-1 psql -U user -d postgres -c "CREATE DATABASE openccb_cms;" || true
docker exec openccb-db-1 psql -U user -d postgres -c "CREATE DATABASE openccb_lms;" || true
else
echo "✅ PostgreSQL ya está corriendo. Saltando inicio del contenedor."
echo " Verificando conexión al puerto 5433..."
sleep 2
fi
CMS_URL=$(grep "CMS_DATABASE_URL=" .env | cut -d'=' -f2-)
LMS_URL=$(grep "LMS_DATABASE_URL=" .env | cut -d'=' -f2-)
echo "🏗️ Ejecutando migraciones..."
DATABASE_URL=$CMS_URL sqlx migrate run --source services/cms-service/migrations
DATABASE_URL=$LMS_URL sqlx migrate run --source services/lms-service/migrations
# PGVector: Generate embeddings for existing data
echo ""
echo "🧠 Configurando PGVector y Embeddings..."
echo " - Extensión vector instalada en ambas bases de datos"
echo " - Índices IVFFlat creados para búsqueda rápida"
echo " - Funciones de similitud y diversidad disponibles"
echo ""
echo "⚠️ Nota: Los embeddings se generarán automáticamente cuando:"
echo " - Importes preguntas desde MySQL"
echo " - Generes preguntas con IA (RAG)"
echo " - Ejecutes: curl -X POST http://localhost:3001/question-bank/embeddings/generate"
echo ""
# Pull embedding model if Ollama is available locally
if curl -s http://localhost:11434/api/tags &> /dev/null; then
echo "📥 Verificando modelo de embeddings en Ollama local..."
if ! curl -s http://localhost:11434/api/tags | grep -q "nomic-embed-text"; then
echo "🔽 Descargando modelo nomic-embed-text..."
docker exec -it ollama ollama pull nomic-embed-text || echo "⚠️ No se pudo descargar el modelo. Se usará el servidor remoto."
else
echo "✅ Modelo de embeddings ya disponible"
fi
else
echo "️ Ollama local no detectado. Se usará el servidor remoto: $REMOTE_OLLAMA_URL"
fi
# 7. System Initialization (Integrated init-system.sh)
echo ""
echo "🔍 Buscando administrador existente..."
ADMIN_EXISTS=$(docker exec openccb-db-1 psql -U user -d openccb_cms -t -c "SELECT EXISTS (SELECT 1 FROM users WHERE role = 'admin');" | xargs 2>/dev/null || echo "f")
if [ "$ADMIN_EXISTS" != "t" ]; then
echo "👤 Configurar Administrador Inicial"
read -p "Nombre Completo [Administrador del Sistema]: " ADMIN_NAME
ADMIN_NAME=${ADMIN_NAME:-Administrador del Sistema}
read -p "Email del Administrador [admin@norteamericano.cl]: " ADMIN_EMAIL
ADMIN_EMAIL=${ADMIN_EMAIL:-admin@norteamericano.cl}
read -s -p "Contraseña del Administrador [Admin123!]: " ADMIN_PASS
ADMIN_PASS=${ADMIN_PASS:-Admin123!}
echo ""
read -p "Nombre de la Organización [Norteamericano]: " ORG_NAME
ORG_NAME=${ORG_NAME:-Norteamericano}
fi
# Selective Build/Rebuild
if [ "$FAST_MODE" == "true" ]; then
echo "⚡ Modo FAST activado. Saltando comprobaciones y reconstrucción de imágenes."
docker compose up -d
else
echo ""
read -p "¿Desea RECONSTRUIR las imágenes de Docker? (Recomendado si hay cambios de código) [y/N]: " REBUILD_CHOICE
if [[ "$REBUILD_CHOICE" =~ ^[Yy]$ ]]; then
echo "🚀 Reconstruyendo e iniciando servicios..."
docker compose up -d --build
else
echo "🚀 Iniciando servicios (sin reconstruir)..."
docker compose up -d
fi
fi
if [ "$ADMIN_EXISTS" != "t" ]; then
echo "⏳ Esperando a que el API CMS esté listo..."
API_URL="http://localhost:3001"
# Wait until the API actually responds (not just the port being open)
MAX_RETRIES=30
count=0
echo -n "Esperando API"
until curl -s -o /dev/null "$API_URL/health" 2>/dev/null; do
echo -n "."
sleep 2
count=$((count+1))
if [ $count -ge $MAX_RETRIES ]; then
echo ""
echo "⚠️ Tiempo de espera agotado. El API no respondió."
break
fi
done
echo ""
# Create admin user directly in database using pgcrypto
echo "🔐 Creando administrador en la base de datos..."
docker exec openccb-db-1 psql -U user -d openccb_cms -c "
CREATE EXTENSION IF NOT EXISTS pgcrypto;
SELECT * FROM fn_register_user(
'$ADMIN_EMAIL',
crypt('$ADMIN_PASS', gen_salt('bf', 12)),
'$ADMIN_NAME',
'admin',
'$ORG_NAME'
);
" 2>/dev/null
if [ $? -eq 0 ]; then
echo "✅ ¡Éxito! Administrador creado."
API_KEY=$(docker exec openccb-db-1 psql -U user -d openccb_cms -t -c "SELECT api_key FROM organizations LIMIT 1;" | xargs 2>/dev/null)
echo "🔑 API Key Inicial: $API_KEY"
echo ""
echo "📋 Credenciales de acceso:"
echo " Email: $ADMIN_EMAIL"
echo " Contraseña: $ADMIN_PASS"
else
echo "⚠️ Fallo al crear el administrador. Intentando con método alternativo..."
# Fallback: Try API endpoint
PAYLOAD=$(cat <<EOF
{
"email": "$ADMIN_EMAIL",
"password": "$ADMIN_PASS",
"full_name": "$ADMIN_NAME",
"organization_name": "$ORG_NAME",
"role": "admin"
}
EOF
)
RESPONSE=$(curl -s -X POST "$API_URL/auth/register" -H "Content-Type: application/json" -d "$PAYLOAD")
if echo "$RESPONSE" | grep -q "token"; then
echo "✅ ¡Éxito! Administrador creado vía API."
API_KEY=$(docker exec openccb-db-1 psql -U user -d openccb_cms -t -c "SELECT api_key FROM organizations LIMIT 1;" | xargs 2>/dev/null)
echo "🔑 API Key Inicial: $API_KEY"
else
echo "⚠️ Fallo al crear el administrador. Respuesta: $RESPONSE"
fi
fi
else
echo "✅ El administrador ya existe. Saltando registro."
fi
echo ""
echo "===================================================="
echo " ✨ ¡Instalación de OpenCCB Completa!"
echo "===================================================="
echo "Studio (Admin/CMS): http://localhost:3000"
echo "Experience (LMS): http://localhost:3003"
echo "===================================================="
echo ""
echo "📋 Notas:"
echo " - Rate limiter: DESHABILITADO (problemas de compatibilidad)"
echo " - Para producción, configura tower_governor en services/cms-service/src/main.rs"
echo " - PGVector: Habilitado para búsqueda semántica"
echo " - Embeddings: Usando modelo '$EMBEDDING_MODEL'"
echo ""
echo "🔗 Comandos Útiles:"
echo " # Generar embeddings para preguntas existentes"
echo " curl -X POST http://localhost:3001/question-bank/embeddings/generate -H \"Authorization: Bearer TOKEN\""
echo ""
echo " # Búsqueda semántica de preguntas"
echo " curl -G \"http://localhost:3001/question-bank/semantic-search?query=past+tense\""
echo ""
echo " # Detectar preguntas duplicadas"
echo " curl -G \"http://localhost:3001/question-bank/similar/{id}?threshold=0.95\""
echo "===================================================="
# ============================================================================
# MODO DESPLIEGUE (PRODUCCIÓN)
# ============================================================================
if [ "$DEPLOY_MODE" == "true" ]; then
echo ""
echo "===================================================="
echo " 🚀 Modo de Despliegue Activado"
echo "===================================================="
echo ""
echo "️ El despliegue remoto se maneja mediante deploy.sh"
echo ""
echo "📋 Ejecutando: ./deploy.sh"
echo ""
# Verificar que existe deploy.sh
if [ ! -f "$SCRIPT_DIR/deploy.sh" ]; then
echo "❌ ERROR: No se encontró deploy.sh"
echo ""
echo "Asegúrate de que deploy.sh esté en el mismo directorio que install.sh"
exit 1
fi
# Ejecutar deploy.sh
exec "$SCRIPT_DIR/deploy.sh"
fi