#!/bin/bash # ============================================================================ # Production Server Stop Script for Task Reporter # Gracefully stops all services: Frontend, Backend, MinIO # ============================================================================ # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Script directory (scripts are in project root) SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$SCRIPT_DIR" # PID file locations PID_DIR="$PROJECT_ROOT/.pids" BACKEND_PID_FILE="$PID_DIR/backend-prod.pid" FRONTEND_PID_FILE="$PID_DIR/frontend-prod.pid" # Helper functions print_header() { echo -e "\n${BLUE}=== $1 ===${NC}" } print_ok() { echo -e "${GREEN}[OK]${NC} $1" } print_error() { echo -e "${RED}[ERROR]${NC} $1" } print_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } print_info() { echo -e "${BLUE}[INFO]${NC} $1" } # Show help show_help() { echo "Usage: $0 [OPTIONS]" echo "" echo "Stops all production services for Task Reporter." echo "" echo "Options:" echo " -h, --help Show this help message" echo " --keep-minio Don't stop MinIO container" echo " --keep-data Alias for --keep-minio" echo "" echo "Services stopped:" echo " - Frontend (static file server)" echo " - Backend (uvicorn workers)" echo " - MinIO (Docker container)" } # Parse arguments KEEP_MINIO=false while [[ $# -gt 0 ]]; do case $1 in -h|--help) show_help exit 0 ;; --keep-minio|--keep-data) KEEP_MINIO=true shift ;; *) echo "Unknown option: $1" show_help exit 1 ;; esac done echo -e "${BLUE}============================================${NC}" echo -e "${BLUE} Task Reporter - Stopping Production${NC}" echo -e "${BLUE}============================================${NC}" # ============================================================================ # Stop Frontend Static Server # ============================================================================ print_header "Stopping Frontend Server" if [[ -f "$FRONTEND_PID_FILE" ]]; then FRONTEND_PID=$(cat "$FRONTEND_PID_FILE") if kill -0 "$FRONTEND_PID" 2>/dev/null; then kill "$FRONTEND_PID" 2>/dev/null || true # Also kill any child processes pkill -P "$FRONTEND_PID" 2>/dev/null || true sleep 1 # Force kill if still running if kill -0 "$FRONTEND_PID" 2>/dev/null; then kill -9 "$FRONTEND_PID" 2>/dev/null || true fi print_ok "Frontend server stopped (was PID: $FRONTEND_PID)" else print_info "Frontend server was not running" fi rm -f "$FRONTEND_PID_FILE" else # Try to find and kill serve or python http.server process SERVE_PID=$(pgrep -f "serve.*dist.*-l" 2>/dev/null | head -1) if [[ -n "$SERVE_PID" ]]; then kill "$SERVE_PID" 2>/dev/null || true print_ok "Frontend server stopped (found PID: $SERVE_PID)" else HTTP_PID=$(pgrep -f "python.*http.server" 2>/dev/null | head -1) if [[ -n "$HTTP_PID" ]]; then kill "$HTTP_PID" 2>/dev/null || true print_ok "Frontend server stopped (found PID: $HTTP_PID)" else print_info "Frontend server was not running" fi fi fi # ============================================================================ # Stop Backend # ============================================================================ print_header "Stopping Backend" if [[ -f "$BACKEND_PID_FILE" ]]; then BACKEND_PID=$(cat "$BACKEND_PID_FILE") if kill -0 "$BACKEND_PID" 2>/dev/null; then # Send SIGTERM first for graceful shutdown kill "$BACKEND_PID" 2>/dev/null || true # Also kill worker processes pkill -P "$BACKEND_PID" 2>/dev/null || true sleep 2 # Force kill if still running if kill -0 "$BACKEND_PID" 2>/dev/null; then kill -9 "$BACKEND_PID" 2>/dev/null || true pkill -9 -P "$BACKEND_PID" 2>/dev/null || true fi print_ok "Backend stopped (was PID: $BACKEND_PID)" else print_info "Backend was not running" fi rm -f "$BACKEND_PID_FILE" else # Try to find and kill uvicorn processes (production mode) UVICORN_PIDS=$(pgrep -f "uvicorn app.main:app" 2>/dev/null) if [[ -n "$UVICORN_PIDS" ]]; then echo "$UVICORN_PIDS" | xargs kill 2>/dev/null || true sleep 1 # Force kill remaining REMAINING=$(pgrep -f "uvicorn app.main:app" 2>/dev/null) if [[ -n "$REMAINING" ]]; then echo "$REMAINING" | xargs kill -9 2>/dev/null || true fi print_ok "Backend stopped (found uvicorn processes)" else print_info "Backend was not running" fi fi # ============================================================================ # Stop MinIO # ============================================================================ if [[ "$KEEP_MINIO" == "false" ]]; then print_header "Stopping MinIO" cd "$PROJECT_ROOT" if docker ps --format '{{.Names}}' 2>/dev/null | grep -q "task-reporter-minio"; then if command -v docker-compose &> /dev/null; then docker-compose -f docker-compose.minio.yml down else docker compose -f docker-compose.minio.yml down fi print_ok "MinIO stopped (data preserved in volume)" else print_info "MinIO was not running" fi else print_header "MinIO" print_info "Keeping MinIO running (--keep-minio)" fi # ============================================================================ # Port Release # ============================================================================ print_header "Port Release" # Function to release port release_port() { local port=$1 local service_name=$2 # Check if port is in use if command -v fuser &> /dev/null; then # Use fuser if available if fuser "$port/tcp" > /dev/null 2>&1; then print_info "Releasing port $port ($service_name)..." fuser -k "$port/tcp" > /dev/null 2>&1 || true sleep 1 if ! fuser "$port/tcp" > /dev/null 2>&1; then print_ok "Port $port released" else print_warn "Port $port may still be in use (TIME_WAIT state)" fi else print_ok "Port $port is free" fi elif command -v lsof &> /dev/null; then # Fallback to lsof local pid=$(lsof -ti ":$port" 2>/dev/null | head -1) if [[ -n "$pid" ]]; then print_info "Releasing port $port ($service_name, PID: $pid)..." kill "$pid" 2>/dev/null || true sleep 1 if ! lsof -ti ":$port" > /dev/null 2>&1; then print_ok "Port $port released" else # Force kill if still in use kill -9 "$pid" 2>/dev/null || true sleep 1 if ! lsof -ti ":$port" > /dev/null 2>&1; then print_ok "Port $port force released" else print_warn "Port $port may still be in use (TIME_WAIT state)" fi fi else print_ok "Port $port is free" fi else # Check with netstat/ss as last resort if ss -tuln 2>/dev/null | grep -q ":$port "; then print_warn "Port $port appears in use but no tool to release it" else print_ok "Port $port is free" fi fi } # Get ports from environment or use defaults BACKEND_PORT="${BACKEND_PORT:-8000}" FRONTEND_PORT="${FRONTEND_PORT:-3000}" # Release application ports release_port "$BACKEND_PORT" "Backend API" release_port "$FRONTEND_PORT" "Frontend" # ============================================================================ # Cleanup # ============================================================================ print_header "Cleanup" # Remove PID files rm -f "$BACKEND_PID_FILE" "$FRONTEND_PID_FILE" 2>/dev/null # Remove PID directory if empty if [[ -d "$PID_DIR" ]] && [[ -z "$(ls -A "$PID_DIR")" ]]; then rmdir "$PID_DIR" print_ok "Cleaned up PID directory" else print_ok "Cleanup complete" fi # ============================================================================ # Summary # ============================================================================ print_header "Summary" echo -e "${GREEN}All production services have been stopped.${NC}" echo "" if [[ "$KEEP_MINIO" == "true" ]]; then echo -e " ${YELLOW}Note:${NC} MinIO is still running (use without --keep-minio to stop)" fi echo -e " ${BLUE}Tip:${NC} Run ./start-prod.sh to start services again" echo ""