- Add debug_font_path, demo_docs_dir, e2e_api_base_url to config.py - Fix hardcoded paths in pp_structure_debug.py, create_demo_images.py - Fix hardcoded paths in test files - Update .env.example with new configuration options - Update .gitignore to exclude AI development files (.claude/, openspec/, AGENTS.md, CLAUDE.md) - Add production startup script (start-prod.sh) - Add README.md with project documentation - Add 1panel Docker deployment files (docker-compose.yml, Dockerfiles, nginx.conf) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
376 lines
11 KiB
Bash
Executable File
376 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
|
# Tool_OCR - Production Server Startup Script
|
|
# Usage:
|
|
# ./start-prod.sh Start all services (backend + frontend)
|
|
# ./start-prod.sh backend Start only backend
|
|
# ./start-prod.sh frontend Start only frontend
|
|
# ./start-prod.sh --stop Stop all services
|
|
# ./start-prod.sh --status Show service status
|
|
# ./start-prod.sh --help Show help
|
|
|
|
# Note: We don't use 'set -e' here because stop commands may fail gracefully
|
|
|
|
# Colors
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
RED='\033[0;31m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
# Configuration
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PID_DIR="$SCRIPT_DIR/.pid-prod"
|
|
BACKEND_PID_FILE="$PID_DIR/backend.pid"
|
|
FRONTEND_PID_FILE="$PID_DIR/frontend.pid"
|
|
|
|
# Production defaults (different from development)
|
|
BACKEND_HOST=${BACKEND_HOST:-0.0.0.0}
|
|
BACKEND_PORT=${BACKEND_PORT:-8000}
|
|
FRONTEND_HOST=${FRONTEND_HOST:-0.0.0.0}
|
|
FRONTEND_PORT=${FRONTEND_PORT:-12010}
|
|
|
|
# Production-specific settings
|
|
UVICORN_WORKERS=${UVICORN_WORKERS:-4}
|
|
|
|
# Create PID directory
|
|
mkdir -p "$PID_DIR"
|
|
|
|
# Functions
|
|
print_header() {
|
|
echo ""
|
|
echo -e "${BLUE}================================${NC}"
|
|
echo -e "${BLUE} Tool_OCR Production Server${NC}"
|
|
echo -e "${BLUE}================================${NC}"
|
|
echo ""
|
|
}
|
|
|
|
show_help() {
|
|
echo "Tool_OCR - Production Server Manager"
|
|
echo ""
|
|
echo "Usage: ./start-prod.sh [command]"
|
|
echo ""
|
|
echo "Commands:"
|
|
echo " (none) Start all services (backend + frontend)"
|
|
echo " backend Start only backend service"
|
|
echo " frontend Start only frontend service"
|
|
echo " --stop Stop all running services"
|
|
echo " --status Show status of services"
|
|
echo " --help Show this help message"
|
|
echo ""
|
|
echo "Environment Variables:"
|
|
echo " BACKEND_PORT Backend port (default: 8000)"
|
|
echo " FRONTEND_PORT Frontend port (default: 12010)"
|
|
echo " UVICORN_WORKERS Number of uvicorn workers (default: 4)"
|
|
echo ""
|
|
echo "Examples:"
|
|
echo " ./start-prod.sh # Start everything"
|
|
echo " ./start-prod.sh backend # Start only backend"
|
|
echo " FRONTEND_PORT=3000 ./start-prod.sh # Custom port"
|
|
echo " ./start-prod.sh --stop # Stop all services"
|
|
echo ""
|
|
}
|
|
|
|
check_requirements() {
|
|
local missing=0
|
|
|
|
# Check virtual environment
|
|
if [ ! -d "$SCRIPT_DIR/venv" ]; then
|
|
echo -e "${RED}Error: Python virtual environment not found${NC}"
|
|
echo "Please run: ./setup_dev_env.sh"
|
|
missing=1
|
|
fi
|
|
|
|
# Check node_modules
|
|
if [ ! -d "$SCRIPT_DIR/frontend/node_modules" ]; then
|
|
echo -e "${RED}Error: Frontend dependencies not found${NC}"
|
|
echo "Please run: ./setup_dev_env.sh"
|
|
missing=1
|
|
fi
|
|
|
|
# Check .env (production uses .env, not .env.local)
|
|
if [ ! -f "$SCRIPT_DIR/.env" ]; then
|
|
echo -e "${YELLOW}Warning: .env not found, using defaults${NC}"
|
|
fi
|
|
|
|
return $missing
|
|
}
|
|
|
|
load_env() {
|
|
# Production loads .env only (not .env.local)
|
|
# For production, you should use .env with production settings
|
|
if [ -f "$SCRIPT_DIR/.env" ]; then
|
|
set -a
|
|
# shellcheck disable=SC1090
|
|
source "$SCRIPT_DIR/.env"
|
|
set +a
|
|
fi
|
|
}
|
|
|
|
is_running() {
|
|
local pid_file=$1
|
|
if [ -f "$pid_file" ]; then
|
|
local pid=$(cat "$pid_file")
|
|
if ps -p "$pid" > /dev/null 2>&1; then
|
|
return 0
|
|
else
|
|
# PID file exists but process is not running, clean up
|
|
rm -f "$pid_file"
|
|
fi
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
get_pid() {
|
|
local pid_file=$1
|
|
if [ -f "$pid_file" ]; then
|
|
cat "$pid_file"
|
|
fi
|
|
}
|
|
|
|
start_backend() {
|
|
if is_running "$BACKEND_PID_FILE"; then
|
|
echo -e "${YELLOW}Backend already running (PID: $(get_pid $BACKEND_PID_FILE))${NC}"
|
|
return 0
|
|
fi
|
|
|
|
echo -e "${GREEN}Starting backend server (production mode)...${NC}"
|
|
|
|
# Activate virtual environment
|
|
source "$SCRIPT_DIR/venv/bin/activate"
|
|
|
|
# Load environment variables
|
|
load_env
|
|
|
|
# Start backend in background
|
|
cd "$SCRIPT_DIR/backend"
|
|
|
|
# Create necessary directories
|
|
mkdir -p uploads/{temp,processed,images}
|
|
mkdir -p storage/{markdown,json,exports,results}
|
|
mkdir -p models/paddleocr
|
|
mkdir -p logs
|
|
|
|
# Start uvicorn in production mode (no --reload, with workers)
|
|
nohup uvicorn app.main:app \
|
|
--host "$BACKEND_HOST" \
|
|
--port "$BACKEND_PORT" \
|
|
--workers "$UVICORN_WORKERS" \
|
|
--access-log \
|
|
--log-level info \
|
|
> "$PID_DIR/backend.log" 2>&1 &
|
|
local pid=$!
|
|
echo $pid > "$BACKEND_PID_FILE"
|
|
|
|
cd "$SCRIPT_DIR"
|
|
|
|
# Wait a moment and verify
|
|
sleep 3
|
|
if is_running "$BACKEND_PID_FILE"; then
|
|
echo -e "${GREEN}Backend started (PID: $pid, Workers: $UVICORN_WORKERS)${NC}"
|
|
echo -e " API Docs: http://localhost:$BACKEND_PORT/docs"
|
|
echo -e " Health: http://localhost:$BACKEND_PORT/health"
|
|
else
|
|
echo -e "${RED}Backend failed to start. Check $PID_DIR/backend.log${NC}"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
start_frontend() {
|
|
if is_running "$FRONTEND_PID_FILE"; then
|
|
echo -e "${YELLOW}Frontend already running (PID: $(get_pid $FRONTEND_PID_FILE))${NC}"
|
|
return 0
|
|
fi
|
|
|
|
echo -e "${GREEN}Starting frontend server (production mode)...${NC}"
|
|
|
|
# Load environment variables
|
|
load_env
|
|
|
|
# Load nvm
|
|
export NVM_DIR="$HOME/.nvm"
|
|
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
|
|
|
cd "$SCRIPT_DIR/frontend"
|
|
|
|
# Build frontend if dist doesn't exist or is older than src
|
|
if [ ! -d "dist" ] || [ "$(find src -newer dist -type f 2>/dev/null | head -1)" ]; then
|
|
echo -e "${YELLOW}Building frontend...${NC}"
|
|
npm run build
|
|
fi
|
|
|
|
# Start production preview server
|
|
# Note: For real production, use nginx to serve dist/ folder
|
|
nohup npm run preview -- --host "$FRONTEND_HOST" --port "$FRONTEND_PORT" > "$PID_DIR/frontend.log" 2>&1 &
|
|
local pid=$!
|
|
echo $pid > "$FRONTEND_PID_FILE"
|
|
|
|
cd "$SCRIPT_DIR"
|
|
|
|
# Wait a moment and verify
|
|
sleep 3
|
|
if is_running "$FRONTEND_PID_FILE"; then
|
|
echo -e "${GREEN}Frontend started (PID: $pid)${NC}"
|
|
echo -e " URL: http://localhost:$FRONTEND_PORT"
|
|
else
|
|
echo -e "${RED}Frontend failed to start. Check $PID_DIR/frontend.log${NC}"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
kill_process_tree() {
|
|
local pid=$1
|
|
# Kill all child processes first
|
|
pkill -TERM -P "$pid" 2>/dev/null || true
|
|
# Then kill the parent
|
|
kill -TERM "$pid" 2>/dev/null || true
|
|
}
|
|
|
|
force_kill_process_tree() {
|
|
local pid=$1
|
|
# Force kill all child processes
|
|
pkill -9 -P "$pid" 2>/dev/null || true
|
|
# Force kill the parent
|
|
kill -9 "$pid" 2>/dev/null || true
|
|
}
|
|
|
|
kill_by_port() {
|
|
local port=$1
|
|
local pids=$(lsof -ti :$port 2>/dev/null)
|
|
if [ -n "$pids" ]; then
|
|
echo "$pids" | xargs kill -TERM 2>/dev/null || true
|
|
sleep 1
|
|
# Force kill if still running
|
|
pids=$(lsof -ti :$port 2>/dev/null)
|
|
if [ -n "$pids" ]; then
|
|
echo "$pids" | xargs kill -9 2>/dev/null || true
|
|
fi
|
|
fi
|
|
}
|
|
|
|
stop_service() {
|
|
local name=$1
|
|
local pid_file=$2
|
|
local port=$3
|
|
|
|
if is_running "$pid_file"; then
|
|
local pid=$(get_pid "$pid_file")
|
|
echo -e "${YELLOW}Stopping $name (PID: $pid)...${NC}"
|
|
|
|
# Kill the entire process tree
|
|
kill_process_tree "$pid"
|
|
|
|
# Wait up to 5 seconds
|
|
local count=0
|
|
while [ $count -lt 5 ] && is_running "$pid_file"; do
|
|
sleep 1
|
|
count=$((count + 1))
|
|
done
|
|
|
|
# Force kill if still running
|
|
if is_running "$pid_file"; then
|
|
force_kill_process_tree "$pid"
|
|
fi
|
|
|
|
rm -f "$pid_file"
|
|
fi
|
|
|
|
# Also kill any orphaned processes by port (fallback)
|
|
if [ -n "$port" ]; then
|
|
local port_pids=$(lsof -ti :$port 2>/dev/null)
|
|
if [ -n "$port_pids" ]; then
|
|
echo -e "${YELLOW}Cleaning up orphaned processes on port $port...${NC}"
|
|
kill_by_port "$port"
|
|
fi
|
|
fi
|
|
|
|
echo -e "${GREEN}$name stopped${NC}"
|
|
}
|
|
|
|
stop_all() {
|
|
echo -e "${YELLOW}Stopping all production services...${NC}"
|
|
stop_service "Backend" "$BACKEND_PID_FILE" "$BACKEND_PORT"
|
|
stop_service "Frontend" "$FRONTEND_PID_FILE" "$FRONTEND_PORT"
|
|
echo -e "${GREEN}All services stopped${NC}"
|
|
}
|
|
|
|
show_status() {
|
|
echo -e "${BLUE}Production Service Status:${NC}"
|
|
echo ""
|
|
|
|
if is_running "$BACKEND_PID_FILE"; then
|
|
local pid=$(get_pid "$BACKEND_PID_FILE")
|
|
echo -e " Backend: ${GREEN}Running${NC} (PID: $pid, Workers: $UVICORN_WORKERS)"
|
|
echo -e " http://localhost:$BACKEND_PORT"
|
|
else
|
|
echo -e " Backend: ${RED}Stopped${NC}"
|
|
fi
|
|
|
|
if is_running "$FRONTEND_PID_FILE"; then
|
|
local pid=$(get_pid "$FRONTEND_PID_FILE")
|
|
echo -e " Frontend: ${GREEN}Running${NC} (PID: $pid)"
|
|
echo -e " http://localhost:$FRONTEND_PORT"
|
|
else
|
|
echo -e " Frontend: ${RED}Stopped${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
}
|
|
|
|
# Main
|
|
case "${1:-all}" in
|
|
--help|-h)
|
|
show_help
|
|
;;
|
|
--stop)
|
|
stop_all
|
|
;;
|
|
--status)
|
|
show_status
|
|
;;
|
|
backend)
|
|
print_header
|
|
check_requirements || exit 1
|
|
start_backend
|
|
echo ""
|
|
echo -e "${YELLOW}Logs: tail -f $PID_DIR/backend.log${NC}"
|
|
;;
|
|
frontend)
|
|
print_header
|
|
check_requirements || exit 1
|
|
start_frontend
|
|
echo ""
|
|
echo -e "${YELLOW}Logs: tail -f $PID_DIR/frontend.log${NC}"
|
|
;;
|
|
all|"")
|
|
print_header
|
|
check_requirements || exit 1
|
|
start_backend
|
|
start_frontend
|
|
echo ""
|
|
echo -e "${GREEN}================================${NC}"
|
|
echo -e "${GREEN}All production services started!${NC}"
|
|
echo -e "${GREEN}================================${NC}"
|
|
echo ""
|
|
echo "Access the application:"
|
|
echo -e " Frontend: ${BLUE}http://localhost:$FRONTEND_PORT${NC}"
|
|
echo -e " API Docs: ${BLUE}http://localhost:$BACKEND_PORT/docs${NC}"
|
|
echo ""
|
|
echo -e "${YELLOW}Use ./start-prod.sh --stop to stop all services${NC}"
|
|
echo -e "${YELLOW}Use ./start-prod.sh --status to check status${NC}"
|
|
echo ""
|
|
echo "Logs:"
|
|
echo " Backend: tail -f $PID_DIR/backend.log"
|
|
echo " Frontend: tail -f $PID_DIR/frontend.log"
|
|
echo ""
|
|
echo -e "${YELLOW}Note: For real production deployment, consider using:${NC}"
|
|
echo " - nginx as reverse proxy"
|
|
echo " - systemd for service management"
|
|
echo " - Docker for containerization"
|
|
;;
|
|
*)
|
|
echo -e "${RED}Unknown command: $1${NC}"
|
|
show_help
|
|
exit 1
|
|
;;
|
|
esac
|