diff --git a/.claude/settings.local.json b/.claude/settings.local.json index d47fec9..35d91b9 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -53,7 +53,13 @@ "Bash(done)", "Bash(git add:*)", "Bash(git commit:*)", - "Bash(git push)" + "Bash(git push)", + "Bash(docker --version:*)", + "Bash(dpkg:*)", + "Bash(pip3:*)", + "Bash(chmod:*)", + "Bash(sudo apt install:*)", + "Bash(/usr/bin/soffice:*)" ], "deny": [], "ask": [] diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 58f7bf8..0000000 --- a/.dockerignore +++ /dev/null @@ -1,87 +0,0 @@ -# Git -.git -.gitignore -.gitattributes - -# Python -__pycache__ -*.py[cod] -*$py.class -*.so -.Python -env/ -venv/ -ENV/ -*.egg-info/ -dist/ -build/ -*.egg -.pytest_cache/ -.coverage -htmlcov/ - -# Node -node_modules/ -npm-debug.log* -yarn-debug.log* -yarn-error.log* -.npm -.yarn - -# IDE -.vscode/ -.idea/ -*.swp -*.swo -*~ -.DS_Store - -# Environment files -.env -.env.local -.env.*.local - -# Logs -logs/ -*.log - -# Data directories (will be mounted as volumes) -data/ -uploads/ -storage/ -models/ - -# Backend specific -backend/uploads/ -backend/storage/ -backend/models/ -backend/logs/ -backend/__pycache__/ -backend/*.egg-info/ - -# Frontend specific -frontend/node_modules/ -frontend/dist/ -frontend/.vite/ -frontend/.cache/ - -# Documentation (not needed in container) -*.md -!README.md -docs/ -demo_docs/ - -# Claude and OpenSpec -.claude/ -openspec/ - -# OS -Thumbs.db -Desktop.ini - -# Temporary files -*.tmp -*.bak -*.swp -temp/ -tmp/ diff --git a/.env.docker b/.env.docker deleted file mode 100644 index 4037ff1..0000000 --- a/.env.docker +++ /dev/null @@ -1,82 +0,0 @@ -# Tool_OCR - Docker Environment Configuration -# Copy this file to .env when deploying with Docker - -# ===== Database Configuration ===== -MYSQL_HOST=mysql.theaken.com -MYSQL_PORT=33306 -MYSQL_USER=A060 -MYSQL_PASSWORD=WLeSCi0yhtc7 -MYSQL_DATABASE=db_A060 - -# ===== Application Configuration ===== -# External port (exposed to host) -FRONTEND_PORT=12015 - -# Security (IMPORTANT: Change SECRET_KEY in production!) -SECRET_KEY=your-secret-key-here-please-change-this-to-random-string -ALGORITHM=HS256 -ACCESS_TOKEN_EXPIRE_MINUTES=1440 - -# ===== OCR Configuration ===== -# PaddleOCR model directory (inside container) -PADDLEOCR_MODEL_DIR=/app/backend/models/paddleocr -# 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 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 (inside container) -UPLOAD_DIR=/app/backend/uploads -TEMP_DIR=/app/backend/uploads/temp -PROCESSED_DIR=/app/backend/uploads/processed -IMAGES_DIR=/app/backend/uploads/images - -# ===== Export Configuration ===== -# Storage directories (inside container) -STORAGE_DIR=/app/backend/storage -MARKDOWN_DIR=/app/backend/storage/markdown -JSON_DIR=/app/backend/storage/json -EXPORTS_DIR=/app/backend/storage/exports - -# ===== PDF Generation Configuration ===== -# Pandoc path (inside container) -PANDOC_PATH=/usr/bin/pandoc -# Font directory (inside container) -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 (Reserved) ===== -# Enable translation feature (reserved for future) -ENABLE_TRANSLATION=false -# Translation engine: offline (argostranslate) or api (future) -TRANSLATION_ENGINE=offline -# Argostranslate models directory (inside container) -ARGOSTRANSLATE_MODELS_DIR=/app/backend/models/argostranslate - -# ===== 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 ===== -# Allowed origins (comma-separated, * for all) -# For Docker, use the external URL -CORS_ORIGINS=http://localhost:12015,http://127.0.0.1:12015 - -# ===== Logging Configuration ===== -LOG_LEVEL=INFO -LOG_FILE=/app/backend/logs/app.log diff --git a/DOCKER_DEPLOYMENT.md b/DOCKER_DEPLOYMENT.md deleted file mode 100644 index 08c3486..0000000 --- a/DOCKER_DEPLOYMENT.md +++ /dev/null @@ -1,524 +0,0 @@ -# Tool_OCR Docker 部署指南 - -## 架構說明 - -Tool_OCR 使用統一容器架構,將前端和後端封裝在同一個容器中: - -``` -┌─────────────────────────────────────┐ -│ Container (tool_ocr) │ -│ │ -│ ┌──────────────────────────────┐ │ -│ │ Nginx :12010 (External) │ │ -│ │ - Frontend Static Files │ │ -│ │ - Reverse Proxy for API │ │ -│ └─────────┬────────────────────┘ │ -│ │ proxy_pass │ -│ ▼ │ -│ ┌──────────────────────────────┐ │ -│ │ FastAPI :8000 (Internal) │ │ -│ │ - OCR Processing │ │ -│ │ - File Management │ │ -│ │ - Export Services │ │ -│ └──────────────────────────────┘ │ -│ │ -│ Supervisor manages both services │ -└─────────────────────────────────────┘ - │ - │ Port 12010 only! - ▼ - External Access -``` - -### 優勢 - -1. **單一端口**: 只需要暴露一個端口 (12010) -2. **簡化部署**: 一個容器包含完整應用 -3. **統一管理**: Supervisor 管理所有服務 -4. **生產就緒**: Nginx 提供高性能靜態文件服務和反向代理 - -## 快速開始 - -### 前置要求 - -- Docker Engine 20.10+ -- Docker Compose 2.0+ -- 至少 4GB 可用內存 -- 至少 10GB 可用磁碟空間 - -### 1. 準備環境配置 - -**複製環境配置範本:** - -Linux/Mac: -```bash -cp .env.docker .env -``` - -Windows (PowerShell): -```powershell -Copy-Item .env.docker .env -``` - -**編輯 `.env` 文件,至少修改以下重要配置:** - -```bash -# 修改為安全的密鑰 -SECRET_KEY=your-very-secure-random-key-here - -# 根據需要調整端口 -FRONTEND_PORT=12010 - -# 根據實際情況配置 CORS -CORS_ORIGINS=http://your-domain.com:12010,http://localhost:12010 -``` - -### 2. 創建數據目錄 - -Linux/Mac: -```bash -mkdir -p data/{uploads,storage,models,logs} -``` - -Windows (PowerShell): -```powershell -mkdir -p data/uploads, data/storage, data/models, data/logs -``` - -或使用跨平台命令: -```bash -mkdir -p data/uploads data/storage data/models data/logs -``` - -### 3. 構建並啟動容器 - -```bash -# 構建映像 -docker compose build - -# 啟動服務 -docker compose up -d - -# 查看日誌 -docker compose logs -f -``` - -> 注意:舊版本 Docker 使用 `docker-compose`(帶連字符),新版本使用 `docker compose`(無連字符)。兩者都支持。 - -### 4. 驗證部署 - -Linux/Mac: -```bash -# 檢查健康狀態 -curl http://localhost:12010/health - -# 訪問 API 文檔 -open http://localhost:12010/docs - -# 訪問前端界面 -open http://localhost:12010 -``` - -Windows (PowerShell): -```powershell -# 檢查健康狀態 -curl http://localhost:12010/health - -# 在瀏覽器中打開 -Start-Process "http://localhost:12010" -Start-Process "http://localhost:12010/docs" -``` - -## 管理命令 - -> 提示:以下命令在 Windows、Linux 和 Mac 上通用。如果您使用舊版 Docker,將 `docker compose` 替換為 `docker-compose`。 - -### 查看狀態 - -```bash -# 查看容器狀態 -docker compose ps - -# 查看實時日誌 -docker compose logs -f - -# 查看特定服務日誌 -docker compose exec tool_ocr tail -f /var/log/nginx/tool_ocr_access.log -docker compose exec tool_ocr tail -f /app/backend/logs/app.log -``` - -### 重啟服務 - -```bash -# 重啟容器 -docker compose restart - -# 重啟 Nginx (容器內) -docker compose exec tool_ocr supervisorctl restart nginx - -# 重啟 Backend (容器內) -docker compose exec tool_ocr supervisorctl restart backend -``` - -### 停止和清理 - -```bash -# 停止服務 -docker compose stop - -# 停止並移除容器 -docker compose down - -# 完全清理(包括數據卷)⚠️ 慎用 -docker compose down -v -``` - -### 進入容器調試 - -```bash -# 進入容器 shell -docker compose exec tool_ocr bash - -# 查看 Supervisor 狀態 -docker compose exec tool_ocr supervisorctl status - -# 查看進程 -docker compose exec tool_ocr ps aux -``` - -## 數據持久化 - -以下目錄會持久化到主機的 `./data/` 目錄: - -| 容器內路徑 | 主機路徑 | 說明 | -|-----------|---------|------| -| `/app/backend/uploads` | `./data/uploads` | 上傳文件 | -| `/app/backend/storage` | `./data/storage` | 處理結果 | -| `/app/backend/models` | `./data/models` | OCR 模型 | -| `/app/backend/logs` | `./data/logs` | 應用日誌 | - -### 備份數據 - -Linux/Mac: -```bash -# 備份所有數據 -tar -czf tool_ocr_backup_$(date +%Y%m%d).tar.gz data/ - -# 只備份重要數據 -tar -czf tool_ocr_data_$(date +%Y%m%d).tar.gz data/uploads data/storage -``` - -Windows (PowerShell): -```powershell -# 備份所有數據(需要安裝 7-Zip 或使用 Compress-Archive) -$date = Get-Date -Format "yyyyMMdd" -Compress-Archive -Path data -DestinationPath "tool_ocr_backup_$date.zip" - -# 只備份重要數據 -Compress-Archive -Path data/uploads, data/storage -DestinationPath "tool_ocr_data_$date.zip" -``` - -### 恢復數據 - -Linux/Mac: -```bash -# 停止容器 -docker compose stop - -# 恢復數據 -tar -xzf tool_ocr_backup_20250113.tar.gz - -# 啟動容器 -docker compose up -d -``` - -Windows (PowerShell): -```powershell -# 停止容器 -docker compose stop - -# 恢復數據 -Expand-Archive -Path tool_ocr_backup_20250113.zip -DestinationPath . -Force - -# 啟動容器 -docker compose up -d -``` - -## 1Panel 部署指南 - -### 1. 準備項目文件 - -在 1Panel 的應用目錄中創建項目: - -```bash -cd /opt/1panel/apps -mkdir -p tool_ocr -cd tool_ocr - -# 上傳項目文件 -# - Dockerfile -# - docker-compose.yml -# - docker/ 目錄 -# - backend/ 目錄 -# - frontend/ 目錄 -# - requirements.txt -# - .env -``` - -### 2. 在 1Panel 中創建應用 - -1. 登入 1Panel 管理面板 -2. 進入「應用商店」→「自定義應用」 -3. 選擇「Docker Compose」 -4. 上傳或粘貼 `docker-compose.yml` 內容 -5. 配置環境變量 -6. 點擊「創建」 - -### 3. 配置反向代理(可選) - -如果需要通過域名訪問: - -1. 在 1Panel 中創建網站 -2. 配置反向代理: - - 目標地址: `http://127.0.0.1:12010` - - 啟用 WebSocket 支援(如需要) - -### 4. 配置 SSL 證書(可選) - -1. 在 1Panel 網站設置中 -2. 申請或上傳 SSL 證書 -3. 啟用 HTTPS - -## 更新部署 - -### 更新代碼 - -```bash -# 停止容器 -docker compose stop - -# 拉取最新代碼 -git pull - -# 重新構建映像 -docker compose build --no-cache - -# 啟動容器 -docker compose up -d - -# 查看日誌確認啟動成功 -docker compose logs -f -``` - -### 數據庫遷移 - -如果有數據庫結構變更: - -```bash -# 進入容器 -docker compose exec tool_ocr bash - -# 運行遷移 -cd /app/backend -alembic upgrade head - -# 退出容器 -exit -``` - -## 故障排除 - -### 1. 容器無法啟動 - -```bash -# 查看詳細錯誤 -docker compose logs -``` - -檢查端口占用: - -Linux/Mac: -```bash -netstat -tuln | grep 12010 -# 或 -lsof -i :12010 -``` - -Windows (PowerShell): -```powershell -netstat -ano | findstr 12010 -# 或 -Get-NetTCPConnection -LocalPort 12010 -``` - -檢查磁碟空間: - -Linux/Mac: -```bash -df -h -``` - -Windows (PowerShell): -```powershell -Get-PSDrive -``` - -### 2. Nginx 無法啟動 - -```bash -# 檢查 Nginx 配置語法 -docker compose exec tool_ocr nginx -t - -# 查看 Nginx 錯誤日誌 -docker compose exec tool_ocr cat /var/log/nginx/error.log -``` - -### 3. Backend API 無法訪問 - -```bash -# 檢查 Backend 是否運行 -docker compose exec tool_ocr supervisorctl status backend - -# 查看 Backend 日誌 -docker compose exec tool_ocr cat /app/backend/logs/app.log - -# 重啟 Backend -docker compose exec tool_ocr supervisorctl restart backend -``` - -### 4. 數據庫連接失敗 - -```bash -# 測試數據庫連接 -docker compose exec tool_ocr python -c " -from app.core.database import engine -try: - with engine.connect() as conn: - print('Database connection successful!') -except Exception as e: - print(f'Database connection failed: {e}') -" -``` - -### 5. OCR 處理失敗 - -```bash -# 檢查 PaddleOCR 模型 -docker compose exec tool_ocr ls -la /app/backend/models/paddleocr/ - -# 測試 OCR 功能 -docker compose exec tool_ocr python -c " -from paddleocr import PaddleOCR -ocr = PaddleOCR(lang='ch') -print('PaddleOCR initialized successfully!') -" -``` - -### 6. 前端頁面無法訪問 - -```bash -# 檢查前端文件是否存在 -docker compose exec tool_ocr ls -la /app/frontend/dist/ - -# 檢查 Nginx 配置 -docker compose exec tool_ocr cat /etc/nginx/conf.d/default.conf -``` - -## 性能優化 - -### 1. 調整 OCR 工作進程數 - -根據 CPU 核心數調整: - -```bash -# 在 .env 中設置 -MAX_OCR_WORKERS=8 # 建議設置為 CPU 核心數 -``` - -### 2. 調整 Nginx Worker 進程數 - -編輯 `docker/nginx.conf`: - -```nginx -worker_processes auto; # 自動根據 CPU 核心數 -``` - -### 3. 優化 Upload 大小限制 - -根據實際需求調整: - -```bash -# 在 .env 中設置(以字節為單位) -MAX_UPLOAD_SIZE=104857600 # 100MB -``` - -同時修改 `docker/nginx.conf`: - -```nginx -client_max_body_size 100M; -``` - -## 監控和日誌 - -### 日誌位置 - -| 服務 | 容器內路徑 | 主機路徑 | -|------|-----------|---------| -| Nginx Access | `/var/log/nginx/tool_ocr_access.log` | - | -| Nginx Error | `/var/log/nginx/tool_ocr_error.log` | - | -| Backend | `/app/backend/logs/app.log` | `./data/logs/app.log` | -| Supervisor | `/var/log/supervisor/supervisord.log` | - | - -### 日誌輪轉 - -建議配置日誌輪轉以防止日誌文件過大: - -```bash -# 創建 logrotate 配置(主機上) -cat > /etc/logrotate.d/tool_ocr << 'EOF' -/path/to/tool_ocr/data/logs/*.log { - daily - rotate 7 - compress - delaycompress - notifempty - create 0644 root root -} -EOF -``` - -## 安全建議 - -1. **修改默認密鑰**: 務必修改 `.env` 中的 `SECRET_KEY` -2. **使用 HTTPS**: 在生產環境中啟用 SSL/TLS -3. **限制 CORS**: 只允許可信的來源 -4. **定期更新**: 及時更新 Docker 映像和依賴 -5. **備份數據**: 定期備份重要數據 -6. **監控日誌**: 定期檢查日誌中的異常活動 - -## 常見問題 - -### Q: 如何修改對外端口? - -A: 修改 `.env` 中的 `FRONTEND_PORT` 和 `docker-compose.yml` 中的端口映射。 - -### Q: 如何增加上傳文件大小限制? - -A: 修改 `.env` 中的 `MAX_UPLOAD_SIZE` 和 `docker/nginx.conf` 中的 `client_max_body_size`。 - -### Q: 如何連接外部 MySQL 數據庫? - -A: 在 `.env` 中配置正確的數據庫連接信息。 - -### Q: 如何查看詳細的錯誤信息? - -A: 設置 `.env` 中的 `LOG_LEVEL=DEBUG` 並重啟容器。 - -## 聯繫支援 - -如果遇到問題,請: - -1. 查看日誌: `docker-compose logs -f` -2. 檢查配置: 確認 `.env` 文件正確 -3. 查看文檔: 參考本文檔的故障排除部分 -4. 提交 Issue: 在項目倉庫提交問題報告 diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index cdb1132..0000000 --- a/Dockerfile +++ /dev/null @@ -1,131 +0,0 @@ -# ============================================ -# Tool_OCR - Unified Docker Image -# Frontend (React + Vite) + Backend (FastAPI) -# Served by Nginx with reverse proxy -# ============================================ - -# ============================================ -# Stage 1: Build Frontend -# ============================================ -FROM node:20-alpine AS frontend-builder - -WORKDIR /app/frontend - -# Copy package files -COPY frontend/package*.json ./ - -# Install all dependencies (including devDependencies for build) -RUN npm ci - -# Copy frontend source -COPY frontend/ ./ - -# Create production environment file -RUN echo "VITE_API_BASE_URL=" > .env.production - -# Build frontend for production -RUN npm run build - - -# ============================================ -# Stage 2: Build Backend + Final Image -# ============================================ -FROM python:3.10-slim-bookworm - -# Set working directory -WORKDIR /app - -# Set environment variables -ENV PYTHONUNBUFFERED=1 \ - PYTHONDONTWRITEBYTECODE=1 \ - PIP_NO_CACHE_DIR=1 \ - PIP_DISABLE_PIP_VERSION_CHECK=1 \ - DEBIAN_FRONTEND=noninteractive - -# Install system dependencies -# - nginx: web server and reverse proxy -# - supervisor: process manager for nginx + uvicorn -# - curl: for health checks -# - pandoc: for markdown to PDF conversion -# - poppler-utils: for pdf2image (PDF processing) -# - libpango-1.0-0, libpangocairo-1.0-0: for WeasyPrint -# - libgdk-pixbuf2.0-0: for WeasyPrint image handling -# - libffi-dev: for cryptography -# - fonts-noto-cjk: Chinese/Japanese/Korean font support -# - libgomp1, libgl1-mesa-glx, libglib2.0-0: for OpenCV and PaddleOCR -# - libmagic1: for python-magic file type detection -# - libreoffice-writer, libreoffice-impress: for Office document conversion (doc/docx/ppt/pptx) -RUN apt-get update && apt-get install -y --no-install-recommends \ - nginx \ - supervisor \ - curl \ - pandoc \ - poppler-utils \ - libpango-1.0-0 \ - libpangocairo-1.0-0 \ - libgdk-pixbuf2.0-0 \ - libffi-dev \ - fonts-noto-cjk \ - fonts-noto-cjk-extra \ - libgomp1 \ - libgl1-mesa-glx \ - libglib2.0-0 \ - libmagic1 \ - libreoffice-writer \ - libreoffice-impress \ - && rm -rf /var/lib/apt/lists/* - -# Copy Python requirements -COPY requirements.txt . - -# Install Python dependencies with extended timeout -# PaddlePaddle is 189MB and may take time to download -# Timeout: 600 seconds (10 minutes), Retries: 5 -RUN pip install --timeout 600 --retries 5 -r requirements.txt - -# Copy backend application -COPY backend/ ./backend/ - -# Copy frontend build from frontend-builder stage -COPY --from=frontend-builder /app/frontend/dist /app/frontend/dist - -# Copy Nginx configuration -COPY docker/nginx.conf /etc/nginx/nginx.conf -COPY docker/default.conf /etc/nginx/conf.d/default.conf - -# Copy supervisor configuration -COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf - -# Copy startup script and fix line endings (Windows CRLF -> Linux LF) -COPY docker/entrypoint.sh /entrypoint.sh -RUN sed -i 's/\r$//' /entrypoint.sh && chmod +x /entrypoint.sh - -# Create necessary directories with proper permissions -RUN mkdir -p \ - /app/backend/uploads/temp \ - /app/backend/uploads/processed \ - /app/backend/uploads/images \ - /app/backend/storage/markdown \ - /app/backend/storage/json \ - /app/backend/storage/exports \ - /app/backend/models/paddleocr \ - /app/backend/logs \ - /var/log/supervisor \ - /var/log/nginx \ - /var/cache/nginx \ - /var/run \ - && chmod -R 755 /app \ - && chown -R www-data:www-data /var/log/nginx /var/cache/nginx - -# Expose port (only one port needed!) -EXPOSE 12015 - -# Health check -HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ - CMD curl -f http://localhost:12015/health || exit 1 - -# Set working directory to backend for Python app -WORKDIR /app/backend - -# Use entrypoint script to start supervisor -ENTRYPOINT ["/entrypoint.sh"] diff --git a/README.md b/README.md index 37c42b7..0b84ec4 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ A web-based solution to extract text, images, and document structure from multip - 🖼️ **Image Extraction**: Preserve document images alongside text content - 📑 **Batch Processing**: Process multiple files concurrently with progress tracking - 📤 **Multiple Export Formats**: TXT, JSON, Excel, Markdown with images, searchable PDF +- 📋 **Office Documents**: DOC, DOCX, PPT, PPTX support via LibreOffice conversion - 🔧 **Flexible Configuration**: Rule-based output formatting - 🌐 **Translation Ready**: Reserved architecture for future translation features @@ -22,173 +23,176 @@ A web-based solution to extract text, images, and document structure from multip - **Database**: MySQL via SQLAlchemy - **PDF Generation**: Pandoc + WeasyPrint - **Image Processing**: OpenCV, Pillow, pdf2image +- **Office Conversion**: LibreOffice (headless mode) ### Frontend -- **Framework**: React 18 with Vite -- **Styling**: TailwindCSS + shadcn/ui -- **HTTP Client**: Axios with React Query +- **Framework**: React 19 with TypeScript +- **Build Tool**: Vite 7 +- **Styling**: Tailwind CSS v4 + shadcn/ui +- **State Management**: React Query + Zustand +- **HTTP Client**: Axios ## Prerequisites -- **macOS**: Apple Silicon (M1/M2/M3) or Intel -- **Python**: 3.10+ -- **Conda**: Miniconda or Anaconda (will be installed automatically) -- **Homebrew**: For system dependencies +- **OS**: WSL2 Ubuntu 24.04 +- **Python**: 3.12+ +- **Node.js**: 24.x LTS - **MySQL**: External database server (provided) -## Installation +## Quick Start ### 1. Automated Setup (Recommended) ```bash -# Clone the repository -cd /Users/egg/Projects/Tool_OCR - # Run automated setup script -chmod +x setup_conda.sh -./setup_conda.sh - -# If Conda was just installed, reload your shell -source ~/.zshrc # or source ~/.bash_profile - -# Run the script again to create environment -./setup_conda.sh +./setup_dev_env.sh ``` -### 2. Install Dependencies +This script automatically installs: +- Python development tools (pip, venv, build-essential) +- System dependencies (pandoc, LibreOffice, fonts, etc.) +- Node.js (via nvm) +- Python packages +- Frontend dependencies + +### 2. Initialize Database ```bash -# Activate Conda environment -conda activate tool_ocr - -# Install Python dependencies -pip install -r requirements.txt - -# Install system dependencies (Pandoc for PDF generation) -brew install pandoc - -# Install Chinese fonts for PDF generation (optional) -brew install --cask font-noto-sans-cjk -# Note: macOS built-in fonts work fine, this is optional -``` - -### 3. Download PaddleOCR Models - -```bash -# Create models directory -mkdir -p models/paddleocr - -# Models will be automatically downloaded on first run -# (~900MB total, includes PaddleOCR-VL 0.9B model) -``` - -### 4. Configure Environment - -```bash -# Copy environment template -cp .env.example .env - -# Edit .env with your settings -# Database credentials are pre-configured -nano .env -``` - -### 5. Initialize Database - -```bash -# Database schema will be created automatically on first run -# Using: mysql.theaken.com:33306/db_A060 -``` - -## Usage - -### Start Backend Server - -```bash -# Activate environment -conda activate tool_ocr - -# Start FastAPI server +source venv/bin/activate cd backend -python -m app.main - -# Server runs at: http://localhost:12010 -# API docs: http://localhost:12010/docs +alembic upgrade head +python create_test_user.py +cd .. ``` -### Start Frontend (Coming Soon) +Default test user: +- Username: `admin` +- Password: `admin123` +### 3. Start Development Servers + +**Backend (Terminal 1):** ```bash -# Install frontend dependencies -cd frontend -npm install - -# Start development server -npm run dev - -# Frontend runs at: http://localhost:12011 +./start_backend.sh ``` +**Frontend (Terminal 2):** +```bash +./start_frontend.sh +``` + +### 4. Access Application + +- **Frontend**: http://localhost:5173 +- **API Docs**: http://localhost:8000/docs +- **Health Check**: http://localhost:8000/health + ## Project Structure ``` Tool_OCR/ -├── backend/ +├── backend/ # FastAPI backend │ ├── app/ -│ │ ├── api/v1/ # API endpoints -│ │ ├── core/ # Configuration, database -│ │ ├── models/ # Database models -│ │ ├── services/ # Business logic -│ │ ├── utils/ # Utilities -│ │ └── main.py # Application entry point -│ └── tests/ # Test suite -├── frontend/ -│ └── src/ # React application -├── uploads/ -│ ├── temp/ # Temporary uploads -│ ├── processed/ # Processed files -│ └── images/ # Extracted images -├── storage/ -│ ├── markdown/ # Markdown outputs -│ ├── json/ # JSON results -│ └── exports/ # Export files -├── models/ -│ └── paddleocr/ # PaddleOCR models -├── config/ # Configuration files -├── templates/ # PDF templates -├── logs/ # Application logs -├── requirements.txt # Python dependencies -├── setup_conda.sh # Environment setup script -├── .env.example # Environment template -└── README.md +│ │ ├── api/v1/ # API endpoints +│ │ ├── core/ # Configuration, database +│ │ ├── models/ # Database models +│ │ ├── services/ # Business logic +│ │ └── main.py # Application entry point +│ ├── alembic/ # Database migrations +│ └── tests/ # Test suite +├── frontend/ # React frontend +│ ├── src/ +│ │ ├── components/ # UI components +│ │ ├── pages/ # Page components +│ │ ├── services/ # API services +│ │ └── stores/ # State management +│ └── public/ # Static assets +├── .env.local # Local development config +├── setup_dev_env.sh # Environment setup script +├── start_backend.sh # Backend startup script +└── start_frontend.sh # Frontend startup script ``` -## API Endpoints (Planned) +## Configuration -- `POST /api/v1/ocr/upload` - Upload files for OCR processing -- `GET /api/v1/ocr/tasks` - List all OCR tasks -- `GET /api/v1/ocr/tasks/{task_id}` - Get task details -- `POST /api/v1/ocr/batch` - Create batch processing task -- `GET /api/v1/export/{task_id}` - Export results (TXT/JSON/Excel/MD/PDF) -- `POST /api/v1/translate/document` - Translate document (reserved, returns 501) +Main config file: `.env.local` + +```bash +# Database +MYSQL_HOST=mysql.theaken.com +MYSQL_PORT=33306 + +# Application ports +BACKEND_PORT=8000 +FRONTEND_PORT=5173 + +# Token expiration (minutes) +ACCESS_TOKEN_EXPIRE_MINUTES=1440 # 24 hours + +# Supported file formats +ALLOWED_EXTENSIONS=png,jpg,jpeg,pdf,bmp,tiff,doc,docx,ppt,pptx + +# OCR settings +OCR_LANGUAGES=ch,en,japan,korean +MAX_OCR_WORKERS=4 +``` + +## API Endpoints + +### Authentication +- `POST /api/v1/auth/login` - User login + +### File Management +- `POST /api/v1/upload` - Upload files +- `POST /api/v1/ocr/process` - Start OCR processing +- `GET /api/v1/batch/{id}/status` - Get batch status + +### Results & Export +- `GET /api/v1/ocr/result/{id}` - Get OCR result +- `GET /api/v1/export/pdf/{id}` - Export as PDF + +Full API documentation: http://localhost:8000/docs + +## Supported File Formats + +- **Images**: PNG, JPG, JPEG, BMP, TIFF +- **Documents**: PDF +- **Office**: DOC, DOCX, PPT, PPTX + +Office files are automatically converted to PDF before OCR processing. ## Development -### Run Tests +### Backend ```bash +source venv/bin/activate cd backend -pytest tests/ -v --cov=app + +# Run tests +pytest + +# Database migration +alembic revision --autogenerate -m "description" +alembic upgrade head + +# Code formatting +black app/ ``` -### Code Quality +### Frontend ```bash -# Format code -black app/ +cd frontend + +# Development server +npm run dev + +# Build for production +npm run build # Lint code -pylint app/ +npm run lint ``` ## OpenSpec Workflow @@ -208,26 +212,26 @@ cat openspec/changes/add-ocr-batch-processing/tasks.md ## Roadmap -- [x] **Phase 0**: Environment setup and configuration -- [ ] **Phase 1**: Core OCR with structure extraction -- [ ] **Phase 2**: Frontend development +- [x] **Phase 0**: Environment setup +- [x] **Phase 1**: Core OCR backend (~98% complete) +- [x] **Phase 2**: Frontend development (~92% complete) - [ ] **Phase 3**: Testing & optimization -- [ ] **Phase 4**: Deployment +- [ ] **Phase 4**: Deployment automation - [ ] **Phase 5**: Translation feature (future) +## Documentation + +- Development specs: [openspec/project.md](openspec/project.md) +- Implementation status: [openspec/changes/add-ocr-batch-processing/STATUS.md](openspec/changes/add-ocr-batch-processing/STATUS.md) +- Agent instructions: [openspec/AGENTS.md](openspec/AGENTS.md) + ## License -[To be determined] +Internal project use -## Contributors +## Notes -- Development environment: macOS Apple Silicon -- Database: MySQL external server -- OCR Engine: PaddleOCR-VL 0.9B with PP-StructureV3 - -## Support - -For issues and questions, refer to: -- OpenSpec documentation: `openspec/AGENTS.md` -- Task breakdown: `openspec/changes/add-ocr-batch-processing/tasks.md` -- Specifications: `openspec/changes/add-ocr-batch-processing/specs/` +- First OCR run will download PaddleOCR models (~900MB) +- Token expiration is set to 24 hours by default +- Office conversion requires LibreOffice (installed via setup script) +- Development environment: WSL2 Ubuntu 24.04 with Python venv diff --git a/SETUP.md b/SETUP.md deleted file mode 100644 index 25b204c..0000000 --- a/SETUP.md +++ /dev/null @@ -1,395 +0,0 @@ -# Tool_OCR Setup Guide - -Complete setup instructions for macOS environment. - -## Prerequisites Check - -Before starting, verify you have: -- ✅ macOS (Apple Silicon or Intel) -- ✅ Terminal access (zsh or bash) -- ✅ Internet connection for downloads - -## Step-by-Step Setup - -### Step 1: Install Conda Environment - -Run the automated setup script: - -```bash -chmod +x setup_conda.sh -./setup_conda.sh -``` - -**Expected output:** -- If Conda not installed: Downloads and installs Miniconda for Apple Silicon -- If Conda already installed: Creates `tool_ocr` environment with Python 3.10 - -**If Conda was just installed:** -```bash -# Reload your shell to activate Conda -source ~/.zshrc # if using zsh (default on macOS) -source ~/.bashrc # if using bash - -# Run setup script again to create environment -./setup_conda.sh -``` - -### Step 2: Activate Environment - -```bash -conda activate tool_ocr -``` - -You should see `(tool_ocr)` prefix in your terminal prompt. - -### Step 3: Install Python Dependencies - -```bash -pip install -r requirements.txt -``` - -**This will install:** -- FastAPI and Uvicorn (web framework) -- PaddleOCR and PaddlePaddle (OCR engine) -- Image processing libraries (Pillow, OpenCV, pdf2image) -- PDF generation tools (WeasyPrint, Markdown) -- Database tools (SQLAlchemy, PyMySQL, Alembic) -- Authentication libraries (python-jose, passlib) -- Testing tools (pytest, pytest-asyncio) - -**Installation time:** ~5-10 minutes depending on your internet speed - -### Step 4: Install System Dependencies - -```bash -# Install libmagic (required for python-magic file type detection) -brew install libmagic - -# Install WeasyPrint dependencies (required for PDF generation) -brew install pango gdk-pixbuf libffi - -# Install Pandoc (optional - for enhanced PDF generation) -brew install pandoc - -# Install Chinese fonts for PDF output (optional - macOS has built-in Chinese fonts) -brew install --cask font-noto-sans-cjk -# Note: If above fails, skip it - macOS built-in fonts (PingFang SC, Heiti TC) work fine -``` - -**If Homebrew not installed:** -```bash -/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" -``` - -### Step 5: Configure Environment Variables - -```bash -# Copy template -cp .env.example .env - -# Edit with your preferred editor -nano .env -# or -code .env -``` - -**Important settings to verify in `.env`:** - -```bash -# Database (pre-configured, should work as-is) -MYSQL_HOST=mysql.theaken.com -MYSQL_PORT=33306 -MYSQL_USER=A060 -MYSQL_PASSWORD=WLeSCi0yhtc7 -MYSQL_DATABASE=db_A060 - -# Application ports -BACKEND_PORT=12010 -FRONTEND_PORT=12011 - -# Security (CHANGE THIS!) -SECRET_KEY=your-secret-key-here-please-change-this-to-random-string -``` - -**Generate a secure SECRET_KEY:** -```bash -python -c "import secrets; print(secrets.token_urlsafe(32))" -``` - -Copy the output and paste it as your `SECRET_KEY` value. - -### Step 6: Set Environment Variable for WeasyPrint - -Add to your shell config (`~/.zshrc` or `~/.bash_profile`): - -```bash -export DYLD_LIBRARY_PATH="/opt/homebrew/lib:$DYLD_LIBRARY_PATH" -``` - -Then reload: -```bash -source ~/.zshrc # or source ~/.bash_profile -``` - -### Step 7: Run Service Layer Tests - -Verify all services are working: - -```bash -cd backend -python test_services.py -``` - -Expected output: -``` -✓ PASS - database -✓ PASS - preprocessor -✓ PASS - pdf_generator -✓ PASS - file_manager -Total: 4-5/5 tests passed -``` - -**Note:** OCR engine test may fail on first run as PaddleOCR downloads models (~900MB). This is normal. - -### Step 8: Create Directory Structure - -The directories should already exist, but verify: - -```bash -ls -la -``` - -You should see: -- `backend/` - FastAPI application -- `frontend/` - React application (will be populated later) -- `uploads/` - File upload storage -- `storage/` - Processed results -- `models/` - PaddleOCR models (empty until first run) -- `logs/` - Application logs - -### Step 8: Start Backend Server - -```bash -cd backend -python -m app.main -``` - -**Expected output:** -``` -INFO: Started server process -INFO: Waiting for application startup. -INFO: Application startup complete. -INFO: Uvicorn running on http://0.0.0.0:12010 -``` - -**Test the server:** -Open browser and visit: -- http://localhost:12010 - API root -- http://localhost:12010/docs - Interactive API documentation -- http://localhost:12010/health - Health check endpoint - -### Step 9: Download PaddleOCR Models - -On first OCR request, PaddleOCR will automatically download models (~900MB). - -**To pre-download models manually:** - -```bash -python -c " -from paddleocr import PaddleOCR -ocr = PaddleOCR(use_angle_cls=True, lang='ch', use_gpu=False) -print('Models downloaded successfully') -" -``` - -This will download: -- Detection model: ch_PP-OCRv4_det -- Recognition model: ch_PP-OCRv4_rec -- Angle classifier: ch_ppocr_mobile_v2.0_cls - -Models are stored in: `./models/paddleocr/` - -## Troubleshooting - -### Issue: "conda: command not found" - -**Solution:** -```bash -# Reload shell configuration -source ~/.zshrc # or source ~/.bashrc - -# If still not working, manually add Conda to PATH -export PATH="$HOME/miniconda3/bin:$PATH" -``` - -### Issue: PaddlePaddle installation fails - -**Solution:** -```bash -# For Apple Silicon Macs, ensure you're using ARM version -pip uninstall paddlepaddle -pip install paddlepaddle --no-cache-dir -``` - -### Issue: WeasyPrint fails to install - -**Solution:** -```bash -# Install required system libraries -brew install cairo pango gdk-pixbuf libffi -pip install --upgrade weasyprint -``` - -### Issue: Database connection fails - -**Solution:** -```bash -# Test database connection -python -c " -import pymysql -conn = pymysql.connect( - host='mysql.theaken.com', - port=33306, - user='A060', - password='WLeSCi0yhtc7', - database='db_A060' -) -print('Database connection OK') -conn.close() -" -``` - -If this fails, verify: -- Internet connection is active -- Firewall is not blocking port 33306 -- Database credentials in `.env` are correct - -### Issue: Port 12010 already in use - -**Solution:** -```bash -# Find what's using the port -lsof -i :12010 - -# Kill the process or change port in .env -# Edit BACKEND_PORT=12011 (or any available port) -``` - -## Next Steps - -After successful setup: - -1. ✅ Environment is ready -2. ✅ Backend server can start -3. ✅ Database connection configured - -**Ready to develop:** -- Implement database models (`backend/app/models/`) -- Create API endpoints (`backend/app/api/v1/`) -- Build OCR service (`backend/app/services/ocr_service.py`) -- Develop frontend UI (`frontend/src/`) - -**Start with Phase 1 tasks:** -Refer to [openspec/changes/add-ocr-batch-processing/tasks.md](openspec/changes/add-ocr-batch-processing/tasks.md) for detailed implementation tasks. - -## Development Workflow - -```bash -# Activate environment -conda activate tool_ocr - -# Start backend in development mode (auto-reload) -cd backend -python -m app.main - -bash -c "source ~/.zshrc && conda activate tool_ocr && export DYLD_LIBRARY_PATH=/opt/homebrew/lib:$DYLD_LIBRARY_PATH && python -m app.main" - -# In another terminal, start frontend -cd frontend -npm run dev - -# Run tests -cd backend -pytest tests/ -v - -# Check code style -black app/ -pylint app/ -``` - -## Background Services - -### Automatic Cleanup Scheduler - -The application automatically runs a cleanup scheduler that: -- **Runs every**: 1 hour (configurable via `BackgroundTaskManager.cleanup_interval`) -- **Deletes files older than**: 24 hours (configurable via `BackgroundTaskManager.file_retention_hours`) -- **Cleans up**: - - Physical files and directories - - Database records (results, files, batches) - - Expired batches in COMPLETED, FAILED, or PARTIAL status - -The cleanup scheduler starts automatically when the backend application starts and stops gracefully on shutdown. - -**Monitor cleanup activity:** -```bash -# Watch cleanup logs in real-time -tail -f /tmp/tool_ocr_startup.log | grep cleanup - -# Or check application logs -tail -f backend/logs/app.log | grep cleanup -``` - -### Retry Logic - -OCR processing includes automatic retry logic: -- **Maximum retries**: 3 attempts (configurable) -- **Retry delay**: 5 seconds between attempts (configurable) -- **Tracks**: `retry_count` field in database -- **Error handling**: Detailed error messages with retry attempt information - -**Configuration** (in [backend/app/services/background_tasks.py](backend/app/services/background_tasks.py)): -```python -task_manager = BackgroundTaskManager( - max_retries=3, # Number of retry attempts - retry_delay=5, # Delay between retries (seconds) - cleanup_interval=3600, # Cleanup runs every hour - file_retention_hours=24 # Keep files for 24 hours -) -``` - -### Background Task Status - -Check if background services are running: -```bash -# Check health endpoint -curl http://localhost:12010/health - -# Check application startup logs for cleanup scheduler -grep "cleanup scheduler" /tmp/tool_ocr_startup.log -# Expected output: "Started cleanup scheduler for expired files" -# Expected output: "Starting cleanup scheduler (interval: 3600s, retention: 24h)" -``` - -## Deactivate Environment - -When done working: -```bash -conda deactivate -``` - -## Environment Management - -```bash -# List Conda environments -conda env list - -# Remove environment (if needed) -conda env remove -n tool_ocr - -# Export environment -conda env export > environment.yml - -# Create from exported environment -conda env create -f environment.yml -``` diff --git a/backend/app/core/database.py b/backend/app/core/database.py index 46ad99f..9623a74 100644 --- a/backend/app/core/database.py +++ b/backend/app/core/database.py @@ -14,6 +14,13 @@ engine = create_engine( pool_pre_ping=True, # Enable connection health checks pool_size=10, max_overflow=20, + pool_recycle=3600, # Recycle connections every hour + pool_timeout=30, # Connection timeout + connect_args={ + 'connect_timeout': 10, + 'read_timeout': 30, + 'write_timeout': 30, + }, echo=False, # Set to True for SQL query logging ) diff --git a/backend/app/services/background_tasks.py b/backend/app/services/background_tasks.py index c83fac2..3d0ec65 100644 --- a/backend/app/services/background_tasks.py +++ b/backend/app/services/background_tasks.py @@ -168,24 +168,39 @@ class BackgroundTaskManager: ocr_file.completed_at = datetime.utcnow() ocr_file.processing_time = (ocr_file.completed_at - ocr_file.started_at).total_seconds() - db.commit() + # Commit with retry on connection errors + try: + db.commit() + except Exception as commit_error: + logger.warning(f"Commit failed, rolling back and retrying: {commit_error}") + db.rollback() + db.refresh(ocr_file) + ocr_file.status = FileStatus.COMPLETED + ocr_file.completed_at = datetime.utcnow() + ocr_file.processing_time = (ocr_file.completed_at - ocr_file.started_at).total_seconds() + db.commit() logger.info(f"Successfully processed file {ocr_file.id} ({ocr_file.original_filename})") return True except Exception as e: logger.error(f"Attempt {attempt + 1}/{self.max_retries + 1} failed for file {ocr_file.id}: {e}") + db.rollback() # Rollback failed transaction if attempt < self.max_retries: # Wait before retry time.sleep(self.retry_delay) else: # Final failure - ocr_file.status = FileStatus.FAILED - ocr_file.error_message = f"Failed after {self.max_retries + 1} attempts: {str(e)}" - ocr_file.completed_at = datetime.utcnow() - ocr_file.retry_count = attempt - db.commit() + try: + ocr_file.status = FileStatus.FAILED + ocr_file.error_message = f"Failed after {self.max_retries + 1} attempts: {str(e)}" + ocr_file.completed_at = datetime.utcnow() + ocr_file.retry_count = attempt + db.commit() + except Exception as final_error: + logger.error(f"Failed to update error status: {final_error}") + db.rollback() return False return False @@ -375,7 +390,17 @@ def process_batch_files_with_retry( batch.status = BatchStatus.FAILED batch.completed_at = datetime.utcnow() - db.commit() + + # Commit with retry on connection errors + try: + db.commit() + except Exception as commit_error: + logger.warning(f"Batch commit failed, rolling back and retrying: {commit_error}") + db.rollback() + batch = db.query(OCRBatch).filter(OCRBatch.id == batch_id).first() + if batch: + batch.completed_at = datetime.utcnow() + db.commit() logger.info( f"Batch {batch_id} processing complete: " @@ -384,6 +409,7 @@ def process_batch_files_with_retry( except Exception as e: logger.error(f"Fatal error processing batch {batch_id}: {e}") + db.rollback() # Rollback any failed transaction try: batch = db.query(OCRBatch).filter(OCRBatch.id == batch_id).first() if batch: @@ -392,3 +418,4 @@ def process_batch_files_with_retry( db.commit() except Exception as commit_error: logger.error(f"Error updating batch status: {commit_error}") + db.rollback() diff --git a/backend/app/services/office_converter.py b/backend/app/services/office_converter.py index 55274f9..1db9998 100644 --- a/backend/app/services/office_converter.py +++ b/backend/app/services/office_converter.py @@ -42,14 +42,16 @@ class OfficeConverter: def _verify_libreoffice(self): """Verify LibreOffice is installed and accessible""" if not Path(self.libreoffice_path).exists(): - # Try alternative path for Homebrew installation + # Try alternative path (system-wide installation) alt_path = shutil.which("soffice") if alt_path: self.libreoffice_path = alt_path logger.info(f"Using LibreOffice at: {alt_path}") else: raise OfficeConverterError( - "LibreOffice not found. Please install LibreOffice: brew install libreoffice" + "LibreOffice not found. Please install LibreOffice:\n" + " Ubuntu/Debian: sudo apt install -y libreoffice-writer libreoffice-impress libreoffice-core-nogui\n" + " macOS: brew install libreoffice" ) def is_office_document(self, file_path: Path) -> bool: diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 936b8a5..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,97 +0,0 @@ -services: - tool_ocr: - build: - context: . - dockerfile: Dockerfile - image: tool_ocr:latest - container_name: tool_ocr - restart: unless-stopped - - ports: - - "12015:12015" # Only one port needed! - - environment: - # Database Configuration - - MYSQL_HOST=${MYSQL_HOST:-mysql.theaken.com} - - MYSQL_PORT=${MYSQL_PORT:-33306} - - MYSQL_USER=${MYSQL_USER:-A060} - - MYSQL_PASSWORD=${MYSQL_PASSWORD:-WLeSCi0yhtc7} - - MYSQL_DATABASE=${MYSQL_DATABASE:-db_A060} - - # Application Configuration - - BACKEND_PORT=8000 # Internal backend port - - FRONTEND_PORT=12015 # External port - - SECRET_KEY=${SECRET_KEY:-your-secret-key-here-please-change-this} - - ALGORITHM=${ALGORITHM:-HS256} - - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES:-1440} - - # OCR Configuration - - PADDLEOCR_MODEL_DIR=/app/backend/models/paddleocr - - OCR_LANGUAGES=${OCR_LANGUAGES:-ch,en,japan,korean} - - OCR_CONFIDENCE_THRESHOLD=${OCR_CONFIDENCE_THRESHOLD:-0.5} - - MAX_OCR_WORKERS=${MAX_OCR_WORKERS:-4} - - # File Upload Configuration - - MAX_UPLOAD_SIZE=${MAX_UPLOAD_SIZE:-52428800} - - ALLOWED_EXTENSIONS=${ALLOWED_EXTENSIONS:-png,jpg,jpeg,pdf,bmp,tiff,doc,docx,ppt,pptx} - - UPLOAD_DIR=/app/backend/uploads - - TEMP_DIR=/app/backend/uploads/temp - - PROCESSED_DIR=/app/backend/uploads/processed - - IMAGES_DIR=/app/backend/uploads/images - - # Export Configuration - - STORAGE_DIR=/app/backend/storage - - MARKDOWN_DIR=/app/backend/storage/markdown - - JSON_DIR=/app/backend/storage/json - - EXPORTS_DIR=/app/backend/storage/exports - - # PDF Generation Configuration - - PANDOC_PATH=/usr/bin/pandoc - - FONT_DIR=/usr/share/fonts - - PDF_PAGE_SIZE=${PDF_PAGE_SIZE:-A4} - - PDF_MARGIN_TOP=${PDF_MARGIN_TOP:-20} - - PDF_MARGIN_BOTTOM=${PDF_MARGIN_BOTTOM:-20} - - PDF_MARGIN_LEFT=${PDF_MARGIN_LEFT:-20} - - PDF_MARGIN_RIGHT=${PDF_MARGIN_RIGHT:-20} - - # Translation Configuration (Reserved) - - ENABLE_TRANSLATION=${ENABLE_TRANSLATION:-false} - - TRANSLATION_ENGINE=${TRANSLATION_ENGINE:-offline} - - ARGOSTRANSLATE_MODELS_DIR=/app/backend/models/argostranslate - - # Background Tasks Configuration - - TASK_QUEUE_TYPE=${TASK_QUEUE_TYPE:-memory} - - # CORS Configuration - - CORS_ORIGINS=${CORS_ORIGINS:-http://localhost:12015,http://127.0.0.1:12015} - - # Logging Configuration - - LOG_LEVEL=${LOG_LEVEL:-INFO} - - LOG_FILE=/app/backend/logs/app.log - - volumes: - # Persist data directories - - ./data/uploads:/app/backend/uploads - - ./data/storage:/app/backend/storage - - ./data/models:/app/backend/models - - ./data/logs:/app/backend/logs - - networks: - - tool_ocr_network - - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:12010/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 40s - -networks: - tool_ocr_network: - driver: bridge - -volumes: - uploads: - storage: - models: - logs: diff --git a/docker/default.conf b/docker/default.conf deleted file mode 100644 index 597f92b..0000000 --- a/docker/default.conf +++ /dev/null @@ -1,89 +0,0 @@ -# Nginx Site Configuration for Tool_OCR - -upstream backend { - server 127.0.0.1:8000; - keepalive 32; -} - -server { - listen 12015; - server_name _; - - # 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; - - # Root directory for frontend - root /app/frontend/dist; - index index.html; - - # Logging - access_log /var/log/nginx/tool_ocr_access.log; - error_log /var/log/nginx/tool_ocr_error.log; - - # Backend API proxy - location /api/ { - proxy_pass http://backend/api/; - proxy_http_version 1.1; - - # Headers - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header Connection ""; - - # Timeouts - proxy_connect_timeout 60s; - proxy_send_timeout 300s; - proxy_read_timeout 300s; - - # Buffering - proxy_buffering off; - proxy_request_buffering off; - } - - # Health check endpoint (backend) - location /health { - proxy_pass http://backend/health; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header Connection ""; - } - - # API docs (backend) - location /docs { - proxy_pass http://backend/docs; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header Connection ""; - } - - location /openapi.json { - proxy_pass http://backend/openapi.json; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header Connection ""; - } - - # Frontend static files with caching - location /assets/ { - expires 1y; - add_header Cache-Control "public, immutable"; - } - - # Frontend - React Router support (SPA fallback) - location / { - try_files $uri $uri/ /index.html; - expires -1; - add_header Cache-Control "no-store, no-cache, must-revalidate"; - } - - # Deny access to hidden files - location ~ /\. { - deny all; - access_log off; - log_not_found off; - } -} diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh deleted file mode 100644 index 64bdc1c..0000000 --- a/docker/entrypoint.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash -set -e - -echo "========================================" -echo "Tool_OCR Container Starting..." -echo "========================================" - -# Wait a moment for system to stabilize -sleep 2 - -# Run database migrations if needed -echo "Checking database migrations..." -cd /app/backend -if [ -f "alembic.ini" ]; then - echo "Running Alembic migrations..." - alembic upgrade head || echo "Warning: Migration failed or already up to date" -fi - -# Create necessary directories if they don't exist -echo "Ensuring directories exist..." -mkdir -p \ - /app/backend/uploads/temp \ - /app/backend/uploads/processed \ - /app/backend/uploads/images \ - /app/backend/storage/markdown \ - /app/backend/storage/json \ - /app/backend/storage/exports \ - /app/backend/models/paddleocr \ - /app/backend/logs - -# Set permissions -chmod -R 755 /app/backend/uploads /app/backend/storage /app/backend/logs - -echo "========================================" -echo "Starting services with Supervisor..." -echo "- Nginx listening on port 12015" -echo "- Backend API on internal port 8000" -echo "========================================" - -# Start supervisord -exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf diff --git a/docker/nginx.conf b/docker/nginx.conf deleted file mode 100644 index 3c2e25a..0000000 --- a/docker/nginx.conf +++ /dev/null @@ -1,40 +0,0 @@ -# Nginx Main Configuration -user www-data; -worker_processes auto; -pid /var/run/nginx.pid; - -events { - worker_connections 1024; - use epoll; -} - -http { - # Basic Settings - sendfile on; - tcp_nopush on; - tcp_nodelay on; - keepalive_timeout 65; - types_hash_max_size 2048; - client_max_body_size 50M; # Match MAX_UPLOAD_SIZE in .env - - # MIME Types - include /etc/nginx/mime.types; - default_type application/octet-stream; - - # Logging - access_log /var/log/nginx/access.log; - error_log /var/log/nginx/error.log; - - # Gzip Compression - gzip on; - gzip_vary on; - gzip_proxied any; - gzip_comp_level 6; - gzip_types text/plain text/css text/xml text/javascript - application/json application/javascript application/xml+rss - application/rss+xml font/truetype font/opentype - application/vnd.ms-fontobject image/svg+xml; - - # Include site configurations - include /etc/nginx/conf.d/*.conf; -} diff --git a/docker/supervisord.conf b/docker/supervisord.conf deleted file mode 100644 index 162dba6..0000000 --- a/docker/supervisord.conf +++ /dev/null @@ -1,28 +0,0 @@ -[supervisord] -nodaemon=true -user=root -logfile=/var/log/supervisor/supervisord.log -pidfile=/var/run/supervisord.pid -loglevel=info - -[program:nginx] -command=/usr/sbin/nginx -g "daemon off;" -autostart=true -autorestart=true -priority=10 -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 - -[program:backend] -command=python -m uvicorn app.main:app --host 127.0.0.1 --port 8000 --log-level info -directory=/app/backend -autostart=true -autorestart=true -priority=20 -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 -environment=PYTHONUNBUFFERED="1" diff --git a/frontend/.env b/frontend/.env index e614546..c2058b5 100644 --- a/frontend/.env +++ b/frontend/.env @@ -1 +1 @@ -VITE_API_BASE_URL=http://localhost:12015 +VITE_API_BASE_URL=http://localhost:8000 diff --git a/frontend/.env.docker b/frontend/.env.docker deleted file mode 100644 index f75b4f2..0000000 --- a/frontend/.env.docker +++ /dev/null @@ -1,7 +0,0 @@ -# Frontend Environment Variables for Docker Deployment -# Copy this to frontend/.env.production for Docker builds - -# API Base URL -# In Docker environment, use empty string for same-origin requests -# Nginx will proxy /api/* to the backend -VITE_API_BASE_URL= diff --git a/frontend/package-lock.json b/frontend/package-lock.json index b029a94..1fac87f 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -86,6 +86,7 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -1672,9 +1673,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.90.7", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.7.tgz", - "integrity": "sha512-6PN65csiuTNfBMXqQUxQhCNdtm1rV+9kC9YwWAIKcaxAauq3Wu7p18j3gQY3YIBJU70jT/wzCCZ2uqto/vQgiQ==", + "version": "5.90.8", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.8.tgz", + "integrity": "sha512-4E0RP/0GJCxSNiRF2kAqE/LQkTJVlL/QNU7gIJSptaseV9HP6kOuA+N11y4bZKZxa3QopK3ZuewwutHx6DqDXQ==", "license": "MIT", "funding": { "type": "github", @@ -1682,12 +1683,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.90.7", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.7.tgz", - "integrity": "sha512-wAHc/cgKzW7LZNFloThyHnV/AX9gTg3w5yAv0gvQHPZoCnepwqCMtzbuPbb2UvfvO32XZ46e8bPOYbfZhzVnnQ==", + "version": "5.90.8", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.8.tgz", + "integrity": "sha512-/3b9QGzkf4rE5/miL6tyhldQRlLXzMHcySOm/2Tm2OLEFE9P1ImkH0+OviDBSvyAvtAOJocar5xhd7vxdLi3aQ==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.90.7" + "@tanstack/query-core": "5.90.8" }, "funding": { "type": "github", @@ -1803,15 +1804,17 @@ "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.16.0" } }, "node_modules/@types/react": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.3.tgz", - "integrity": "sha512-k5dJVszUiNr1DSe8Cs+knKR6IrqhqdhpUwzqhkS8ecQTSf3THNtbfIp/umqHMpX2bv+9dkx3fwDv/86LcSfvSg==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.4.tgz", + "integrity": "sha512-tBFxBp9Nfyy5rsmefN+WXc1JeW/j2BpBHFdLZbEVfs9wn3E3NRFxwV0pJg8M1qQAexFpvz73hJXFofV0ZAu92A==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -1878,6 +1881,7 @@ "integrity": "sha512-tK3GPFWbirvNgsNKto+UmB/cRtn6TZfyw0D6IKrW55n6Vbs7KJoZtI//kpTKzE/DUmmnAFD8/Ca46s7Obs92/w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.4", "@typescript-eslint/types": "8.46.4", @@ -2136,6 +2140,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2275,9 +2280,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.26", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.26.tgz", - "integrity": "sha512-73lC1ugzwoaWCLJ1LvOgrR5xsMLTqSKIEoMHVtL9E/HNk0PXtTM76ZIm84856/SF7Nv8mPZxKoBsgpm0tR1u1Q==", + "version": "2.8.27", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.27.tgz", + "integrity": "sha512-2CXFpkjVnY2FT+B6GrSYxzYf65BJWEqz5tIRHCvNsZZ2F3CmsCB37h8SpYgKG7y9C4YAeTipIPWG7EmFmhAeXA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -2328,6 +2333,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -2789,6 +2795,7 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -3413,6 +3420,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.27.6" }, @@ -3581,9 +3589,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -4867,6 +4875,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -4956,6 +4965,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -4965,6 +4975,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -4990,9 +5001,9 @@ } }, "node_modules/react-i18next": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.3.0.tgz", - "integrity": "sha512-XGYIVU6gCOL4UQsfp87WbbvBc2WvgdkEDI8r4TwACzFg1bXY8pd1d9Cw6u9WJ2soTKHKaF1xQEyWA3/dUvtAGw==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.3.1.tgz", + "integrity": "sha512-HbYaBeA58Hg38OzdEvJp4kLIvk10rp9F9Jq+wNkqtqxDXObtdYMSsQnegWgdUVcpZjZuK9ZxehM+Z9BW2Vqgqw==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.27.6", @@ -5412,6 +5423,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -5490,6 +5502,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5700,6 +5713,7 @@ "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -5793,6 +5807,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, diff --git a/frontend/src/services/api.ts b/frontend/src/services/api.ts index ccf5885..5b05088 100644 --- a/frontend/src/services/api.ts +++ b/frontend/src/services/api.ts @@ -19,10 +19,10 @@ import type { /** * API Client Configuration * - In Docker: VITE_API_BASE_URL is empty string, use relative path - * - In development: Use VITE_API_BASE_URL from .env or default to localhost:12015 + * - In development: Use VITE_API_BASE_URL from .env or default to localhost:8000 */ const envApiBaseUrl = import.meta.env.VITE_API_BASE_URL -const API_BASE_URL = envApiBaseUrl !== undefined ? envApiBaseUrl : 'http://localhost:12015' +const API_BASE_URL = envApiBaseUrl !== undefined ? envApiBaseUrl : 'http://localhost:8000' const API_VERSION = 'v1' class ApiClient { diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 3b52191..c9e87cf 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -6,10 +6,10 @@ import path from 'path' export default defineConfig({ plugins: [react()], server: { - port: 12011, + port: 5173, proxy: { '/api': { - target: 'http://localhost:12015', + target: 'http://localhost:8000', changeOrigin: true, }, }, diff --git a/openspec/project.md b/openspec/project.md index bcd0eaf..38ce803 100644 --- a/openspec/project.md +++ b/openspec/project.md @@ -14,10 +14,11 @@ Tool_OCR is a web-based application for batch image-to-text conversion with mult ## Tech Stack ### Development Environment -- **OS Platform**: Windows 10/11 -- **Python Version**: 3.10 (via Conda) -- **Environment Manager**: Conda -- **Virtual Environment Path**: `C:\Users\lin46\.conda\envs\tool_ocr` +- **OS Platform**: WSL2 Ubuntu 24.04 +- **Python Version**: 3.12 +- **Environment Manager**: Python venv +- **Virtual Environment Path**: `./venv` +- **Node.js**: 24.x LTS (via nvm) - **IDE Recommended**: VS Code with Python + React extensions ### Backend Technologies @@ -74,11 +75,15 @@ Tool_OCR is a web-based application for batch image-to-text conversion with mult ### Environment Setup (Backend) ```bash -# Create new conda environment -conda create -n tool_ocr python=3.10 -y +# Run automated setup script (recommended) +./setup_dev_env.sh + +# Or manually: +# Create Python virtual environment +python3 -m venv venv # Activate environment -conda activate tool_ocr +source venv/bin/activate # Install dependencies pip install -r requirements.txt diff --git a/setup_conda.sh b/setup_conda.sh deleted file mode 100755 index cb8e2b8..0000000 --- a/setup_conda.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash -# Tool_OCR - Conda 環境設置腳本 (macOS Apple Silicon) - -set -e # 遇到錯誤立即退出 - -echo "===================================" -echo "Tool_OCR - 環境設置" -echo "===================================" - -# 檢查 Conda 是否已安裝 -if command -v conda &> /dev/null; then - echo "✓ Conda 已安裝: $(conda --version)" -else - echo "📦 開始安裝 Miniconda..." - - # 下載 Miniconda for Apple Silicon - MINICONDA_URL="https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-arm64.sh" - INSTALLER="/tmp/miniconda_installer.sh" - - echo "下載 Miniconda..." - curl -o "$INSTALLER" "$MINICONDA_URL" - - echo "安裝 Miniconda (默認安裝到 ~/miniconda3)..." - bash "$INSTALLER" -b -p "$HOME/miniconda3" - - # 初始化 Conda - echo "初始化 Conda..." - "$HOME/miniconda3/bin/conda" init zsh bash - - # 清理安裝檔案 - rm "$INSTALLER" - - echo "✓ Miniconda 安裝完成!" - echo "" - echo "⚠️ 請執行以下命令以載入 Conda:" - echo " source ~/.zshrc (如果使用 zsh)" - echo " source ~/.bash_profile (如果使用 bash)" - echo "" - echo "然後重新執行此腳本繼續設置。" - exit 0 -fi - -# 檢查是否在 base 環境 -CURRENT_ENV=$(conda info --envs | grep '*' | awk '{print $1}') -echo "當前 Conda 環境: $CURRENT_ENV" - -# 創建 tool_ocr 環境 -ENV_NAME="tool_ocr" -if conda env list | grep -q "^$ENV_NAME "; then - echo "✓ 環境 '$ENV_NAME' 已存在" - read -p "是否重新創建? (y/N): " -n 1 -r - echo - if [[ $REPLY =~ ^[Yy]$ ]]; then - echo "移除現有環境..." - conda env remove -n "$ENV_NAME" -y - else - echo "使用現有環境" - exit 0 - fi -fi - -echo "📦 創建 Conda 環境: $ENV_NAME (Python 3.10)..." -conda create -n "$ENV_NAME" python=3.10 -y - -echo "" -echo "✅ Conda 環境設置完成!" -echo "" -echo "下一步:" -echo " 1. 啟動環境: conda activate $ENV_NAME" -echo " 2. 安裝依賴: pip install -r requirements.txt" -echo " 3. 下載 PaddleOCR 模型" -echo "" diff --git a/setup_dev_env.sh b/setup_dev_env.sh new file mode 100755 index 0000000..f4790fc --- /dev/null +++ b/setup_dev_env.sh @@ -0,0 +1,165 @@ +#!/bin/bash +# Tool_OCR WSL Ubuntu 開發環境設置腳本 + +set -e # 遇到錯誤時停止 + +echo "================================" +echo "Tool_OCR 開發環境設置" +echo "================================" +echo "" + +# 顏色定義 +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +# 檢查是否為 root +if [ "$EUID" -eq 0 ]; then + echo -e "${RED}請不要使用 sudo 運行此腳本${NC}" + echo "腳本會在需要時提示輸入 sudo 密碼" + exit 1 +fi + +echo -e "${YELLOW}[1/8] 更新系統套件列表...${NC}" +sudo apt update + +echo "" +echo -e "${YELLOW}[2/8] 安裝 Python 開發工具...${NC}" +sudo apt install -y \ + python3-pip \ + python3-venv \ + python3-dev \ + build-essential \ + pkg-config + +echo "" +echo -e "${YELLOW}[3/8] 安裝系統層級依賴...${NC}" +sudo apt install -y \ + pandoc \ + libmagic1 \ + libmagic-dev \ + fonts-noto-cjk \ + fonts-noto-cjk-extra \ + fonts-liberation \ + libpango-1.0-0 \ + libpangocairo-1.0-0 \ + libcairo2 \ + libcairo2-dev \ + libgdk-pixbuf2.0-0 \ + libgdk-pixbuf-2.0-dev \ + libffi-dev \ + libffi8 \ + shared-mime-info \ + poppler-utils \ + libgl1 \ + libglib2.0-0 \ + libglib2.0-dev \ + libgomp1 \ + libjpeg-dev \ + libpng-dev \ + libtiff-dev \ + libopencv-dev \ + libsqlite3-dev \ + libreoffice-core-nogui \ + libreoffice-writer-nogui \ + libreoffice-impress-nogui \ + ca-certificates \ + curl \ + wget \ + libxml2 \ + libxslt1-dev \ + python3-cffi + +echo "" +echo -e "${YELLOW}[4/8] 安裝 Node.js 和 npm...${NC}" +# 檢查是否已安裝 nvm +if [ ! -d "$HOME/.nvm" ]; then + echo "安裝 nvm..." + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash + + # 載入 nvm + export NVM_DIR="$HOME/.nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" +else + echo "nvm 已安裝" + export NVM_DIR="$HOME/.nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" +fi + +# 安裝 Node.js LTS +echo "安裝 Node.js LTS..." +nvm install --lts +nvm use --lts + +echo "" +echo -e "${YELLOW}[5/8] 創建 Python 虛擬環境...${NC}" +if [ ! -d "venv" ]; then + python3 -m venv venv + echo "虛擬環境已創建" +else + echo "虛擬環境已存在" +fi + +echo "" +echo -e "${YELLOW}[6/8] 安裝 Python 依賴...${NC}" +source venv/bin/activate +pip install --upgrade pip setuptools wheel +pip install -r requirements.txt + +echo "" +echo -e "${YELLOW}測試關鍵套件...${NC}" +python -c "import magic; print('✓ python-magic')" || echo "✗ python-magic failed" +python -c "from weasyprint import HTML; print('✓ WeasyPrint')" || echo "✗ WeasyPrint failed" +python -c "import cv2; print('✓ OpenCV')" || echo "✗ OpenCV failed" + +echo "" +echo -e "${YELLOW}[7/8] 安裝前端依賴...${NC}" +cd frontend + +# 清理可能存在的鎖定文件 +if [ -d "node_modules" ]; then + echo "清理現有 node_modules..." + rm -rf node_modules package-lock.json +fi + +# 清理 npm 緩存 +npm cache clean --force + +# 安裝依賴(使用 --force 避免鎖定問題) +echo "安裝前端依賴..." +npm install --force + +cd .. + +echo "" +echo -e "${YELLOW}[8/8] 創建必要的目錄...${NC}" +mkdir -p backend/uploads/{temp,processed,images} +mkdir -p backend/storage/{markdown,json,exports} +mkdir -p backend/models/paddleocr +mkdir -p backend/logs + +echo "" +echo -e "${GREEN}================================${NC}" +echo -e "${GREEN}環境設置完成!${NC}" +echo -e "${GREEN}================================${NC}" +echo "" +echo "下一步操作:" +echo "1. 初始化數據庫:" +echo " source venv/bin/activate" +echo " cd backend" +echo " alembic upgrade head" +echo " python create_test_user.py" +echo " cd .." +echo "" +echo "2. 啟動後端:" +echo " ./start_backend.sh" +echo "" +echo "3. 啟動前端 (新終端):" +echo " ./start_frontend.sh" +echo "" +echo "4. 訪問應用:" +echo " 前端: http://localhost:5173" +echo " API文檔: http://localhost:8000/docs" +echo " 健康檢查: http://localhost:8000/health" +echo "" diff --git a/start_backend.sh b/start_backend.sh new file mode 100755 index 0000000..f4dde24 --- /dev/null +++ b/start_backend.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# Tool_OCR - 後端開發服務器啟動腳本 + +set -e + +# 顏色定義 +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +echo -e "${YELLOW}正在啟動 Tool_OCR 後端開發服務器...${NC}" +echo "" + +# 檢查虛擬環境 +if [ ! -d "venv" ]; then + echo -e "${RED}錯誤: 未找到虛擬環境${NC}" + echo "請先運行: ./setup_dev_env.sh" + exit 1 +fi + +# 檢查 .env.local +if [ ! -f ".env.local" ]; then + echo -e "${RED}錯誤: 未找到 .env.local 配置文件${NC}" + echo "請確保 .env.local 文件存在" + exit 1 +fi + +# 啟動虛擬環境 +echo -e "${GREEN}啟動 Python 虛擬環境...${NC}" +source venv/bin/activate + +# 載入環境變量 +echo -e "${GREEN}載入環境變量...${NC}" +export $(cat .env.local | grep -v '^#' | xargs) + +# 進入後端目錄 +cd backend + +# 檢查必要的目錄 +echo -e "${GREEN}檢查目錄結構...${NC}" +mkdir -p uploads/{temp,processed,images} +mkdir -p storage/{markdown,json,exports} +mkdir -p models/paddleocr +mkdir -p logs + +# 啟動後端服務器 +echo "" +echo -e "${GREEN}================================${NC}" +echo -e "${GREEN}後端服務器啟動中...${NC}" +echo -e "${GREEN}================================${NC}" +echo "" +echo "API 文檔: http://localhost:8000/docs" +echo "健康檢查: http://localhost:8000/health" +echo "" +echo "按 Ctrl+C 停止服務器" +echo "" + +uvicorn app.main:app --reload --host 0.0.0.0 --port 8000 diff --git a/start_frontend.sh b/start_frontend.sh new file mode 100755 index 0000000..2a18672 --- /dev/null +++ b/start_frontend.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# Tool_OCR - 前端開發服務器啟動腳本 + +set -e + +# 顏色定義 +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +echo -e "${YELLOW}正在啟動 Tool_OCR 前端開發服務器...${NC}" +echo "" + +# 檢查 node_modules +if [ ! -d "frontend/node_modules" ]; then + echo -e "${RED}錯誤: 未找到 node_modules${NC}" + echo "請先運行: ./setup_dev_env.sh" + exit 1 +fi + +# 載入 nvm +export NVM_DIR="$HOME/.nvm" +[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + +# 進入前端目錄 +cd frontend + +# 啟動前端服務器 +echo "" +echo -e "${GREEN}================================${NC}" +echo -e "${GREEN}前端服務器啟動中...${NC}" +echo -e "${GREEN}================================${NC}" +echo "" +echo "前端界面: http://localhost:5173" +echo "" +echo "按 Ctrl+C 停止服務器" +echo "" + +npm run dev