#!/bin/bash # RPA Vision V3 - Gestionnaire de Services Centralisé # Supporte deux modes : systemd (défaut) et legacy (PID files) # # Usage: ./svc.sh [start|stop|status|restart|logs|enable|disable|install] [service_name|all] # # Exemples: # ./svc.sh start all # Démarrer tout (via systemd) # ./svc.sh start vwb # Démarrer VWB (backend + frontend) # ./svc.sh stop streaming # Arrêter le streaming server # ./svc.sh status # État de tous les services # ./svc.sh restart dashboard # Redémarrer le dashboard # ./svc.sh logs streaming # Voir les logs du streaming server # ./svc.sh logs streaming -f # Suivre les logs en temps réel # ./svc.sh enable # Activer le démarrage auto au boot # ./svc.sh disable # Désactiver le démarrage auto au boot # ./svc.sh install # Installer/recharger les fichiers systemd # ./svc.sh --legacy start all # Mode legacy (PID files, sans systemd) set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" # Couleurs RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' BOLD='\033[1m' NC='\033[0m' # Répertoires VENV_DIR="$SCRIPT_DIR/.venv" LOG_DIR="$SCRIPT_DIR/logs" PID_DIR="$SCRIPT_DIR/.pids" SYSTEMD_DIR="$HOME/.config/systemd/user" mkdir -p "$LOG_DIR" "$PID_DIR" # Mode : systemd (défaut) ou legacy USE_SYSTEMD=true if [ "${1:-}" = "--legacy" ]; then USE_SYSTEMD=false shift fi # Carte des ports (source de vérité) declare -A PORTS=( [api]=8000 [dashboard]=5001 [vwb-backend]=5002 [monitoring]=5003 [agent-chat]=5004 [streaming]=5005 [worker]=5099 [vwb-frontend]=3002 [session-cleaner]=5006 ) # Mapping nom court -> nom service systemd declare -A SYSTEMD_UNITS=( [dashboard]="rpa-dashboard.service" [vwb-backend]="rpa-vwb-backend.service" [agent-chat]="rpa-agent-chat.service" [streaming]="rpa-streaming.service" [worker]="rpa-worker.service" [vwb-frontend]="rpa-vwb-frontend.service" [session-cleaner]="rpa-session-cleaner.service" ) # Services gérés par systemd (ceux qui ont un .service) SYSTEMD_SERVICES="streaming worker agent-chat dashboard vwb-backend vwb-frontend session-cleaner" # Tous les services connus ALL_SERVICES="api dashboard vwb-backend monitoring agent-chat streaming worker vwb-frontend session-cleaner" declare -A COMMANDS=( [api]="$VENV_DIR/bin/python3 server/api_upload.py" [dashboard]="$VENV_DIR/bin/python3 web_dashboard/app.py" [vwb-backend]="cd $SCRIPT_DIR/visual_workflow_builder/backend && $VENV_DIR/bin/python3 app.py" [monitoring]="$VENV_DIR/bin/python3 monitoring_server.py" [agent-chat]="$VENV_DIR/bin/python3 -m agent_chat.app" [streaming]="$VENV_DIR/bin/python3 -m agent_v0.server_v1.api_stream" [worker]="$VENV_DIR/bin/python3 -m agent_v0.server_v1.run_worker" [vwb-frontend]="cd $SCRIPT_DIR/visual_workflow_builder/frontend_v4 && npm run dev" [session-cleaner]="$VENV_DIR/bin/python3 tools/session_cleaner.py" ) # Groupes de services declare -A SVC_GROUPS=( [vwb]="vwb-backend vwb-frontend" [all]="api dashboard vwb-backend vwb-frontend" [full]="api dashboard vwb-backend vwb-frontend monitoring agent-chat streaming worker session-cleaner" [boot]="streaming worker agent-chat dashboard vwb-backend vwb-frontend session-cleaner" ) # ============================================================================= # Fonctions systemd # ============================================================================= systemd_start() { local name=$1 local unit=${SYSTEMD_UNITS[$name]:-} if [ -z "$unit" ]; then # Pas de service systemd pour ce composant -> fallback legacy echo -e " ${YELLOW}$name${NC}: pas de service systemd, lancement legacy..." legacy_start "$name" return fi echo -n " Démarrage $name... " if systemctl --user start "$unit" 2>/dev/null; then sleep 1 if systemctl --user is-active --quiet "$unit" 2>/dev/null; then echo -e "${GREEN}OK${NC}" else echo -e "${RED}ECHEC${NC}" echo " -> journalctl --user -u $unit --no-pager -n 10" fi else echo -e "${RED}ECHEC${NC}" echo " -> journalctl --user -u $unit --no-pager -n 10" fi } systemd_stop() { local name=$1 local unit=${SYSTEMD_UNITS[$name]:-} if [ -z "$unit" ]; then legacy_stop "$name" return fi echo -n " Arrêt $name... " systemctl --user stop "$unit" 2>/dev/null || true echo -e "${GREEN}OK${NC}" } systemd_restart() { local name=$1 local unit=${SYSTEMD_UNITS[$name]:-} if [ -z "$unit" ]; then legacy_stop "$name" sleep 1 legacy_start "$name" return fi echo -n " Redémarrage $name... " if systemctl --user restart "$unit" 2>/dev/null; then sleep 1 if systemctl --user is-active --quiet "$unit" 2>/dev/null; then echo -e "${GREEN}OK${NC}" else echo -e "${RED}ECHEC${NC}" fi else echo -e "${RED}ECHEC${NC}" fi } systemd_status() { echo "" echo -e "${BOLD}Service Port Status Mode${NC}" echo "─────────────────────────────────────────────────" for name in $ALL_SERVICES; do local port=${PORTS[$name]:-?} local unit=${SYSTEMD_UNITS[$name]:-} if [ -n "$unit" ]; then # Service systemd local state state=$(systemctl --user is-active "$unit" 2>/dev/null) || state="inactive" local mode="systemd" if [ "$state" = "active" ]; then printf " %-18s %-6s ${GREEN}%-14s${NC} %s\n" "$name" "$port" "$state" "$mode" elif [ "$state" = "failed" ]; then printf " %-18s %-6s ${RED}%-14s${NC} %s\n" "$name" "$port" "$state" "$mode" else # Vérifier si le port est quand même occupé (lancement legacy possible) if ss -tlnp 2>/dev/null | grep -q ":${port} "; then printf " %-18s %-6s ${YELLOW}%-14s${NC} %s\n" "$name" "$port" "running (ext)" "port" else printf " %-18s %-6s ${RED}%-14s${NC} %s\n" "$name" "$port" "$state" "$mode" fi fi else # Pas de service systemd -> vérifier PID/port if is_running_legacy "$name"; then printf " %-18s %-6s ${GREEN}%-14s${NC} %s\n" "$name" "$port" "running" "legacy" else printf " %-18s %-6s ${RED}%-14s${NC} %s\n" "$name" "$port" "stopped" "legacy" fi fi done echo "" # Afficher l'état du target local target_state target_state=$(systemctl --user is-active "rpa-vision.target" 2>/dev/null) || target_state="inactive" local target_enabled target_enabled=$(systemctl --user is-enabled "rpa-vision.target" 2>/dev/null) || target_enabled="disabled" echo -e "${BOLD}Target rpa-vision.target:${NC} $target_state (boot: $target_enabled)" echo "" } systemd_logs() { local name=$1 shift local unit=${SYSTEMD_UNITS[$name]:-} if [ -z "$unit" ]; then # Fallback vers fichier log if [ -f "$LOG_DIR/${name}.log" ]; then tail -50 "$LOG_DIR/${name}.log" else echo "Pas de logs pour $name" fi return fi # Passer les arguments restants a journalctl (ex: -f pour follow) journalctl --user -u "$unit" --no-pager -n 50 "$@" } # ============================================================================= # Fonctions legacy (PID files) # ============================================================================= is_running_legacy() { local name=$1 local port=${PORTS[$name]:-} local pid_file="$PID_DIR/${name}.pid" if [ -f "$pid_file" ]; then local pid pid=$(cat "$pid_file") if kill -0 "$pid" 2>/dev/null; then return 0 fi rm -f "$pid_file" fi if [ -n "$port" ] && ss -tlnp 2>/dev/null | grep -q ":${port} "; then return 0 fi return 1 } legacy_start() { local name=$1 local port=${PORTS[$name]:-} local cmd=${COMMANDS[$name]:-} if [ -z "$cmd" ]; then echo -e "${RED}Service inconnu: $name${NC}" return 1 fi if is_running_legacy "$name"; then echo -e " ${YELLOW}$name${NC} deja en cours (port $port)" return 0 fi if [ -n "$port" ] && ss -tlnp 2>/dev/null | grep -q ":${port} "; then echo -e " ${RED}Port $port occupe !${NC} Liberez-le avec: fuser -k ${port}/tcp" return 1 fi echo -n " Démarrage $name (port $port)... " bash -c "$cmd" > "$LOG_DIR/${name}.log" 2>&1 & local pid=$! echo "$pid" > "$PID_DIR/${name}.pid" local max_wait=15 if [ "$name" = "vwb-frontend" ]; then max_wait=60 fi for i in $(seq 1 $max_wait); do if ! kill -0 "$pid" 2>/dev/null; then echo -e "${RED}ECHEC${NC} (process mort)" echo " -> tail $LOG_DIR/${name}.log" tail -5 "$LOG_DIR/${name}.log" 2>/dev/null || true rm -f "$PID_DIR/${name}.pid" return 1 fi if [ -n "$port" ] && curl -s "http://localhost:$port" > /dev/null 2>&1; then echo -e "${GREEN}OK${NC} (PID $pid)" return 0 fi sleep 1 done if kill -0 "$pid" 2>/dev/null; then echo -e "${GREEN}OK${NC} (PID $pid, port non verifie)" return 0 fi echo -e "${RED}TIMEOUT${NC}" return 1 } legacy_stop() { local name=$1 local pid_file="$PID_DIR/${name}.pid" local port=${PORTS[$name]:-} if [ -f "$pid_file" ]; then local pid pid=$(cat "$pid_file") if kill -0 "$pid" 2>/dev/null; then kill "$pid" 2>/dev/null for i in $(seq 1 5); do kill -0 "$pid" 2>/dev/null || break sleep 1 done kill -9 "$pid" 2>/dev/null || true fi rm -f "$pid_file" fi if [ -n "$port" ]; then fuser -k "${port}/tcp" 2>/dev/null || true fi echo -e " ${name}: ${GREEN}arrete${NC}" } # ============================================================================= # Fonctions utilitaires # ============================================================================= resolve_services() { local input=$1 if [ -n "${SVC_GROUPS[$input]:-}" ]; then echo "${SVC_GROUPS[$input]}" else echo "$input" fi } do_install() { echo -e "${CYAN}${BOLD}Installation des services systemd...${NC}" echo "" # Vérifier que les fichiers existent local missing=false for unit in rpa-streaming.service rpa-worker.service rpa-agent-chat.service rpa-dashboard.service rpa-vwb-backend.service rpa-vwb-frontend.service rpa-session-cleaner.service rpa-vision.target; do if [ -f "$SYSTEMD_DIR/$unit" ]; then echo -e " ${GREEN}OK${NC} $unit" else echo -e " ${RED}MANQUANT${NC} $unit" missing=true fi done if [ "$missing" = true ]; then echo "" echo -e "${RED}Fichiers manquants dans $SYSTEMD_DIR${NC}" echo "Les fichiers .service doivent etre dans ~/.config/systemd/user/" return 1 fi echo "" # Recharger systemd echo -n " Rechargement systemd... " systemctl --user daemon-reload echo -e "${GREEN}OK${NC}" # Vérifier le lingering if loginctl show-user "$(whoami)" 2>/dev/null | grep -q "Linger=yes"; then echo -e " Lingering: ${GREEN}actif${NC}" else echo -n " Activation du lingering... " loginctl enable-linger "$(whoami)" 2>/dev/null || { echo -e "${YELLOW}necessaire en root : sudo loginctl enable-linger $(whoami)${NC}" } echo -e "${GREEN}OK${NC}" fi echo "" echo -e "${GREEN}Installation terminee.${NC}" echo " -> ./svc.sh enable # Activer le demarrage au boot" echo " -> ./svc.sh start boot # Démarrer maintenant" } do_enable() { echo -e "${CYAN}${BOLD}Activation du demarrage automatique au boot...${NC}" systemctl --user daemon-reload systemctl --user enable rpa-vision.target for unit in rpa-streaming.service rpa-worker.service rpa-agent-chat.service rpa-dashboard.service rpa-vwb-backend.service rpa-vwb-frontend.service rpa-session-cleaner.service; do systemctl --user enable "$unit" 2>/dev/null echo -e " ${GREEN}OK${NC} $unit" done echo "" echo -e "${GREEN}Les services demarreront automatiquement au boot.${NC}" } do_disable() { echo -e "${YELLOW}${BOLD}Desactivation du demarrage automatique...${NC}" systemctl --user disable rpa-vision.target 2>/dev/null || true for unit in rpa-streaming.service rpa-worker.service rpa-agent-chat.service rpa-dashboard.service rpa-vwb-backend.service rpa-vwb-frontend.service rpa-session-cleaner.service; do systemctl --user disable "$unit" 2>/dev/null || true echo -e " ${GREEN}OK${NC} $unit" done echo "" echo -e "${YELLOW}Les services ne demarreront plus au boot.${NC}" } show_help() { echo -e "${CYAN}${BOLD}RPA Vision V3 - Gestionnaire de Services${NC}" echo "" echo -e "${BOLD}Usage:${NC} $0 [ACTION] [TARGET]" echo "" echo -e "${BOLD}Actions:${NC}" echo " start [svc|group] Demarrer un service ou un groupe" echo " stop [svc|group] Arreter un service ou un groupe" echo " restart [svc|group] Redemarrer un service ou un groupe" echo " status Etat de tous les services" echo " logs [svc] [-f] Voir les logs (ajouter -f pour suivre)" echo " install Installer/recharger les fichiers systemd" echo " enable Activer le demarrage auto au boot" echo " disable Desactiver le demarrage auto au boot" echo "" echo -e "${BOLD}Services:${NC}" echo " streaming Streaming Server HTTP (port 5005)" echo " worker VLM Worker GPU (process séparé)" echo " agent-chat Agent Chat (port 5004)" echo " dashboard Web Dashboard (port 5001)" echo " vwb-backend VWB Backend Flask (port 5002)" echo " vwb-frontend VWB Frontend Vite (port 3002)" echo " session-cleaner Session Cleaner (port 5006)" echo " api API Server (port 8000) [legacy uniquement]" echo " monitoring Monitoring (port 5003) [legacy uniquement]" echo "" echo -e "${BOLD}Groupes:${NC}" echo " boot Services systemd (streaming, worker, chat, dashboard, vwb, session-cleaner)" echo " vwb VWB backend + frontend" echo " all Core (api, dashboard, vwb)" echo " full Tous les services" echo "" echo -e "${BOLD}Options:${NC}" echo " --legacy Forcer le mode legacy (PID files au lieu de systemd)" echo "" echo -e "${BOLD}Exemples:${NC}" echo " $0 start boot # Demarrer les services systemd" echo " $0 stop boot # Arreter les services systemd" echo " $0 restart streaming # Redemarrer le streaming server" echo " $0 logs streaming -f # Suivre les logs du streaming" echo " $0 status # Voir l'etat de tout" echo " $0 install # Installer les services systemd" echo " $0 enable # Activer le boot automatique" echo "" } # ============================================================================= # Main # ============================================================================= ACTION="${1:-status}" TARGET="${2:-}" # Fonctions dispatching dispatch_start() { local svc=$1 if [ "$USE_SYSTEMD" = true ]; then systemd_start "$svc" else legacy_start "$svc" fi } dispatch_stop() { local svc=$1 if [ "$USE_SYSTEMD" = true ]; then systemd_stop "$svc" else legacy_stop "$svc" fi } dispatch_restart() { local svc=$1 if [ "$USE_SYSTEMD" = true ]; then systemd_restart "$svc" else legacy_stop "$svc" sleep 1 legacy_start "$svc" fi } case "$ACTION" in start) if [ -z "$TARGET" ]; then echo "Usage: $0 start [service|boot|all|full|vwb]" exit 1 fi echo -e "${CYAN}${BOLD}Demarrage des services...${NC}" # Activer le venv pour le mode legacy if [ "$USE_SYSTEMD" = false ] && [ -d "$VENV_DIR" ]; then source "$VENV_DIR/bin/activate" fi services=$(resolve_services "$TARGET") for svc in $services; do dispatch_start "$svc" done echo "" echo -e "${GREEN}${BOLD}URLs d'acces :${NC}" for svc in $services; do port=${PORTS[$svc]:-} if [ -n "$port" ]; then echo -e " $svc: ${BLUE}http://localhost:$port${NC}" fi done ;; stop) if [ -z "$TARGET" ]; then TARGET="full" fi echo -e "${YELLOW}${BOLD}Arret des services...${NC}" services=$(resolve_services "$TARGET") for svc in $services; do dispatch_stop "$svc" done ;; restart) if [ -z "$TARGET" ]; then echo "Usage: $0 restart [service|boot|all|full|vwb]" exit 1 fi echo -e "${CYAN}${BOLD}Redemarrage...${NC}" services=$(resolve_services "$TARGET") for svc in $services; do dispatch_restart "$svc" done ;; status) echo -e "${CYAN}${BOLD}RPA Vision V3 - Etat des services${NC}" if [ "$USE_SYSTEMD" = true ]; then systemd_status else # Status legacy echo "" echo -e "${BOLD}Service Port Status${NC}" echo "──────────────────────────────────────" for name in $ALL_SERVICES; do port=${PORTS[$name]:-?} if is_running_legacy "$name"; then pid="" if [ -f "$PID_DIR/${name}.pid" ]; then pid=" (PID $(cat "$PID_DIR/${name}.pid"))" fi printf " %-18s %-6s ${GREEN}running${NC}%s\n" "$name" "$port" "$pid" else printf " %-18s %-6s ${RED}stopped${NC}\n" "$name" "$port" fi done echo "" fi ;; logs) if [ -z "$TARGET" ]; then echo "Usage: $0 logs [service] [-f]" echo "Services: streaming, agent-chat, dashboard, vwb-backend, vwb-frontend" exit 1 fi # Passer les arguments supplementaires (ex: -f) shift 2 2>/dev/null || true systemd_logs "$TARGET" "$@" ;; install) do_install ;; enable) do_enable ;; disable) do_disable ;; -h|--help|help) show_help ;; *) show_help exit 1 ;; esac