#!/usr/bin/env bash # ============================================================================= # COCONEWS - POC local (sin Docker, sin ML workers) # Levanta backend + frontend con datos de prueba en ~2 minutos # # Requisitos mínimos: Go 1.25, Node.js 18+, PostgreSQL, Redis # Uso: bash poc/poc.sh # bash poc/poc.sh --clean (borra BD y empieza de cero) # ============================================================================= set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" POC_DIR="$REPO_ROOT/poc" TMP_DIR="/tmp/coconews-poc" PID_FILE="$TMP_DIR/pids" LOG_DIR="$TMP_DIR/logs" CLEAN_MODE=false [[ "${1:-}" == "--clean" ]] && CLEAN_MODE=true export PATH=$PATH:/usr/local/go/bin RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' BLUE='\033[0;34m'; BOLD='\033[1m'; DIM='\033[2m'; NC='\033[0m' info() { echo -e "${GREEN}[✓]${NC} $*"; } step() { echo -e "${BLUE}[→]${NC} $*"; } warn() { echo -e "${YELLOW}[!]${NC} $*"; } error() { echo -e "${RED}[✗] $*${NC}" echo -e "${DIM} → Logs en: $LOG_DIR/${NC}" exit 1 } # Muestra las últimas líneas de un log con contexto show_log_tail() { local logfile="$1" local lines="${2:-20}" if [[ -f "$logfile" && -s "$logfile" ]]; then echo -e "${DIM}--- últimas líneas de $(basename "$logfile") ---${NC}" tail -n "$lines" "$logfile" | sed 's/^/ /' echo -e "${DIM}--- fin del log ---${NC}" fi } # Manejar Ctrl+C cleanup() { echo "" warn "Deteniendo POC..." if [[ -f "$PID_FILE" ]]; then while IFS= read -r pid; do kill "$pid" 2>/dev/null || true done < "$PID_FILE" rm -f "$PID_FILE" fi # Parar Redis POC si está corriendo [[ -f "$TMP_DIR/redis-poc.pid" ]] && \ kill "$(cat "$TMP_DIR/redis-poc.pid")" 2>/dev/null || true echo -e "${YELLOW}POC detenido.${NC}" exit 0 } trap cleanup INT TERM mkdir -p "$TMP_DIR" "$LOG_DIR" "$TMP_DIR/bin" > "$PID_FILE" echo "" echo -e "${BOLD}=================================================${NC}" echo -e "${BOLD} COCONEWS · POC Local${NC}" echo -e "${BOLD}=================================================${NC}" echo "" [[ "$CLEAN_MODE" == "true" ]] && warn "Modo --clean: se borrará la BD de prueba anterior" # ============================================================================= # 1. VERIFICAR PREREQUISITOS # ============================================================================= step "Verificando prerequisitos..." PREREQ_OK=true # Go if ! command -v go &>/dev/null; then echo -e " ${RED}[✗]${NC} Go no encontrado" echo -e " Instala Go 1.25: https://go.dev/dl/" echo -e " O en Debian: sudo bash deploy/debian/prerequisites.sh" PREREQ_OK=false else GO_VER=$(go version | awk '{print $3}' | tr -d 'go') IFS='.' read -ra V <<< "$GO_VER" if [[ "${V[0]}" -lt 1 ]] || [[ "${V[0]}" -eq 1 && "${V[1]:-0}" -lt 22 ]]; then echo -e " ${RED}[✗]${NC} Go ${GO_VER} instalado, se necesita 1.22+ (recomendado 1.25)" PREREQ_OK=false else echo -e " ${GREEN}[✓]${NC} Go ${GO_VER}" fi fi # Node.js if ! command -v node &>/dev/null; then echo -e " ${RED}[✗]${NC} Node.js no encontrado" echo -e " Instala: curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash -" echo -e " sudo apt-get install -y nodejs" PREREQ_OK=false else NODE_VER=$(node -v | tr -d 'v') NODE_MAJOR=$(echo "$NODE_VER" | cut -d. -f1) if [[ "$NODE_MAJOR" -lt 18 ]]; then echo -e " ${RED}[✗]${NC} Node.js v${NODE_VER} instalado, se necesita v18+" PREREQ_OK=false else echo -e " ${GREEN}[✓]${NC} Node.js v${NODE_VER}" fi fi # PostgreSQL if ! command -v psql &>/dev/null; then echo -e " ${RED}[✗]${NC} psql no encontrado" echo -e " Instala: sudo apt-get install -y postgresql postgresql-client" PREREQ_OK=false else echo -e " ${GREEN}[✓]${NC} PostgreSQL $(psql --version | awk '{print $3}')" fi # Redis if ! command -v redis-cli &>/dev/null; then echo -e " ${RED}[✗]${NC} redis-cli no encontrado" echo -e " Instala: sudo apt-get install -y redis-server" PREREQ_OK=false else echo -e " ${GREEN}[✓]${NC} Redis $(redis-server --version | awk '{print $3}' | tr -d ',')" fi # curl (para health check del backend) if ! command -v curl &>/dev/null; then echo -e " ${RED}[✗]${NC} curl no encontrado" echo -e " Instala: sudo apt-get install -y curl" PREREQ_OK=false fi [[ "$PREREQ_OK" == "false" ]] && { echo "" error "Faltan prerequisitos. Corrígelos y vuelve a ejecutar." } info "Todos los prerequisitos OK" # ============================================================================= # 2. CONFIGURACION POC # ============================================================================= POC_DB_NAME="coconews_poc" POC_DB_USER="coconews_poc" POC_DB_PASS="poc_password_local" POC_REDIS_PORT="6380" POC_API_PORT="18080" POC_FRONTEND_PORT="18001" export DATABASE_URL="postgres://${POC_DB_USER}:${POC_DB_PASS}@127.0.0.1:5432/${POC_DB_NAME}?sslmode=disable" export REDIS_URL="redis://127.0.0.1:${POC_REDIS_PORT}" export SECRET_KEY="poc_secret_key_solo_para_desarrollo_local" export SERVER_PORT="$POC_API_PORT" export GIN_MODE="release" # Verificar que los puertos necesarios estén libres check_port() { local port="$1" name="$2" if ss -tlnp 2>/dev/null | grep -q ":${port} " || \ lsof -i ":${port}" &>/dev/null 2>&1; then warn "Puerto ${port} (${name}) ya en uso." echo -e " Proceso usando ese puerto:" ss -tlnp 2>/dev/null | grep ":${port} " | sed 's/^/ /' || true echo -e " Puedes cambiarlo editando las variables POC_*_PORT en poc.sh" return 1 fi return 0 } step "Verificando puertos disponibles..." PORT_OK=true check_port "$POC_API_PORT" "API backend" || PORT_OK=false check_port "$POC_FRONTEND_PORT" "Frontend" || PORT_OK=false check_port "$POC_REDIS_PORT" "Redis POC" || PORT_OK=false [[ "$PORT_OK" == "false" ]] && error "Hay puertos en conflicto. Resuélvelos antes de continuar." info "Puertos ${POC_API_PORT}, ${POC_FRONTEND_PORT}, ${POC_REDIS_PORT} disponibles" # ============================================================================= # 3. POSTGRESQL # ============================================================================= step "Preparando base de datos POC (${POC_DB_NAME})..." # Verificar que PostgreSQL está corriendo if ! pg_isready -q 2>/dev/null; then warn "PostgreSQL no está corriendo. Intentando iniciar..." sudo systemctl start postgresql 2>/dev/null || \ sudo service postgresql start 2>/dev/null || { echo -e " ${RED}Fallo al iniciar PostgreSQL.${NC}" echo -e " Posibles causas y soluciones:" echo -e " • Ver logs: sudo journalctl -u postgresql -n 30" echo -e " • Ver estado: sudo systemctl status postgresql" echo -e " • Puerto en uso: sudo ss -tlnp | grep 5432" echo -e " • Datos corruptos: sudo pg_ctlcluster 16 main status" error "PostgreSQL no pudo iniciarse." } sleep 2 pg_isready -q || error "PostgreSQL sigue sin responder tras el inicio." fi info "PostgreSQL corriendo" # Limpiar si --clean if [[ "$CLEAN_MODE" == "true" ]]; then sudo -u postgres psql -q -c "DROP DATABASE IF EXISTS ${POC_DB_NAME};" 2>/dev/null || true sudo -u postgres psql -q -c "DROP USER IF EXISTS ${POC_DB_USER};" 2>/dev/null || true info "BD anterior eliminada" fi # Crear usuario POC sudo -u postgres psql -q -tc "SELECT 1 FROM pg_roles WHERE rolname='${POC_DB_USER}'" \ | grep -q 1 2>/dev/null || \ sudo -u postgres psql -q -c "CREATE USER ${POC_DB_USER} WITH PASSWORD '${POC_DB_PASS}';" \ 2>"$LOG_DIR/psql-setup.log" || { show_log_tail "$LOG_DIR/psql-setup.log" echo -e " Verifica que tienes permisos sudo para el usuario postgres:" echo -e " sudo -u postgres psql -c '\\du'" error "No se pudo crear el usuario PostgreSQL ${POC_DB_USER}." } # Crear BD sudo -u postgres psql -q -tc "SELECT 1 FROM pg_database WHERE datname='${POC_DB_NAME}'" \ | grep -q 1 2>/dev/null || \ sudo -u postgres createdb -O "${POC_DB_USER}" "${POC_DB_NAME}" \ 2>"$LOG_DIR/psql-createdb.log" || { show_log_tail "$LOG_DIR/psql-createdb.log" error "No se pudo crear la base de datos ${POC_DB_NAME}." } # Schema sudo -u postgres psql -q -d "${POC_DB_NAME}" \ -f "$REPO_ROOT/init-db/00-complete-schema.sql" \ >"$LOG_DIR/psql-schema.log" 2>&1 || \ warn "Algunos statements del schema fallaron (puede ser normal si ya existían)" # Permisos sudo -u postgres psql -q -d "${POC_DB_NAME}" \ -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ${POC_DB_USER}; GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO ${POC_DB_USER}; GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public TO ${POC_DB_USER};" \ 2>/dev/null || true # Seed sudo -u postgres psql -q -d "${POC_DB_NAME}" \ -f "$POC_DIR/seed.sql" >"$LOG_DIR/psql-seed.log" 2>&1 || \ warn "Algunos inserts del seed fallaron (puede que ya existieran)" NEWS_COUNT=$(sudo -u postgres psql -tq -d "${POC_DB_NAME}" \ -c "SELECT COUNT(*) FROM noticias;" 2>/dev/null | tr -d ' \n' || echo "?") info "BD lista: ${NEWS_COUNT} noticias de prueba" # ============================================================================= # 4. REDIS (instancia temporal en puerto alternativo) # ============================================================================= step "Iniciando Redis en puerto ${POC_REDIS_PORT}..." redis-server \ --port "$POC_REDIS_PORT" \ --daemonize yes \ --logfile "$LOG_DIR/redis.log" \ --pidfile "$TMP_DIR/redis-poc.pid" \ --maxmemory 128mb \ --maxmemory-policy allkeys-lru \ 2>"$LOG_DIR/redis-start.log" || { show_log_tail "$LOG_DIR/redis-start.log" echo -e " Posibles causas:" echo -e " • Puerto ${POC_REDIS_PORT} en uso: sudo ss -tlnp | grep ${POC_REDIS_PORT}" echo -e " • Permisos: redis-server necesita poder escribir en $LOG_DIR" error "Redis no pudo iniciarse en puerto ${POC_REDIS_PORT}." } sleep 1 if redis-cli -p "$POC_REDIS_PORT" ping 2>/dev/null | grep -q PONG; then info "Redis OK (puerto ${POC_REDIS_PORT})" else show_log_tail "$LOG_DIR/redis.log" error "Redis inició pero no responde a PING." fi cat "$TMP_DIR/redis-poc.pid" 2>/dev/null >> "$PID_FILE" || true # ============================================================================= # 5. COMPILAR BACKEND GO # ============================================================================= step "Compilando backend Go..." (cd "$REPO_ROOT/backend" && \ CGO_ENABLED=0 go build -buildvcs=false \ -o "$TMP_DIR/bin/server" ./cmd/server \ 2>"$LOG_DIR/build-backend.log") || { echo "" # Filtrar errores relevantes del log de compilación echo -e " ${RED}Error de compilación:${NC}" grep -E "^(.*\.go:[0-9]+:|error:|undefined:)" "$LOG_DIR/build-backend.log" \ | head -20 | sed 's/^/ /' || show_log_tail "$LOG_DIR/build-backend.log" 15 echo "" echo -e " Posibles causas:" echo -e " • Version de Go insuficiente: $(go version)" echo -e " Se necesita 1.22+. Instala: sudo bash deploy/debian/prerequisites.sh" echo -e " • Dependencias faltantes: cd backend && go mod download" echo -e " • Log completo: cat $LOG_DIR/build-backend.log" error "Fallo al compilar backend Go." } info "Backend compilado OK" # ============================================================================= # 6. ARRANCAR BACKEND API # ============================================================================= step "Arrancando API en puerto ${POC_API_PORT}..." "$TMP_DIR/bin/server" > "$LOG_DIR/backend.log" 2>&1 & BACKEND_PID=$! echo "$BACKEND_PID" >> "$PID_FILE" # Esperar con feedback visual printf " Esperando respuesta" BACKEND_UP=false for i in {1..20}; do sleep 1 printf "." if curl -sf "http://127.0.0.1:${POC_API_PORT}/api/stats" &>/dev/null; then BACKEND_UP=true break fi # Detectar si el proceso murió if ! kill -0 "$BACKEND_PID" 2>/dev/null; then break fi done echo "" if [[ "$BACKEND_UP" == "false" ]]; then echo "" echo -e " ${RED}El backend no arrancó correctamente.${NC}" echo -e " ${BOLD}Log del backend:${NC}" show_log_tail "$LOG_DIR/backend.log" 25 echo "" echo -e " Posibles causas:" echo -e " • Error de conexión a BD: revisa DATABASE_URL" echo -e " Prueba: psql \"${DATABASE_URL}\" -c 'SELECT 1'" echo -e " • Error de conexión a Redis: revisa REDIS_URL" echo -e " Prueba: redis-cli -p ${POC_REDIS_PORT} ping" echo -e " • Puerto ${POC_API_PORT} ocupado: ss -tlnp | grep ${POC_API_PORT}" echo -e " • Log completo: cat $LOG_DIR/backend.log" error "Backend no responde." fi info "API respondiendo en http://127.0.0.1:${POC_API_PORT}" # ============================================================================= # 7. FRONTEND REACT # ============================================================================= step "Preparando frontend React..." cd "$REPO_ROOT/frontend" if [[ ! -d node_modules ]]; then step " Instalando dependencias npm (primera vez, puede tardar)..." npm install 2>"$LOG_DIR/npm-install.log" || { show_log_tail "$LOG_DIR/npm-install.log" 20 echo -e " Posibles causas:" echo -e " • Sin conexión a internet (npm registry)" echo -e " • Versión de Node.js incompatible: $(node -v)" echo -e " • Disco lleno: df -h ." echo -e " • Prueba manualmente: cd frontend && npm install" error "npm install falló." } fi step " Compilando frontend..." VITE_API_URL="http://127.0.0.1:${POC_API_PORT}" \ npm run build -- --outDir "$TMP_DIR/frontend-dist" \ >"$LOG_DIR/build-frontend.log" 2>&1 || { echo -e " ${RED}Error de compilación del frontend:${NC}" grep -E "(error TS|Error:|ERROR)" "$LOG_DIR/build-frontend.log" \ | head -15 | sed 's/^/ /' || show_log_tail "$LOG_DIR/build-frontend.log" 15 echo "" echo -e " Posibles causas:" echo -e " • Error TypeScript: revisa cambios recientes en src/" echo -e " • Log completo: cat $LOG_DIR/build-frontend.log" error "Compilación del frontend falló." } info "Frontend compilado OK" step " Sirviendo frontend en puerto ${POC_FRONTEND_PORT}..." npx --yes serve "$TMP_DIR/frontend-dist" -l "$POC_FRONTEND_PORT" \ > "$LOG_DIR/frontend-serve.log" 2>&1 & FRONTEND_PID=$! echo "$FRONTEND_PID" >> "$PID_FILE" sleep 2 if ! kill -0 "$FRONTEND_PID" 2>/dev/null; then show_log_tail "$LOG_DIR/frontend-serve.log" echo -e " Prueba manual: npx serve $TMP_DIR/frontend-dist -l ${POC_FRONTEND_PORT}" error "El servidor del frontend no arrancó." fi info "Frontend sirviendo en http://127.0.0.1:${POC_FRONTEND_PORT}" cd "$REPO_ROOT" # ============================================================================= # RESUMEN FINAL # ============================================================================= echo "" echo -e "${BOLD}${GREEN}=================================================${NC}" echo -e "${BOLD}${GREEN} COCONEWS POC corriendo${NC}" echo -e "${BOLD}${GREEN}=================================================${NC}" echo "" echo -e " ${BOLD}Abrir en el navegador:${NC}" echo -e " → http://127.0.0.1:${POC_FRONTEND_PORT}" echo "" echo -e " ${BOLD}Endpoints útiles:${NC}" echo -e " API stats: http://127.0.0.1:${POC_API_PORT}/api/stats" echo -e " API noticias: http://127.0.0.1:${POC_API_PORT}/api/news" echo "" echo -e " ${BOLD}Primer login:${NC}" echo -e " Regístrate en la UI → el primer usuario es admin automáticamente" echo "" echo -e " ${BOLD}Datos cargados:${NC} ${NEWS_COUNT} noticias de prueba en español" echo -e " ${YELLOW}Sin workers ML:${NC} no hay traducción ni entidades (normal en POC)" echo "" echo -e " ${BOLD}Si algo falla:${NC}" echo -e " Logs: $LOG_DIR/" echo -e " Backend: tail -f $LOG_DIR/backend.log" echo -e " Limpiar: bash poc/poc.sh --clean" echo "" echo -e " ${DIM}Pulsa Ctrl+C para detener${NC}" echo "" wait