diff --git a/.env b/.env index deafdfb..563ed95 100644 --- a/.env +++ b/.env @@ -1,49 +1,116 @@ -# Tool_OCR - Production/Docker Environment Configuration -# For local development, copy .env.example to .env.local and configure there -# -# This file is for Docker deployment or production use. -# Sensitive values should be set via environment variables or secrets management. +# Tool_OCR - Environment Configuration Template +# Copy this file to .env and fill in your actual values # ===== Database Configuration ===== -# Set these via Docker secrets or environment variables in production -MYSQL_HOST=${MYSQL_HOST:-localhost} -MYSQL_PORT=${MYSQL_PORT:-3306} -MYSQL_USER=${MYSQL_USER:-} -MYSQL_PASSWORD=${MYSQL_PASSWORD:-} -MYSQL_DATABASE=${MYSQL_DATABASE:-} +MYSQL_HOST=mysql.theaken.com +MYSQL_PORT=33306 +MYSQL_USER=A060 +MYSQL_PASSWORD=WLeSCi0yhtc7 +MYSQL_DATABASE=db_A060 # ===== Application Configuration ===== -# Production port (different from development) -FRONTEND_PORT=12010 +# Server ports BACKEND_PORT=8000 +FRONTEND_PORT=5173 -# Security - MUST be set via environment variable in production -SECRET_KEY=${SECRET_KEY:-change-this-in-production} +# Security +SECRET_KEY=your-secret-key-here-please-change-this-to-random-string ALGORITHM=HS256 ACCESS_TOKEN_EXPIRE_MINUTES=1440 # ===== External Authentication Configuration ===== -EXTERNAL_AUTH_API_URL=${EXTERNAL_AUTH_API_URL:-https://your-auth-api.example.com} +# External authentication API URL +EXTERNAL_AUTH_API_URL=https://pj-auth-api.vercel.app +# Authentication endpoint path EXTERNAL_AUTH_ENDPOINT=/api/auth/login +# API request timeout in seconds EXTERNAL_AUTH_TIMEOUT=30 +# Token refresh buffer in seconds (refresh tokens X seconds before expiry) +TOKEN_REFRESH_BUFFER=300 + +# ===== Task Management Configuration ===== +# Database table prefix for clear namespace separation +DATABASE_TABLE_PREFIX=tool_ocr_ +# Enable task history feature +ENABLE_TASK_HISTORY=true +# Auto-delete old tasks after X days (0 to disable) +TASK_RETENTION_DAYS=30 +# Maximum tasks per user (0 for unlimited) +MAX_TASKS_PER_USER=1000 # ===== OCR Configuration ===== +# Note: PaddleOCR/PaddleX models are stored in ~/.paddleocr/ and ~/.paddlex/ by default +# Supported languages (comma-separated) OCR_LANGUAGES=ch,en,japan,korean +# Default confidence threshold OCR_CONFIDENCE_THRESHOLD=0.5 +# Maximum concurrent OCR workers MAX_OCR_WORKERS=4 -# ===== File Configuration ===== +# GPU Acceleration Configuration +# Force CPU mode even if GPU is available (default: false) +FORCE_CPU_MODE=false +# GPU memory fraction (0.0-1.0, default: 0.8) +GPU_MEMORY_FRACTION=0.8 +# GPU device ID to use (default: 0, -1 for CPU) +GPU_DEVICE_ID=0 + +# ===== File Upload Configuration ===== +# Maximum file size in bytes (50MB default) MAX_UPLOAD_SIZE=52428800 +# Allowed file extensions (comma-separated) ALLOWED_EXTENSIONS=png,jpg,jpeg,pdf,bmp,tiff,doc,docx,ppt,pptx +# Upload directories +UPLOAD_DIR=./uploads +TEMP_DIR=./uploads/temp +PROCESSED_DIR=./uploads/processed +IMAGES_DIR=./uploads/images + +# ===== Export Configuration ===== +# Storage directories +STORAGE_DIR=./storage +MARKDOWN_DIR=./storage/markdown +JSON_DIR=./storage/json +EXPORTS_DIR=./storage/exports + +# ===== PDF Generation Configuration ===== +# Pandoc path (Linux) +PANDOC_PATH=/usr/bin/pandoc +# WeasyPrint font directory +FONT_DIR=/usr/share/fonts +# Default PDF page size +PDF_PAGE_SIZE=A4 +# Default PDF margins (mm) +PDF_MARGIN_TOP=20 +PDF_MARGIN_BOTTOM=20 +PDF_MARGIN_LEFT=20 +PDF_MARGIN_RIGHT=20 # ===== Translation Configuration (DIFY API) ===== -ENABLE_TRANSLATION=${ENABLE_TRANSLATION:-false} -DIFY_BASE_URL=${DIFY_BASE_URL:-} -DIFY_API_KEY=${DIFY_API_KEY:-} +# Enable translation feature +ENABLE_TRANSLATION=true +# DIFY API base URL +DIFY_BASE_URL=https://dify.theaken.com/v1 +# DIFY API key (required for translation) +DIFY_API_KEY=app-YOPrF2ro5fshzMkCZviIuUJd +# API request timeout in seconds DIFY_TIMEOUT=120.0 +# Maximum retry attempts +DIFY_MAX_RETRIES=3 +# Batch translation limits +DIFY_MAX_BATCH_CHARS=5000 +DIFY_MAX_BATCH_ITEMS=20 + +# ===== Background Tasks Configuration ===== +# Task queue type: memory (default) or redis (future) +TASK_QUEUE_TYPE=memory +# Redis URL (if using redis) +# REDIS_URL=redis://localhost:6379/0 # ===== CORS Configuration ===== -CORS_ORIGINS=http://localhost:12010,http://127.0.0.1:12010 +# Allowed origins (comma-separated, * for all) +CORS_ORIGINS=http://localhost:5173,http://127.0.0.1:5173 # ===== Logging Configuration ===== -LOG_LEVEL=INFO +LOG_LEVEL=DEBUG +LOG_FILE=./logs/app.log diff --git a/.env.example b/.env.example index b198128..9fe3922 100644 --- a/.env.example +++ b/.env.example @@ -1,69 +1,35 @@ -# Tool_OCR - Environment Configuration Template -# Copy this file to .env.local and fill in your actual values -# -# Note: Most path configurations have sensible defaults in config.py -# Only override if you need custom paths +# Tool_OCR - 環境變數配置 +# 複製此檔案為 .env 並填入實際值 +# cp .env.example .env -# ===== Database Configuration (Required) ===== +# ===== 資料庫 ===== MYSQL_HOST=your-mysql-host MYSQL_PORT=3306 MYSQL_USER=your-username MYSQL_PASSWORD=your-password MYSQL_DATABASE=your-database -# ===== Application Configuration ===== -# Server ports -BACKEND_HOST=0.0.0.0 +# ===== 服務端口 ===== +# IT 會指派實際端口 BACKEND_PORT=8000 -FRONTEND_HOST=0.0.0.0 FRONTEND_PORT=5173 -# Security (generate a random string for production: openssl rand -hex 32) -SECRET_KEY=your-secret-key-here-please-change-this-to-random-string -ALGORITHM=HS256 -ACCESS_TOKEN_EXPIRE_MINUTES=1440 +# ===== 安全性 ===== +# 生產環境請使用: openssl rand -hex 32 +SECRET_KEY=your-secret-key-change-this -# ===== External Authentication Configuration (Required) ===== +# ===== 外部認證 ===== EXTERNAL_AUTH_API_URL=https://your-auth-api.example.com EXTERNAL_AUTH_ENDPOINT=/api/auth/login -EXTERNAL_AUTH_TIMEOUT=30 -TOKEN_REFRESH_BUFFER=300 -# ===== Task Management Configuration ===== -DATABASE_TABLE_PREFIX=tool_ocr_ -ENABLE_TASK_HISTORY=true -TASK_RETENTION_DAYS=30 -MAX_TASKS_PER_USER=1000 - -# ===== OCR Configuration ===== -# Note: PaddleOCR/PaddleX models are stored in ~/.paddleocr/ and ~/.paddlex/ by default -OCR_LANGUAGES=ch,en,japan,korean -OCR_CONFIDENCE_THRESHOLD=0.5 -MAX_OCR_WORKERS=4 - -# GPU Acceleration Configuration -FORCE_CPU_MODE=false -GPU_MEMORY_FRACTION=0.8 -GPU_DEVICE_ID=0 - -# ===== File Upload Configuration ===== -MAX_UPLOAD_SIZE=52428800 -ALLOWED_EXTENSIONS=png,jpg,jpeg,pdf,bmp,tiff,doc,docx,ppt,pptx -# Path defaults to backend/uploads - only override if needed -# UPLOAD_DIR=./uploads - -# ===== Translation Configuration (DIFY API) ===== +# ===== 翻譯 API (DIFY) ===== ENABLE_TRANSLATION=true DIFY_BASE_URL=https://your-dify-instance.example.com/v1 DIFY_API_KEY=your-dify-api-key -DIFY_TIMEOUT=120.0 -DIFY_MAX_RETRIES=3 -DIFY_MAX_BATCH_CHARS=5000 -DIFY_MAX_BATCH_ITEMS=20 -# ===== CORS Configuration ===== -CORS_ORIGINS=http://localhost:5173,http://127.0.0.1:5173 +# ===== CORS ===== +# 根據實際部署的前端 URL 設定 +CORS_ORIGINS=http://localhost:5173 -# ===== Logging Configuration ===== +# ===== 日誌 ===== LOG_LEVEL=INFO -# LOG_FILE defaults to backend/logs/app.log diff --git a/.gitignore b/.gitignore index 5b27e74..34c7834 100644 --- a/.gitignore +++ b/.gitignore @@ -49,11 +49,9 @@ AGENTS.md CLAUDE.md # ===== Environment Variables ===== -# Local environment files (contain secrets, never commit) -.env.local -.env.*.local -frontend/.env.local -frontend/.env.*.local +# 實際配置檔案包含敏感資訊,不追蹤 +.env +frontend/.env # ===== Process ID Files ===== .pid/ diff --git a/DEPLOY.md b/DEPLOY.md new file mode 100644 index 0000000..68b5dd2 --- /dev/null +++ b/DEPLOY.md @@ -0,0 +1,163 @@ +# Tool_OCR 佈署指南 + +本指南說明如何在 1panel 或其他 Linux 環境中佈署 Tool_OCR。 + +## 快速佈署 + +```bash +# 1. 複製並配置環境變數 +cp .env.example .env +# 編輯 .env 填入實際的資料庫、API 金鑰等配置 + +# 2. 安裝依賴並初始化 +./setup_dev_env.sh + +# 3. 啟動服務 +./start.sh --prod +``` + +## 詳細步驟 + +### 1. 系統需求 + +- Ubuntu 20.04+ 或相容的 Linux 發行版 +- Python 3.10+ +- Node.js 18+ (透過 nvm 自動安裝) +- 至少 4GB RAM +- 至少 10GB 磁碟空間 + +### 2. 環境配置 + +複製範例配置檔: +```bash +cp .env.example .env +``` + +編輯 `.env` 填入實際值: +```bash +# 資料庫 (必填) +MYSQL_HOST=your-mysql-host +MYSQL_PORT=3306 +MYSQL_USER=your-username +MYSQL_PASSWORD=your-password +MYSQL_DATABASE=your-database + +# 服務端口 (由 IT 指派) +BACKEND_PORT=8000 +FRONTEND_PORT=5173 + +# 安全性 (必填,請使用隨機字串) +SECRET_KEY=your-random-secret-key + +# 外部認證 (必填) +EXTERNAL_AUTH_API_URL=https://your-auth-api.example.com + +# 翻譯 API (選填) +ENABLE_TRANSLATION=true +DIFY_BASE_URL=https://your-dify-instance.example.com/v1 +DIFY_API_KEY=your-dify-api-key + +# CORS (根據前端 URL 設定) +CORS_ORIGINS=http://localhost:5173 +``` + +### 3. 安裝依賴 + +執行設置腳本: +```bash +./setup_dev_env.sh +``` + +此腳本會: +- 安裝系統依賴 (pandoc, fonts, opencv 等) +- 安裝 Node.js (透過 nvm) +- 建立 Python 虛擬環境 +- 自動偵測 GPU 並安裝對應的 PaddlePaddle 版本 +- 安裝前端依賴 +- 初始化資料庫 + +### 4. 啟動服務 + +**開發模式** (hot-reload): +```bash +./start.sh +``` + +**正式模式** (無 hot-reload,多 worker): +```bash +./start.sh --prod +``` + +**自訂端口**: +```bash +BACKEND_PORT=8088 FRONTEND_PORT=3000 ./start.sh --prod +``` + +### 5. 服務管理 + +```bash +# 查看狀態 +./start.sh --status + +# 停止服務 +./start.sh --stop + +# 僅啟動後端 +./start.sh backend + +# 僅啟動前端 +./start.sh frontend +``` + +### 6. 日誌 + +```bash +# 後端日誌 +tail -f .pid/backend.log + +# 前端日誌 +tail -f .pid/frontend.log +``` + +## 1panel 設定 + +在 1panel 中設定為「應用」或「腳本任務」: + +1. 工作目錄設為專案根目錄 +2. 啟動指令:`./start.sh --prod` +3. 停止指令:`./start.sh --stop` +4. 環境變數:可在 `.env` 中配置,或透過 1panel 的環境變數功能設定端口 + +## 常見問題 + +### GPU 支援 + +如果有 NVIDIA GPU,`setup_dev_env.sh` 會自動偵測並安裝 GPU 版本的 PaddlePaddle。 + +強制使用 CPU 版本: +```bash +./setup_dev_env.sh --cpu-only +``` + +### 資料庫連線失敗 + +1. 確認 `.env` 中的資料庫配置正確 +2. 手動執行資料庫遷移: + ```bash + source venv/bin/activate + cd backend + alembic upgrade head + ``` + +### 端口被佔用 + +檢查並終止佔用端口的程序: +```bash +lsof -i :8000 # 檢查後端端口 +lsof -i :5173 # 檢查前端端口 +``` + +或使用不同端口: +```bash +BACKEND_PORT=8088 FRONTEND_PORT=3000 ./start.sh --prod +``` diff --git a/deploy/1panel/.env.example b/deploy/1panel/.env.example deleted file mode 100644 index f22f25a..0000000 --- a/deploy/1panel/.env.example +++ /dev/null @@ -1,24 +0,0 @@ -# Tool_OCR - 1Panel Docker Deployment Configuration -# Copy this file to .env and fill in your values - -# ===== Service Ports ===== -BACKEND_PORT=8000 -FRONTEND_PORT=12010 - -# ===== Database Configuration ===== -MYSQL_HOST=your-mysql-host -MYSQL_PORT=3306 -MYSQL_USER=your-username -MYSQL_PASSWORD=your-password -MYSQL_DATABASE=your-database - -# ===== Security ===== -# Generate a random string for production: openssl rand -hex 32 -SECRET_KEY=your-secret-key-change-this - -# ===== Translation API (DIFY) ===== -DIFY_BASE_URL=https://your-dify-instance.example.com/v1 -DIFY_API_KEY=your-dify-api-key - -# ===== Logging ===== -LOG_LEVEL=INFO diff --git a/deploy/1panel/Dockerfile.backend b/deploy/1panel/Dockerfile.backend deleted file mode 100644 index 108e485..0000000 --- a/deploy/1panel/Dockerfile.backend +++ /dev/null @@ -1,67 +0,0 @@ -# Tool_OCR Backend Dockerfile -# Multi-stage build for optimized image size - -# Stage 1: Build environment -FROM python:3.12-slim as builder - -WORKDIR /build - -# Install build dependencies -RUN apt-get update && apt-get install -y --no-install-recommends \ - build-essential \ - git \ - && rm -rf /var/lib/apt/lists/* - -# Copy requirements and install dependencies -COPY backend/requirements.txt . -RUN pip install --no-cache-dir --user -r requirements.txt - -# Stage 2: Runtime environment -FROM python:3.12-slim - -WORKDIR /app - -# Install runtime dependencies -RUN apt-get update && apt-get install -y --no-install-recommends \ - libgl1-mesa-glx \ - libglib2.0-0 \ - libsm6 \ - libxext6 \ - libxrender1 \ - libgomp1 \ - pandoc \ - poppler-utils \ - fonts-dejavu-core \ - fonts-noto-cjk \ - curl \ - && rm -rf /var/lib/apt/lists/* - -# Copy Python packages from builder -COPY --from=builder /root/.local /root/.local -ENV PATH=/root/.local/bin:$PATH - -# Copy application code -COPY backend/ /app/backend/ - -# Create necessary directories -RUN mkdir -p /app/backend/uploads/{temp,processed,images} \ - && mkdir -p /app/backend/storage/{markdown,json,exports,results} \ - && mkdir -p /app/backend/models/paddleocr \ - && mkdir -p /app/backend/logs - -# Set working directory -WORKDIR /app/backend - -# Environment variables -ENV PYTHONUNBUFFERED=1 -ENV PYTHONDONTWRITEBYTECODE=1 - -# Expose port -EXPOSE 8000 - -# Health check -HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ - CMD curl -f http://localhost:8000/health || exit 1 - -# Run application -CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"] diff --git a/deploy/1panel/Dockerfile.frontend b/deploy/1panel/Dockerfile.frontend deleted file mode 100644 index a05418f..0000000 --- a/deploy/1panel/Dockerfile.frontend +++ /dev/null @@ -1,38 +0,0 @@ -# Tool_OCR Frontend Dockerfile -# Multi-stage build for optimized image size - -# Stage 1: Build -FROM node:20-alpine as builder - -WORKDIR /app - -# Copy package files -COPY frontend/package*.json ./ - -# Install dependencies -RUN npm ci - -# Copy source code -COPY frontend/ ./ - -# Build application -RUN npm run build - -# Stage 2: Production -FROM nginx:alpine - -# Copy built files -COPY --from=builder /app/dist /usr/share/nginx/html - -# Copy nginx configuration -COPY deploy/1panel/nginx.conf /etc/nginx/conf.d/default.conf - -# Expose port -EXPOSE 80 - -# Health check -HEALTHCHECK --interval=30s --timeout=10s --retries=3 \ - CMD curl -f http://localhost:80 || exit 1 - -# Run nginx -CMD ["nginx", "-g", "daemon off;"] diff --git a/deploy/1panel/README.md b/deploy/1panel/README.md deleted file mode 100644 index ef834e3..0000000 --- a/deploy/1panel/README.md +++ /dev/null @@ -1,125 +0,0 @@ -# Tool_OCR - 1Panel Docker 佈署指南 - -本目錄包含 1Panel 平台的 Docker 佈署配置。 - -## 前置需求 - -- Docker 20.10+ -- Docker Compose 2.0+ -- NVIDIA Container Toolkit(GPU 加速) -- MySQL 8.0+(外部資料庫) - -## 佈署步驟 - -### 1. 配置環境變數 - -```bash -# 複製環境變數範本 -cp .env.example .env - -# 編輯配置 -nano .env -``` - -必要配置項: -- `MYSQL_*` - 資料庫連線設定 -- `SECRET_KEY` - JWT 簽名金鑰(使用 `openssl rand -hex 32` 生成) -- `DIFY_*` - 翻譯 API 設定 - -### 2. 建置映像 - -```bash -# 建置所有服務 -docker-compose build - -# 或分別建置 -docker-compose build backend -docker-compose build frontend -``` - -### 3. 啟動服務 - -```bash -# 啟動所有服務 -docker-compose up -d - -# 查看日誌 -docker-compose logs -f - -# 查看狀態 -docker-compose ps -``` - -### 4. 存取服務 - -- 前端介面:http://your-server:12010 -- API 文件:http://your-server:8000/docs -- 健康檢查:http://your-server:8000/health - -## 服務管理 - -```bash -# 停止服務 -docker-compose down - -# 重啟服務 -docker-compose restart - -# 更新服務 -docker-compose pull -docker-compose up -d --build - -# 查看資源使用 -docker stats -``` - -## 資料持久化 - -以下 Docker volumes 會自動建立: - -| Volume | 用途 | -|--------|------| -| `tool_ocr_uploads` | 上傳檔案 | -| `tool_ocr_storage` | 處理結果 | -| `tool_ocr_logs` | 應用日誌 | -| `tool_ocr_models` | OCR 模型快取 | - -## GPU 加速 - -預設配置會使用 NVIDIA GPU。如果不需要 GPU 加速,請修改 `docker-compose.yml`,移除 `deploy.resources.reservations` 區塊。 - -## 常見問題 - -### Q: 服務啟動失敗? - -檢查日誌: -```bash -docker-compose logs backend -docker-compose logs frontend -``` - -### Q: 資料庫連線失敗? - -確認: -1. MySQL 服務正在運行 -2. 防火牆允許連接 -3. 使用者權限正確 - -### Q: GPU 不可用? - -確認 NVIDIA Container Toolkit 已安裝: -```bash -nvidia-smi -docker run --rm --gpus all nvidia/cuda:11.8.0-base-ubuntu22.04 nvidia-smi -``` - -## 1Panel 整合 - -在 1Panel 中: - -1. 進入「應用商店」→「自訂應用」 -2. 上傳此目錄的所有檔案 -3. 配置環境變數 -4. 點擊「安裝」 - -或使用 1Panel 的「容器」功能直接導入 `docker-compose.yml`。 diff --git a/deploy/1panel/docker-compose.yml b/deploy/1panel/docker-compose.yml deleted file mode 100644 index 7561582..0000000 --- a/deploy/1panel/docker-compose.yml +++ /dev/null @@ -1,68 +0,0 @@ -version: '3.8' - -services: - backend: - build: - context: ../.. - dockerfile: deploy/1panel/Dockerfile.backend - container_name: tool_ocr_backend - restart: unless-stopped - ports: - - "${BACKEND_PORT:-8000}:8000" - environment: - - MYSQL_HOST=${MYSQL_HOST} - - MYSQL_PORT=${MYSQL_PORT} - - MYSQL_USER=${MYSQL_USER} - - MYSQL_PASSWORD=${MYSQL_PASSWORD} - - MYSQL_DATABASE=${MYSQL_DATABASE} - - SECRET_KEY=${SECRET_KEY} - - DIFY_BASE_URL=${DIFY_BASE_URL} - - DIFY_API_KEY=${DIFY_API_KEY} - - CORS_ORIGINS=http://localhost:${FRONTEND_PORT:-12010} - - LOG_LEVEL=${LOG_LEVEL:-INFO} - volumes: - - tool_ocr_uploads:/app/backend/uploads - - tool_ocr_storage:/app/backend/storage - - tool_ocr_logs:/app/backend/logs - - tool_ocr_models:/app/backend/models - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8000/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s - deploy: - resources: - reservations: - devices: - - driver: nvidia - count: 1 - capabilities: [gpu] - - frontend: - build: - context: ../.. - dockerfile: deploy/1panel/Dockerfile.frontend - container_name: tool_ocr_frontend - restart: unless-stopped - ports: - - "${FRONTEND_PORT:-12010}:80" - environment: - - VITE_API_BASE_URL=http://localhost:${BACKEND_PORT:-8000} - depends_on: - - backend - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:80"] - interval: 30s - timeout: 10s - retries: 3 - -volumes: - tool_ocr_uploads: - tool_ocr_storage: - tool_ocr_logs: - tool_ocr_models: - -networks: - default: - name: tool_ocr_network diff --git a/deploy/1panel/nginx.conf b/deploy/1panel/nginx.conf deleted file mode 100644 index 108f3e2..0000000 --- a/deploy/1panel/nginx.conf +++ /dev/null @@ -1,37 +0,0 @@ -server { - listen 80; - server_name _; - - root /usr/share/nginx/html; - index index.html; - - # Gzip compression - gzip on; - gzip_vary on; - gzip_min_length 1024; - gzip_proxied expired no-cache no-store private auth; - gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml application/javascript; - - # Security headers - add_header X-Frame-Options "SAMEORIGIN" always; - add_header X-Content-Type-Options "nosniff" always; - add_header X-XSS-Protection "1; mode=block" always; - - # SPA routing - all routes go to index.html - location / { - try_files $uri $uri/ /index.html; - } - - # Static assets caching - location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { - expires 1y; - add_header Cache-Control "public, immutable"; - } - - # Health check endpoint - location /health { - access_log off; - return 200 "healthy\n"; - add_header Content-Type text/plain; - } -} diff --git a/setup_dev_env.sh b/setup_dev_env.sh index 7e6d9ef..3233724 100755 --- a/setup_dev_env.sh +++ b/setup_dev_env.sh @@ -227,17 +227,17 @@ init_database() { echo "初始化數據庫..." cd "$SCRIPT_DIR/backend" - # 檢查 .env.local 是否存在 - if [ ! -f "$SCRIPT_DIR/.env.local" ]; then - print_warning "未找到 .env.local,跳過數據庫初始化" - echo " 請複製 .env.example 到 .env.local 並配置後,手動執行:" + # 檢查 .env 是否存在 + if [ ! -f "$SCRIPT_DIR/.env" ]; then + print_warning "未找到 .env,跳過數據庫初始化" + echo " 請複製 .env.example 到 .env 並配置後,手動執行:" echo " cd backend && alembic upgrade head" cd "$SCRIPT_DIR" return fi # 載入環境變量 - export $(grep -v '^#' "$SCRIPT_DIR/.env.local" | xargs) + export $(grep -v '^#' "$SCRIPT_DIR/.env" | xargs) # 執行遷移 if alembic upgrade head 2>/dev/null; then @@ -414,8 +414,8 @@ echo "" echo "下一步操作:" echo "" echo "1. 配置環境變量 (如尚未配置):" -echo " cp .env.example .env.local" -echo " # 編輯 .env.local 填入實際配置" +echo " cp .env.example .env" +echo " # 編輯 .env 填入實際配置" echo "" echo "2. 啟動應用:" echo " ./start.sh # 同時啟動前後端" diff --git a/start-prod.sh b/start-prod.sh deleted file mode 100755 index d81fa52..0000000 --- a/start-prod.sh +++ /dev/null @@ -1,375 +0,0 @@ -#!/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 diff --git a/start.sh b/start.sh index 44c6f9a..953ed1f 100755 --- a/start.sh +++ b/start.sh @@ -1,9 +1,10 @@ #!/bin/bash -# Tool_OCR - Unified Development Server Startup Script +# Tool_OCR - Unified Server Startup Script # Usage: # ./start.sh Start all services (backend + frontend) # ./start.sh backend Start only backend # ./start.sh frontend Start only frontend +# ./start.sh --prod Start in production mode (no hot-reload) # ./start.sh --stop Stop all services # ./start.sh --status Show service status # ./start.sh --help Show help @@ -27,6 +28,10 @@ BACKEND_PORT=${BACKEND_PORT:-8000} FRONTEND_HOST=${FRONTEND_HOST:-0.0.0.0} FRONTEND_PORT=${FRONTEND_PORT:-5173} +# Production settings +PROD_MODE=false +UVICORN_WORKERS=${UVICORN_WORKERS:-4} + # Create PID directory mkdir -p "$PID_DIR" @@ -34,15 +39,19 @@ mkdir -p "$PID_DIR" print_header() { echo "" echo -e "${BLUE}================================${NC}" - echo -e "${BLUE} Tool_OCR Development Server${NC}" + if [ "$PROD_MODE" = true ]; then + echo -e "${BLUE} Tool_OCR Server (Production)${NC}" + else + echo -e "${BLUE} Tool_OCR Server (Development)${NC}" + fi echo -e "${BLUE}================================${NC}" echo "" } show_help() { - echo "Tool_OCR - Development Server Manager" + echo "Tool_OCR - Server Manager" echo "" - echo "Usage: ./start.sh [command]" + echo "Usage: ./start.sh [options] [command]" echo "" echo "Commands:" echo " (none) Start all services (backend + frontend)" @@ -52,10 +61,19 @@ show_help() { echo " --status Show status of services" echo " --help Show this help message" echo "" + echo "Options:" + echo " --prod Run in production mode (no hot-reload, multiple workers)" + echo "" + echo "Environment Variables:" + echo " BACKEND_PORT Backend port (default: 8000)" + echo " FRONTEND_PORT Frontend port (default: 5173)" + echo " UVICORN_WORKERS Number of workers in prod mode (default: 4)" + echo "" echo "Examples:" - echo " ./start.sh # Start everything" - echo " ./start.sh backend # Start only backend" - echo " ./start.sh --stop # Stop all services" + echo " ./start.sh # Start in dev mode" + echo " ./start.sh --prod # Start in production mode" + echo " BACKEND_PORT=8088 ./start.sh # Use custom port" + echo " ./start.sh --stop # Stop all services" echo "" } @@ -76,10 +94,10 @@ check_requirements() { missing=1 fi - # Check .env.local - if [ ! -f "$SCRIPT_DIR/.env.local" ]; then - echo -e "${RED}Error: .env.local not found${NC}" - echo "Please copy .env.example to .env.local and configure" + # Check .env + if [ ! -f "$SCRIPT_DIR/.env" ]; then + echo -e "${RED}Error: .env not found${NC}" + echo "Please copy .env.example to .env and configure" missing=1 fi @@ -87,12 +105,12 @@ check_requirements() { } load_env() { - # Load environment variables from root .env.local (if present). + # Load environment variables from root .env (if present). # This keeps backend/frontend config in sync without hardcoding ports/URLs in scripts. - if [ -f "$SCRIPT_DIR/.env.local" ]; then + if [ -f "$SCRIPT_DIR/.env" ]; then set -a # shellcheck disable=SC1090 - source "$SCRIPT_DIR/.env.local" + source "$SCRIPT_DIR/.env" set +a fi } @@ -124,7 +142,11 @@ start_backend() { return 0 fi - echo -e "${GREEN}Starting backend server...${NC}" + if [ "$PROD_MODE" = true ]; then + echo -e "${GREEN}Starting backend server (production mode)...${NC}" + else + echo -e "${GREEN}Starting backend server (development mode)...${NC}" + fi # Activate virtual environment source "$SCRIPT_DIR/venv/bin/activate" @@ -141,8 +163,18 @@ start_backend() { mkdir -p models/paddleocr mkdir -p logs - # Start uvicorn - nohup uvicorn app.main:app --reload --host "$BACKEND_HOST" --port "$BACKEND_PORT" > "$SCRIPT_DIR/.pid/backend.log" 2>&1 & + # Start uvicorn (different modes) + if [ "$PROD_MODE" = true ]; then + nohup uvicorn app.main:app \ + --host "$BACKEND_HOST" \ + --port "$BACKEND_PORT" \ + --workers "$UVICORN_WORKERS" \ + --access-log \ + --log-level info \ + > "$SCRIPT_DIR/.pid/backend.log" 2>&1 & + else + nohup uvicorn app.main:app --reload --host "$BACKEND_HOST" --port "$BACKEND_PORT" > "$SCRIPT_DIR/.pid/backend.log" 2>&1 & + fi local pid=$! echo $pid > "$BACKEND_PID_FILE" @@ -151,7 +183,11 @@ start_backend() { # Wait a moment and verify sleep 2 if is_running "$BACKEND_PID_FILE"; then - echo -e "${GREEN}Backend started (PID: $pid)${NC}" + if [ "$PROD_MODE" = true ]; then + echo -e "${GREEN}Backend started (PID: $pid, Workers: $UVICORN_WORKERS)${NC}" + else + echo -e "${GREEN}Backend started (PID: $pid)${NC}" + fi echo -e " API Docs: http://localhost:$BACKEND_PORT/docs" echo -e " Health: http://localhost:$BACKEND_PORT/health" else @@ -166,7 +202,11 @@ start_frontend() { return 0 fi - echo -e "${GREEN}Starting frontend server...${NC}" + if [ "$PROD_MODE" = true ]; then + echo -e "${GREEN}Starting frontend server (production mode)...${NC}" + else + echo -e "${GREEN}Starting frontend server (development mode)...${NC}" + fi # Load environment variables so Vite config can use FRONTEND_PORT/FRONTEND_HOST/etc. load_env @@ -177,8 +217,19 @@ start_frontend() { cd "$SCRIPT_DIR/frontend" - # Start vite in background - nohup npm run dev > "$SCRIPT_DIR/.pid/frontend.log" 2>&1 & + # Start frontend (different modes) + if [ "$PROD_MODE" = true ]; then + # Build if needed + 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 + nohup npm run preview -- --host "$FRONTEND_HOST" --port "$FRONTEND_PORT" > "$SCRIPT_DIR/.pid/frontend.log" 2>&1 & + else + # Start vite dev server + nohup npm run dev > "$SCRIPT_DIR/.pid/frontend.log" 2>&1 & + fi local pid=$! echo $pid > "$FRONTEND_PID_FILE" @@ -294,8 +345,31 @@ show_status() { echo "" } +# Parse arguments +COMMAND="" +while [[ $# -gt 0 ]]; do + case $1 in + --prod) + PROD_MODE=true + shift + ;; + --help|-h|--stop|--status|backend|frontend|all) + COMMAND=$1 + shift + ;; + *) + echo -e "${RED}Unknown option: $1${NC}" + show_help + exit 1 + ;; + esac +done + +# Default command +COMMAND=${COMMAND:-all} + # Main -case "${1:-all}" in +case "$COMMAND" in --help|-h) show_help ;; @@ -321,7 +395,7 @@ case "${1:-all}" in echo -e "${YELLOW}Press Ctrl+C to stop (or use ./start.sh --stop)${NC}" echo -e "${YELLOW}Logs: tail -f .pid/frontend.log${NC}" ;; - all|"") + all) print_header check_requirements || exit 1 start_backend @@ -343,7 +417,7 @@ case "${1:-all}" in echo " Frontend: tail -f .pid/frontend.log" ;; *) - echo -e "${RED}Unknown command: $1${NC}" + echo -e "${RED}Unknown command: $COMMAND${NC}" show_help exit 1 ;;