feat: Add Chat UX improvements with notifications and @mention support
- Add ActionBar component with expandable toolbar for mobile - Add @mention functionality with autocomplete dropdown - Add browser notification system (push, sound, vibration) - Add NotificationSettings modal for user preferences - Add mention badges on room list cards - Add ReportPreview with Markdown rendering and copy/download - Add message copy functionality with hover actions - Add backend mentions field to messages with Alembic migration - Add lots field to rooms, remove templates - Optimize WebSocket database session handling - Various UX polish (animations, accessibility) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
376
start-prod.sh
Executable file
376
start-prod.sh
Executable file
@@ -0,0 +1,376 @@
|
||||
#!/bin/bash
|
||||
# ============================================================================
|
||||
# Production Server Startup Script for Task Reporter
|
||||
# Starts all services: MinIO, Backend (FastAPI with Gunicorn/Uvicorn workers)
|
||||
# Frontend is served as static files (build first with npm run build)
|
||||
# ============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
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"
|
||||
|
||||
# Log file locations
|
||||
LOG_DIR="$PROJECT_ROOT/logs"
|
||||
BACKEND_LOG="$LOG_DIR/backend-prod.log"
|
||||
FRONTEND_LOG="$LOG_DIR/frontend-prod.log"
|
||||
|
||||
# Default configuration
|
||||
BACKEND_PORT="${BACKEND_PORT:-8000}"
|
||||
FRONTEND_PORT="${FRONTEND_PORT:-3000}"
|
||||
WORKERS="${WORKERS:-4}"
|
||||
|
||||
# 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 "${CYAN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
# Show help
|
||||
show_help() {
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "Starts all production services for Task Reporter."
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -h, --help Show this help message"
|
||||
echo " --skip-build Skip frontend build step"
|
||||
echo " --no-frontend Don't start static file server (use external nginx/caddy)"
|
||||
echo " --no-minio Don't start MinIO (use external storage)"
|
||||
echo " --workers N Number of uvicorn workers (default: 4)"
|
||||
echo " --backend-port N Backend port (default: 8000)"
|
||||
echo " --frontend-port N Frontend static server port (default: 3000)"
|
||||
echo ""
|
||||
echo "Environment variables:"
|
||||
echo " WORKERS Number of uvicorn workers (default: 4)"
|
||||
echo " BACKEND_PORT Backend API port (default: 8000)"
|
||||
echo " FRONTEND_PORT Frontend static server port (default: 3000)"
|
||||
echo ""
|
||||
echo "Services started:"
|
||||
echo " - MinIO (Object Storage) - http://localhost:9000 (API), http://localhost:9001 (Console)"
|
||||
echo " - Backend (FastAPI) - http://localhost:\$BACKEND_PORT"
|
||||
echo " - Frontend (Static) - http://localhost:\$FRONTEND_PORT"
|
||||
echo ""
|
||||
echo "Prerequisites:"
|
||||
echo " - Python virtual environment at ./venv"
|
||||
echo " - Frontend built (npm run build in ./frontend)"
|
||||
echo " - Docker for MinIO"
|
||||
}
|
||||
|
||||
# Cleanup function for graceful shutdown
|
||||
cleanup() {
|
||||
echo -e "\n${YELLOW}Shutting down services...${NC}"
|
||||
"$PROJECT_ROOT/stop-prod.sh"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Trap Ctrl+C
|
||||
trap cleanup SIGINT SIGTERM
|
||||
|
||||
# Parse arguments
|
||||
SKIP_BUILD=false
|
||||
NO_FRONTEND=false
|
||||
NO_MINIO=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
--skip-build)
|
||||
SKIP_BUILD=true
|
||||
shift
|
||||
;;
|
||||
--no-frontend)
|
||||
NO_FRONTEND=true
|
||||
shift
|
||||
;;
|
||||
--no-minio)
|
||||
NO_MINIO=true
|
||||
shift
|
||||
;;
|
||||
--workers)
|
||||
WORKERS="$2"
|
||||
shift 2
|
||||
;;
|
||||
--backend-port)
|
||||
BACKEND_PORT="$2"
|
||||
shift 2
|
||||
;;
|
||||
--frontend-port)
|
||||
FRONTEND_PORT="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo -e "${BLUE}============================================${NC}"
|
||||
echo -e "${BLUE} Task Reporter - Production Server${NC}"
|
||||
echo -e "${BLUE}============================================${NC}"
|
||||
|
||||
# Create directories
|
||||
mkdir -p "$PID_DIR"
|
||||
mkdir -p "$LOG_DIR"
|
||||
|
||||
# ============================================================================
|
||||
# Pre-flight Check
|
||||
# ============================================================================
|
||||
print_header "Pre-flight Check"
|
||||
|
||||
# Check .env file exists
|
||||
if [[ ! -f "$PROJECT_ROOT/.env" ]]; then
|
||||
print_error ".env file not found. Copy from .env.example and configure."
|
||||
exit 1
|
||||
fi
|
||||
print_ok ".env file exists"
|
||||
|
||||
# Check virtual environment
|
||||
if [[ ! -d "$PROJECT_ROOT/venv" ]]; then
|
||||
print_error "Python virtual environment not found at ./venv"
|
||||
exit 1
|
||||
fi
|
||||
print_ok "Python virtual environment found"
|
||||
|
||||
# Check Docker
|
||||
if ! command -v docker &> /dev/null; then
|
||||
print_warn "Docker not found. MinIO will not be started."
|
||||
NO_MINIO=true
|
||||
else
|
||||
print_ok "Docker available"
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# Build Frontend (if needed)
|
||||
# ============================================================================
|
||||
if [[ "$NO_FRONTEND" == "false" ]] && [[ "$SKIP_BUILD" == "false" ]]; then
|
||||
print_header "Building Frontend"
|
||||
|
||||
cd "$PROJECT_ROOT/frontend"
|
||||
|
||||
if [[ ! -d "node_modules" ]]; then
|
||||
print_info "Installing frontend dependencies..."
|
||||
npm install
|
||||
fi
|
||||
|
||||
print_info "Building frontend for production..."
|
||||
npm run build
|
||||
|
||||
if [[ -d "dist" ]]; then
|
||||
print_ok "Frontend built successfully"
|
||||
else
|
||||
print_error "Frontend build failed"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# Start MinIO
|
||||
# ============================================================================
|
||||
if [[ "$NO_MINIO" == "false" ]]; then
|
||||
print_header "Starting MinIO"
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# Check if MinIO container is already running
|
||||
if docker ps --format '{{.Names}}' | grep -q "task-reporter-minio"; then
|
||||
print_ok "MinIO is already running"
|
||||
else
|
||||
# Start MinIO
|
||||
if command -v docker-compose &> /dev/null; then
|
||||
docker-compose -f docker-compose.minio.yml up -d
|
||||
else
|
||||
docker compose -f docker-compose.minio.yml up -d
|
||||
fi
|
||||
|
||||
# Wait for MinIO to be healthy
|
||||
print_info "Waiting for MinIO to be healthy..."
|
||||
MINIO_READY=false
|
||||
for i in {1..30}; do
|
||||
if curl -s http://localhost:9000/minio/health/live > /dev/null 2>&1; then
|
||||
MINIO_READY=true
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
echo -n "."
|
||||
done
|
||||
echo ""
|
||||
|
||||
if [[ "$MINIO_READY" == "true" ]]; then
|
||||
print_ok "MinIO is healthy"
|
||||
else
|
||||
print_error "MinIO failed to start within 30 seconds"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
print_info "MinIO Console: http://localhost:9001"
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# Start Backend (Production Mode)
|
||||
# ============================================================================
|
||||
print_header "Starting Backend (Production)"
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# Check if backend is already running
|
||||
if [[ -f "$BACKEND_PID_FILE" ]] && kill -0 "$(cat "$BACKEND_PID_FILE")" 2>/dev/null; then
|
||||
print_ok "Backend is already running (PID: $(cat "$BACKEND_PID_FILE"))"
|
||||
else
|
||||
print_info "Starting uvicorn with $WORKERS workers..."
|
||||
|
||||
# Source the virtual environment and run uvicorn in production mode
|
||||
(
|
||||
source "$PROJECT_ROOT/venv/bin/activate"
|
||||
cd "$PROJECT_ROOT"
|
||||
# Production mode: no reload, multiple workers
|
||||
uvicorn app.main:app \
|
||||
--host 0.0.0.0 \
|
||||
--port "$BACKEND_PORT" \
|
||||
--workers "$WORKERS" \
|
||||
--log-level info \
|
||||
--access-log \
|
||||
> "$BACKEND_LOG" 2>&1 &
|
||||
echo $! > "$BACKEND_PID_FILE"
|
||||
)
|
||||
|
||||
# Wait for backend to be ready
|
||||
print_info "Waiting for backend to be ready..."
|
||||
BACKEND_READY=false
|
||||
for i in {1..30}; do
|
||||
if curl -s "http://localhost:$BACKEND_PORT/api/health" > /dev/null 2>&1 || \
|
||||
curl -s "http://localhost:$BACKEND_PORT/docs" > /dev/null 2>&1; then
|
||||
BACKEND_READY=true
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
echo -n "."
|
||||
done
|
||||
echo ""
|
||||
|
||||
if [[ "$BACKEND_READY" == "true" ]]; then
|
||||
print_ok "Backend is ready (PID: $(cat "$BACKEND_PID_FILE"))"
|
||||
else
|
||||
print_warn "Backend may still be starting. Check logs: $BACKEND_LOG"
|
||||
fi
|
||||
fi
|
||||
|
||||
print_info "Backend API: http://localhost:$BACKEND_PORT"
|
||||
print_info "API Docs: http://localhost:$BACKEND_PORT/docs"
|
||||
|
||||
# ============================================================================
|
||||
# Start Frontend Static Server
|
||||
# ============================================================================
|
||||
if [[ "$NO_FRONTEND" == "false" ]]; then
|
||||
print_header "Starting Frontend Static Server"
|
||||
|
||||
cd "$PROJECT_ROOT/frontend"
|
||||
|
||||
# Check if dist folder exists
|
||||
if [[ ! -d "dist" ]]; then
|
||||
print_error "Frontend dist folder not found. Run 'npm run build' first or remove --skip-build"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if frontend is already running
|
||||
if [[ -f "$FRONTEND_PID_FILE" ]] && kill -0 "$(cat "$FRONTEND_PID_FILE")" 2>/dev/null; then
|
||||
print_ok "Frontend server is already running (PID: $(cat "$FRONTEND_PID_FILE"))"
|
||||
else
|
||||
# Use Python's http.server as a simple static file server
|
||||
# For production, consider using nginx, caddy, or serve
|
||||
print_info "Starting static file server on port $FRONTEND_PORT..."
|
||||
|
||||
# Check if 'serve' is available (better for SPA)
|
||||
if command -v npx &> /dev/null; then
|
||||
# Use serve for proper SPA support (handles client-side routing)
|
||||
cd "$PROJECT_ROOT/frontend"
|
||||
npx serve -s dist -l "$FRONTEND_PORT" > "$FRONTEND_LOG" 2>&1 &
|
||||
echo $! > "$FRONTEND_PID_FILE"
|
||||
else
|
||||
# Fallback to Python http.server
|
||||
cd "$PROJECT_ROOT/frontend/dist"
|
||||
python3 -m http.server "$FRONTEND_PORT" --bind 0.0.0.0 > "$FRONTEND_LOG" 2>&1 &
|
||||
echo $! > "$FRONTEND_PID_FILE"
|
||||
print_warn "Using Python http.server (SPA routing may not work). Install 'serve' for better support."
|
||||
fi
|
||||
|
||||
# Wait for frontend to be ready
|
||||
sleep 2
|
||||
if kill -0 "$(cat "$FRONTEND_PID_FILE")" 2>/dev/null; then
|
||||
print_ok "Frontend server started (PID: $(cat "$FRONTEND_PID_FILE"))"
|
||||
else
|
||||
print_error "Frontend server failed to start. Check logs: $FRONTEND_LOG"
|
||||
fi
|
||||
fi
|
||||
|
||||
print_info "Frontend: http://localhost:$FRONTEND_PORT"
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# Summary
|
||||
# ============================================================================
|
||||
print_header "Production Services Running"
|
||||
|
||||
echo -e "${GREEN}============================================${NC}"
|
||||
echo -e "${GREEN} All production services are running!${NC}"
|
||||
echo -e "${GREEN}============================================${NC}"
|
||||
echo ""
|
||||
if [[ "$NO_FRONTEND" == "false" ]]; then
|
||||
echo -e " ${CYAN}Frontend:${NC} http://localhost:$FRONTEND_PORT"
|
||||
fi
|
||||
echo -e " ${CYAN}Backend API:${NC} http://localhost:$BACKEND_PORT"
|
||||
echo -e " ${CYAN}API Docs:${NC} http://localhost:$BACKEND_PORT/docs"
|
||||
if [[ "$NO_MINIO" == "false" ]]; then
|
||||
echo -e " ${CYAN}MinIO Console:${NC} http://localhost:9001"
|
||||
fi
|
||||
echo ""
|
||||
echo -e " ${CYAN}Workers:${NC} $WORKERS"
|
||||
echo ""
|
||||
echo -e " ${YELLOW}Logs:${NC}"
|
||||
echo -e " Backend: $BACKEND_LOG"
|
||||
if [[ "$NO_FRONTEND" == "false" ]]; then
|
||||
echo -e " Frontend: $FRONTEND_LOG"
|
||||
fi
|
||||
echo ""
|
||||
echo -e " ${YELLOW}Press Ctrl+C to stop all services${NC}"
|
||||
echo ""
|
||||
|
||||
# Keep script running to catch Ctrl+C
|
||||
while true; do
|
||||
sleep 1
|
||||
done
|
||||
Reference in New Issue
Block a user