#!/bin/bash #Script : nodered.sh #Apps : MRDEVS TOOLS #Description : Inicia y Detiene el servicio de Nodered #Author : Mauro Rosero Pérez #Company Email : mauro@rosero.one #Personal Email : mauro.rosero@gmail.com #Created : 2024/12/01 15:27:00 #Modified : 2025/04/01 18:55:38 #Version : 1.3.1 #============================================================================== # Derechos de Autor [2025] [Mauro Rosero P. ] #============================================================================== # Este programa es software libre: usted puede redistribuirlo y/o modificarlo # bajo los términos de la Licencia Pública Affero General de GNU tal como # lo publica la Free Software Foundation, ya sea la versión 3 de la licencia, # o (a su elección) cualquier versión posterior. # # Este programa se distribuye con la esperanza de que sea útil, # pero SIN NINGUNA GARANTÍA; sin siquiera la garantía implícita de # COMERCIABILIDAD o IDONEIDAD PARA UN PROPÓSITO PARTICULAR. Consulte la # Licencia Pública Affero General de GNU para obtener más detalles. # # Debería haber recibido una copia de la Licencia Pública Affero General # junto con este programa. Si no la recibió, consulte . # Configuración inicial # Usar DEVELOPER_DIR de base.lib SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" BIN_BASE="bin" BIN_LIBS="lib" BIN_MESG="msg" BIN_CFGS="config" # Leer DEVSPATH desde el archivo de configuración o usar "devs" por defecto if [ -f "$SCRIPT_DIR/config/devspath.dat" ]; then DEVSPATH=$(cat "$SCRIPT_DIR/$BIN_CFGS/devspath.dat") else DEVSPATH="devs" fi BIN_HOME="$HOME/$DEVSPATH" BIN_PATH=$BIN_HOME/$BIN_BASE VERSION=$(cat "$BIN_HOME/$BIN_BASE/$BIN_CFGS/version") # CHECK SHELL LANGUAGE BIN_LANG=${LANG:0:2} # Importar bibliotecas necesarias if [ -f "${BIN_HOME}/${BIN_BASE}/${BIN_LIBS}/base.lib" ]; then source "${BIN_HOME}/${BIN_BASE}/${BIN_LIBS}/base.lib" # Cargar mensajes en el idioma del sistema o español por defecto load_messages "${BIN_HOME}/${BIN_BASE}" "${BIN_MESG}" "${BIN_LANG}" "head" # Cargar mensajes específicos de NodeRED load_messages "${BIN_HOME}/${BIN_BASE}" "${BIN_MESG}" "${BIN_LANG}" "developers" title="${head_000} ${head_002} - ${nrmsg_000}" fi # Verificar si es un proyecto de desarrollo y de tipo NodeJS if [ ! -f "$PWD/.protype" ]; then echo "${head_noproject}" exit 1 fi if [ "$(cat "$PWD/.protype")" != "nodejs" ]; then echo "${head_002} ${head_warning}: Este script está diseñado para proyectos NodeJS." read -p "¿Desea continuar de todos modos? (s/N): " CONTINUE if [[ ! "$CONTINUE" =~ ^[Ss]$ ]]; then echo "Operación cancelada." exit 1 fi fi # Configuración de Node-RED NODE_RED_DIR="$PWD" NODE_RED_DATA="$NODE_RED_DIR/node_red" NODE_RED_PORT=${NODE_RED_PORT:-1880} NODE_RED_CMD="npx node-red -u $NODE_RED_DATA -p $NODE_RED_PORT" PID_FILE="$NODE_RED_DIR/nodered.pid" LOG_FILE="$NODE_RED_DIR/nodered.log" CONFIG_FILE="$NODE_RED_DATA/settings.js" # Crear directorio de datos si no existe mkdir -p "$NODE_RED_DATA" 2>/dev/null start() { if [ -f "$PID_FILE" ] && kill -0 "$(cat $PID_FILE)" 2>/dev/null; then printf "${nrmsg_001}\n" "$(cat $PID_FILE)" printf "${nrmsg_002}\n" "$NODE_RED_PORT" exit 1 fi # Verificar si Node.js y npm están instalados if ! command -v node &> /dev/null || ! command -v npm &> /dev/null; then echo "Error: Node.js y npm son requeridos pero no están instalados." echo "Instala Node.js con el comando: bin/npm_install.sh" exit 1 fi # Verificar package.json y corregirlo si está vacío o dañado if [ -f "package.json" ]; then # Verificar si package.json está vacío o solo contiene espacios/saltos de línea if [ ! -s "package.json" ] || [ "$(cat package.json | tr -d '[:space:]')" = "" ]; then echo "ADVERTENCIA: Archivo package.json está vacío. Eliminándolo..." rm -f "package.json" else # Verificar si el archivo es un JSON válido if ! node -e "try { require('./package.json'); process.exit(0); } catch(e) { console.error('JSON inválido:', e.message); process.exit(1); }" 2>/dev/null; then echo "ADVERTENCIA: Archivo package.json contiene JSON inválido. Haciendo copia de seguridad y eliminándolo..." mv "package.json" "package.json.bak.$(date +%Y%m%d%H%M%S)" fi fi fi # Verificar si node-red está disponible localmente if ! npm list -g node-red &> /dev/null && ! npm list node-red &> /dev/null; then echo "Node-RED no está instalado. Instalando localmente..." npm install --save node-red if [ $? -ne 0 ]; then echo "Error al instalar Node-RED. Verifica tu conexión a internet." exit 1 fi fi echo "Iniciando Node-RED en el puerto $NODE_RED_PORT..." mkdir -p "$NODE_RED_DATA" nohup $NODE_RED_CMD > "$LOG_FILE" 2>&1 & echo $! > "$PID_FILE" # Esperar a que Node-RED esté listo echo "Iniciando Node-RED (PID $(cat $PID_FILE))..." echo "Esperando a que el servicio esté disponible..." for i in {1..30}; do if grep -q "Server now running" "$LOG_FILE" 2>/dev/null; then echo "Node-RED iniciado correctamente." echo "Abre http://localhost:$NODE_RED_PORT en tu navegador" return 0 fi sleep 1 echo -n "." done echo "" echo "Node-RED parece estar tardando en iniciar. Verifica $LOG_FILE para más detalles." echo "Si todo está bien, abre http://localhost:$NODE_RED_PORT en tu navegador" } stop() { # Variable para seguir si se detectaron procesos local found_processes=0 echo "Deteniendo Node-RED..." # Matar todos los procesos recursivamente y de forma agresiva kill_all_nodered() { echo "Buscando procesos Node-RED..." # Buscar todos los procesos relacionados con Node-RED local NR_PIDS=$(pgrep -f "node-red" 2>/dev/null || echo "") # Si no se encontraron procesos if [ -z "$NR_PIDS" ]; then return 1 fi # Procesar cada PID encontrado found_processes=1 echo "Terminando procesos Node-RED: $NR_PIDS" # Primera pasada: intento amigable (SIGTERM) for pid in $NR_PIDS; do echo "Enviando SIGTERM al proceso $pid..." kill $pid 2>/dev/null done # Esperar un momento sleep 2 # Segunda pasada: verificar y forzar si es necesario local REMAINING_PIDS=$(pgrep -f "node-red" 2>/dev/null || echo "") if [ -n "$REMAINING_PIDS" ]; then echo "Algunos procesos persisten. Forzando terminación (SIGKILL)..." for pid in $REMAINING_PIDS; do echo "Forzando terminación del proceso $pid..." kill -9 $pid 2>/dev/null done # Esperar un poco más sleep 1 # Verificación final local FINAL_CHECK=$(pgrep -f "node-red" 2>/dev/null || echo "") if [ -n "$FINAL_CHECK" ]; then echo "ADVERTENCIA: Algunos procesos de Node-RED aún persisten: $FINAL_CHECK" echo "Puede ser necesario reiniciar el sistema para liberarlos completamente." return 2 fi fi return 0 } # Verificar primero si el archivo PID existe if [ -f "$PID_FILE" ]; then echo "Usando archivo PID: $PID_FILE" if kill -0 "$(cat $PID_FILE)" 2>/dev/null; then local PID=$(cat "$PID_FILE") echo "PID válido encontrado: $PID" # Enviar señal SIGTERM para terminar amigablemente kill $PID 2>/dev/null found_processes=1 # Esperar brevemente sleep 2 else echo "Archivo PID existe pero el proceso no está activo" fi # Eliminar el archivo PID en cualquier caso rm -f "$PID_FILE" fi # Buscar y matar todos los procesos de Node-RED kill_all_nodered # Usar pkill como último recurso pkill -9 -f "node-red" 2>/dev/null # Verificación final y mensaje if pgrep -f "node-red" > /dev/null; then echo "ERROR: No se pudo detener completamente Node-RED." echo "Verifique manualmente los procesos con: ps aux | grep node-red" return 1 elif [ $found_processes -eq 1 ]; then echo "Node-RED detenido correctamente." return 0 else echo "No se encontraron procesos de Node-RED en ejecución." return 1 fi } status() { # Primera verificación: archivo PID existe y el proceso está en ejecución if [ -f "$PID_FILE" ] && kill -0 "$(cat $PID_FILE)" 2>/dev/null; then echo "Node-RED está en ejecución:" echo " - PID: $(cat $PID_FILE)" echo " - URL: http://localhost:$NODE_RED_PORT" echo " - Directorio de datos: $NODE_RED_DATA" echo " - Archivo de log: $LOG_FILE" # Mostrar tiempo de ejecución si está disponible if command -v ps &>/dev/null; then local start_time=$(ps -p $(cat $PID_FILE) -o lstart= 2>/dev/null) [ -n "$start_time" ] && echo " - Iniciado: $start_time" fi # Verificar si el servicio está realmente respondiendo if command -v curl &>/dev/null; then if curl -s "http://localhost:$NODE_RED_PORT" >/dev/null; then echo " - Estado: Respondiendo" else echo " - Estado: Proceso en ejecución pero no responde HTTP" fi fi return 0 fi # Segunda verificación: buscar procesos de Node-RED sin archivo PID local NR_PIDS=$(pgrep -f "node-red" | tr "\n" " ") if [ -n "$NR_PIDS" ]; then echo "Node-RED está en ejecución, pero sin archivo PID correcto:" for pid in $NR_PIDS; do echo " - PID: $pid" # Mostrar información adicional si está disponible if command -v ps &>/dev/null; then local cmd_line=$(ps -p $pid -o cmd= 2>/dev/null) [ -n "$cmd_line" ] && echo " - Comando: $cmd_line" local start_time=$(ps -p $pid -o lstart= 2>/dev/null) [ -n "$start_time" ] && echo " - Iniciado: $start_time" fi done echo " - URL: http://localhost:$NODE_RED_PORT (probable)" # Verificar si el servicio está realmente respondiendo if command -v curl &>/dev/null; then if curl -s "http://localhost:$NODE_RED_PORT" >/dev/null; then echo " - Estado: Respondiendo" else echo " - Estado: Proceso en ejecución pero no responde HTTP" fi fi echo "NOTA: Ejecute 'stop' y 'start' para regenerar el archivo PID correcto." return 0 fi # Si no se encontró ningún proceso echo "Node-RED no está en ejecución." # Limpiar archivos PID huérfanos si existen [ -f "$PID_FILE" ] && rm -f "$PID_FILE" return 1 } restart() { echo "Reiniciando Node-RED..." # Forzar detención completa stop # Verificación adicional y limpieza for i in {1..3}; do if pgrep -f "node-red" > /dev/null; then echo "Intento adicional $i: Forzando terminación de procesos persistentes..." pkill -9 -f "node-red" 2>/dev/null sleep 2 else break fi done # Comprobar una última vez if pgrep -f "node-red" > /dev/null; then echo "ADVERTENCIA: No fue posible detener completamente Node-RED." echo "Los nuevos procesos podrían comportarse de manera inesperada." fi # Limpiar cualquier archivo temporal o socket que pudiera causar problemas rm -f /tmp/node-red-*.sock 2>/dev/null # Esperar a que el sistema se estabilice sleep 3 # Iniciar Node-RED start } install_deps() { echo "Instalando dependencias de Node-RED..." # Verificar si Node.js y npm están instalados if ! command -v node &> /dev/null || ! command -v npm &> /dev/null; then echo "Error: Node.js y npm son requeridos pero no están instalados." echo "Instala Node.js con el comando: bin/npm_install.sh" exit 1 fi # Verificar y corregir package.json si es necesario if [ -f "package.json" ]; then # Verificar si package.json está vacío o solo contiene espacios/saltos de línea if [ ! -s "package.json" ] || [ "$(cat package.json | tr -d '[:space:]')" = "" ]; then echo "ADVERTENCIA: Archivo package.json está vacío. Eliminándolo para una instalación limpia..." rm -f "package.json" else # Verificar si el archivo es un JSON válido if ! node -e "try { require('./package.json'); process.exit(0); } catch(e) { process.exit(1); }" 2>/dev/null; then echo "ADVERTENCIA: Archivo package.json contiene JSON inválido. Haciendo copia de seguridad..." mv "package.json" "package.json.bak.$(date +%Y%m%d%H%M%S)" fi fi fi # Inicializar package.json si no existe if [ ! -f "package.json" ]; then echo "Inicializando package.json..." npm init -y fi # Instalar o actualizar Node-RED echo "Instalando Node-RED..." npm install --save node-red # Instalar paquetes comunes útiles para IoT echo "Instalando módulos adicionales para Node-RED..." npm install --save node-red-dashboard node-red-node-serialport node-red-contrib-mqtt-broker # Verificar si las instalaciones fueron exitosas if npm list node-red | grep -q "node-red"; then echo "Node-RED instalado correctamente." else echo "ADVERTENCIA: Hubo problemas instalando Node-RED." fi echo "Todas las dependencias instaladas. Use './nodered.sh start' para iniciar el servidor." } logs() { if [ -f "$LOG_FILE" ]; then if command -v tail &>/dev/null; then echo "Mostrando las últimas líneas del log (Ctrl+C para salir):" tail -f "$LOG_FILE" else echo "Contenido del archivo de log:" cat "$LOG_FILE" fi else echo "No hay archivo de log disponible." fi } case "$1" in start) start ;; stop) stop ;; restart) restart ;; status) status ;; install) install_deps ;; logs) logs ;; *) echo "Node-RED Administración" echo "Uso: $0 {start|stop|restart|status|install|logs}" echo "" echo "Comandos:" echo " start - Inicia el servidor Node-RED" echo " stop - Detiene el servidor Node-RED" echo " restart - Reinicia el servidor Node-RED" echo " status - Muestra el estado del servidor" echo " install - Instala/actualiza Node-RED y dependencias comunes" echo " logs - Muestra los logs en tiempo real" ;; esac