feat: prerequisites, POC local y README reescrito

deploy/debian/prerequisites.sh:
- Instalador de dependencias del sistema para Debian/Ubuntu
- Detecta OS, instala PostgreSQL 16 (repo oficial), Redis, nginx,
  Go 1.25, Node.js 20 LTS, Qdrant binario, Python venv
- Crea usuario rss2 y estructura /opt/rss2
- Pregunta interactivamente si instalar modelos ML pesados
  (ctranslate2, transformers, spaCy es_core_news_lg, NLLB-200)
- Separado de install.sh para poder ejecutarlo independientemente

poc/poc.sh:
- POC local en ~2 minutos sin Docker, sin workers ML
- Crea BD temporal coconews_poc con schema completo
- Carga 10 noticias de muestra en español listas para ver
- Compila backend Go y frontend React en /tmp/coconews-poc
- Lanza Redis en puerto alternativo (6380) sin interferir
- Sirve frontend con npx serve en http://127.0.0.1:18001
- Limpieza automatica al Ctrl+C

poc/seed.sql:
- 10 noticias de muestra en español (no requieren traduccion)
- Categorias, continentes y paises basicos
- 5 feeds de ejemplo (El Pais, BBC Mundo, etc.)

README.md:
- Reescrito completamente sin referencias Docker
- Diagrama ASCII de arquitectura
- Inicio rapido con poc.sh (2 minutos)
- Instrucciones de install en Debian con prerequisites.sh
- Tabla de requisitos hardware por modo
- Mapa completo del repositorio

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
SITO 2026-03-30 21:17:11 +02:00
parent ab3b0b53c5
commit e3c682a36f
4 changed files with 766 additions and 101 deletions

213
poc/poc.sh Executable file
View file

