Files
OCR/start-prod.sh
egg 86a6633000 feat: consolidate env config and add deployment files
- 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>
2025-12-14 15:02:16 +08:00

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