@ -0,0 +1,213 @@
#!/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
# =============================================================================
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"
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'; NC='\033[0m'
info() { echo -e "${GREEN}[✓]${NC} $*"; }
step() { echo -e "${BLUE}[→]${NC} $*"; }
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
error() { echo -e "${RED}[✗]${NC} $*"; exit 1; }
# Manejar Ctrl+C: para todos los procesos del POC
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
echo -e "${YELLOW}POC detenido.${NC}"
exit 0
}
trap cleanup INT TERM
mkdir -p "$TMP_DIR"
> "$PID_FILE"
echo ""
echo -e "${BOLD}=================================================${NC}"
echo -e "${BOLD} COCONEWS · POC Local${NC}"
echo -e "${BOLD}=================================================${NC}"
echo ""
# =============================================================================
# 1. VERIFICAR PREREQUISITOS
# =============================================================================
step "Verificando prerequisitos..."
command -v go &>/dev/null || error "Go no encontrado. Instala Go 1.25+."
command -v psql &>/dev/null || error "PostgreSQL no encontrado. Instala postgresql."
command -v redis-cli &>/dev/null || error "Redis no encontrado. Instala redis-server."
command -v node &>/dev/null || error "Node.js no encontrado. Instala Node.js 18+."
info "Prerequisitos OK (Go: $(go version | awk '{print $3}'), Node: $(node -v))"
# =============================================================================
# 2. CONFIGURACION POC (sin contrasenas fuertes, solo local)
# =============================================================================
POC_DB_NAME="coconews_poc"
POC_DB_USER="coconews_poc"
POC_DB_PASS="poc_password_local"
POC_REDIS_PORT="6380" # Puerto alternativo para no interferir con Redis principal
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"
# =============================================================================
# 3. POSTGRESQL - crear DB de prueba
# =============================================================================
step "Preparando base de datos POC (${POC_DB_NAME})..."
# Verificar que PostgreSQL está corriendo
pg_isready -q || {
warn "PostgreSQL no está corriendo. Intentando iniciar..."
sudo systemctl start postgresql 2>/dev/null || \
sudo service postgresql start 2>/dev/null || \
error "No se puede iniciar PostgreSQL. Inícialo manualmente."
}
# Crear usuario y BD de prueba
sudo -u postgres psql -q -tc "SELECT 1 FROM pg_roles WHERE rolname='${POC_DB_USER}'" \
| grep -q 1 || \
sudo -u postgres psql -q -c "CREATE USER ${POC_DB_USER} WITH PASSWORD '${POC_DB_PASS}';" 2>/dev/null
sudo -u postgres psql -q -tc "SELECT 1 FROM pg_database WHERE datname='${POC_DB_NAME}'" \
| grep -q 1 || \
sudo -u postgres createdb -O "${POC_DB_USER}" "${POC_DB_NAME}" 2>/dev/null
# Aplicar schema completo
sudo -u postgres psql -q -d "${POC_DB_NAME}" \
-f "$REPO_ROOT/init-db/00-complete-schema.sql" 2>/dev/null || true
# Otorgar permisos al usuario POC
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};" \
2>/dev/null || true
# Cargar datos de muestra
sudo -u postgres psql -q -d "${POC_DB_NAME}" -f "$POC_DIR/seed.sql" 2>/dev/null || true
NEWS_COUNT=$(sudo -u postgres psql -tq -d "${POC_DB_NAME}" -c "SELECT COUNT(*) FROM noticias;" 2>/dev/null | tr -d ' ')
info "BD lista: ${NEWS_COUNT} noticias de prueba cargadas"
# =============================================================================
# 4. REDIS (instancia temporal en puerto alternativo)
# =============================================================================
step "Iniciando Redis en puerto ${POC_REDIS_PORT}..."
redis-server --port "$POC_REDIS_PORT" --daemonize yes \
--logfile "$TMP_DIR/redis-poc.log" \
--pidfile "$TMP_DIR/redis-poc.pid" \
--maxmemory 128mb --maxmemory-policy allkeys-lru \
2>/dev/null || warn "Redis ya corriendo en ${POC_REDIS_PORT}"
sleep 1
redis-cli -p "$POC_REDIS_PORT" ping &>/dev/null && info "Redis OK (puerto ${POC_REDIS_PORT})"
cat "$TMP_DIR/redis-poc.pid" 2>/dev/null >> "$PID_FILE" || true
# =============================================================================
# 5. COMPILAR BACKEND GO
# =============================================================================
step "Compilando backend Go..."
mkdir -p "$TMP_DIR/bin"
(cd "$REPO_ROOT/backend" && \
CGO_ENABLED=0 go build -buildvcs=false -o "$TMP_DIR/bin/server" ./cmd/server \
2>"$TMP_DIR/build-backend.log") || {
cat "$TMP_DIR/build-backend.log"
error "Fallo al compilar backend. Ver log arriba."
}
info "Backend compilado OK"
# =============================================================================
# 6. ARRANCAR BACKEND API
# =============================================================================
step "Arrancando API en puerto ${POC_API_PORT}..."
"$TMP_DIR/bin/server" > "$TMP_DIR/backend.log" 2>&1 &
BACKEND_PID=$!
echo "$BACKEND_PID" >> "$PID_FILE"
# Esperar a que el backend responda
for i in {1..15}; do
sleep 1
if curl -sf "http://127.0.0.1:${POC_API_PORT}/api/stats" &>/dev/null; then
info "API respondiendo en http://127.0.0.1:${POC_API_PORT}"
break
fi
if [[ $i -eq 15 ]]; then
cat "$TMP_DIR/backend.log"
error "El backend no responde. Ver log arriba."
fi
done
# =============================================================================
# 7. FRONTEND REACT
# =============================================================================
step "Preparando frontend..."
cd "$REPO_ROOT/frontend"
if [[ ! -d node_modules ]]; then
step " Instalando dependencias npm (primera vez)..."
npm install --silent
fi
# Compilar apuntando al API local del POC
VITE_API_URL="http://127.0.0.1:${POC_API_PORT}" \
npm run build -- --outDir "$TMP_DIR/frontend-dist" 2>"$TMP_DIR/build-frontend.log" || {
cat "$TMP_DIR/build-frontend.log"
error "Fallo al compilar frontend. Ver log arriba."
}
info "Frontend compilado OK"
# Servir frontend con npx serve (simple, sin nginx)
step "Sirviendo frontend en puerto ${POC_FRONTEND_PORT}..."
npx --yes serve "$TMP_DIR/frontend-dist" -l "$POC_FRONTEND_PORT" \
> "$TMP_DIR/frontend.log" 2>&1 &
FRONTEND_PID=$!
echo "$FRONTEND_PID" >> "$PID_FILE"
sleep 2
cd "$REPO_ROOT"
# =============================================================================
# LISTO
# =============================================================================
echo ""
echo -e "${BOLD}${GREEN}=================================================${NC}"
echo -e "${BOLD}${GREEN} COCONEWS POC corriendo${NC}"
echo -e "${BOLD}${GREEN}=================================================${NC}"
echo ""
echo -e " ${BOLD}Frontend:${NC} http://127.0.0.1:${POC_FRONTEND_PORT}"
echo -e " ${BOLD}API:${NC} http://127.0.0.1:${POC_API_PORT}/api/stats"
echo ""
echo -e " ${BOLD}Login:${NC} Registra el primer usuario en la UI"
echo -e " (será admin automáticamente)"
echo ""
echo -e " ${BOLD}Noticias:${NC} ${NEWS_COUNT} artículos de prueba en español"
echo -e " ${YELLOW}Nota:${NC} Sin workers ML activos."
echo -e " Noticias no tendrán traducción ni entidades."
echo ""
echo -e " ${BLUE}Logs:${NC} $TMP_DIR/*.log"
echo ""
echo -e " Pulsa ${BOLD}Ctrl+C${NC} para detener el POC."
echo ""
# Mantener el script corriendo
wait

94
poc/seed.sql Normal file
View file

@ -0,0 +1,94 @@
-- =============================================================================
-- COCONEWS POC - Datos de prueba mínimos
-- Carga rápida para ver la interfaz funcionando sin workers ML
-- =============================================================================
-- Taxonomía base
INSERT INTO continentes (id, nombre) VALUES
(1, 'África'), (2, 'América'), (3, 'Asia'),
(4, 'Europa'), (5, 'Oceanía')
ON CONFLICT (id) DO NOTHING;
INSERT INTO categorias (nombre) VALUES
('Ciencia'), ('Cultura'), ('Deportes'), ('Economía'),
('Internacional'), ('Política'), ('Salud'), ('Tecnología'), ('Sociedad')
ON CONFLICT DO NOTHING;
INSERT INTO paises (nombre, continente_id) VALUES
('España', 4),
('Argentina', 2),
('México', 2),
('Francia', 4),
('Estados Unidos', 2)
ON CONFLICT DO NOTHING;
-- Config básica
INSERT INTO config (key, value) VALUES
('translator_type', 'cpu'),
('translator_workers', '1'),
('translator_status', 'stopped')
ON CONFLICT (key) DO NOTHING;
-- Feeds de muestra (en español, no necesitan traducción)
INSERT INTO feeds (nombre, descripcion, url, idioma, activo, fallos)
VALUES
('El País', 'Noticias de España y el mundo', 'https://feeds.elpais.com/mrss-s/pages/ep/site/elpais.com/portada', 'es', true, 0),
('El Mundo', 'Diario de información general', 'https://e00-elmundo.uecdn.es/elmundo/rss/portada.xml', 'es', true, 0),
('La Vanguardia','Noticias de España y Cataluña', 'https://www.lavanguardia.com/mvc/feed/rss/home', 'es', true, 0),
('BBC Mundo', 'Noticias en español de la BBC', 'https://feeds.bbci.co.uk/mundo/rss.xml', 'es', true, 0),
('RT Español', 'Russia Today en español', 'https://actualidad.rt.com/rss', 'es', true, 0)
ON CONFLICT (url) DO NOTHING;
-- Noticias de muestra (en español, listas para mostrarse sin traducción)
INSERT INTO noticias (id, titulo, resumen, url, fecha, fuente_nombre, categoria_id, lang, topics_processed)
VALUES
(md5('poc-001'), 'La inteligencia artificial transforma el mercado laboral global',
'Los modelos de lenguaje de gran escala están redefiniendo sectores enteros de la economía, desde el servicio al cliente hasta el desarrollo de software. Empresas de todo el mundo aceleran su adopción mientras sindicatos y gobiernos debaten marcos regulatorios.',
'https://example.com/ia-mercado-laboral', NOW() - INTERVAL '2 hours',
'El País', 8, 'es', false),
(md5('poc-002'), 'Cumbre climática de la ONU aprueba fondo de 100.000 millones para países vulnerables',
'Los representantes de 196 países alcanzaron un acuerdo histórico en la última jornada de negociaciones. El fondo estará operativo en 2026 y priorizará adaptación en África subsahariana y pequeñas islas del Pacífico.',
'https://example.com/cumbre-climatica-onu', NOW() - INTERVAL '4 hours',
'BBC Mundo', 5, 'es', false),
(md5('poc-003'), 'España registra el mayor crecimiento económico de la eurozona en el primer trimestre',
'El PIB español creció un 3,2% interanual en los primeros tres meses del año, impulsado por el turismo, las exportaciones y el consumo interno. El Banco de España revisa al alza sus previsiones para el conjunto del ejercicio.',
'https://example.com/economia-espana-pib', NOW() - INTERVAL '5 hours',
'El Mundo', 4, 'es', false),
(md5('poc-004'), 'La selección española de fútbol golea en la fase de clasificación',
'La Roja aplastó por 4-0 al combinado rival con dos goles de Yamal y otros tantos de Morata en un partido que dejó pocas dudas sobre el potencial del equipo de Luis de la Fuente de cara al próximo gran torneo.',
'https://example.com/seleccion-espana-futbol', NOW() - INTERVAL '6 hours',
'La Vanguardia', 3, 'es', false),
(md5('poc-005'), 'Descubrimiento arqueológico en Extremadura revela ciudad romana inédita',
'Un equipo de la Universidad de Extremadura ha localizado los restos de un asentamiento romano del siglo II d.C. con teatro, termas y foro en perfecto estado de conservación bajo un olivar de la comarca de La Serena.',
'https://example.com/arqueologia-extremadura', NOW() - INTERVAL '8 hours',
'El País', 2, 'es', false),
(md5('poc-006'), 'Nuevo fármaco contra el Alzheimer obtiene aprobación de la EMA',
'La Agencia Europea del Medicamento ha dado luz verde al primer tratamiento que demuestra ralentizar significativamente el deterioro cognitivo en fases tempranas. El medicamento llegará a las farmacias europeas antes de final de año.',
'https://example.com/farmaco-alzheimer-ema', NOW() - INTERVAL '10 hours',
'BBC Mundo', 7, 'es', false),
(md5('poc-007'), 'México anuncia plan de inversión en energías renovables por 50.000 millones',
'El gobierno mexicano presentó su Estrategia Nacional de Transición Energética que contempla duplicar la capacidad solar y eólica instalada antes de 2030, con fuerte participación de capital privado nacional e internacional.',
'https://example.com/mexico-renovables', NOW() - INTERVAL '12 hours',
'RT Español', 5, 'es', false),
(md5('poc-008'), 'OpenAI lanza modelo multimodal capaz de generar video fotorrealista en tiempo real',
'La empresa californiana presentó Sora 2, capaz de producir secuencias de vídeo de alta definición en menos de 30 segundos. Investigadores advierten sobre los riesgos de desinformación mientras la compañía promete mecanismos de marca de agua.',
'https://example.com/openai-sora2', NOW() - INTERVAL '14 hours',
'El Mundo', 8, 'es', false),
(md5('poc-009'), 'Argentina cierra acuerdo comercial con la Unión Europea tras 25 años de negociaciones',
'El tratado de libre comercio Mercosur-UE entra en vigor de forma provisional tras superar los últimos obstáculos relacionados con protección ambiental y acceso al mercado agrícola europeo para los productos del cono sur.',
'https://example.com/argentina-acuerdo-ue', NOW() - INTERVAL '18 hours',
'BBC Mundo', 4, 'es', false),
(md5('poc-010'), 'Telescopio James Webb detecta atmósfera en exoplaneta a 40 años luz de la Tierra',
'Astrónomos del Instituto de Tecnología de California confirmaron la presencia de dióxido de carbono y vapor de agua en la atmósfera del exoplaneta K2-18b, abriendo nuevas posibilidades en la búsqueda de condiciones habitables fuera del sistema solar.',
'https://example.com/james-webb-exoplaneta', NOW() - INTERVAL '22 hours',
'El País', 1, 'es', false)
ON CONFLICT (id) DO NOTHING;