feat: Docker化部署 - 單容器架構轉換

將 Tool_OCR 從 macOS conda 環境轉換為 Docker 單容器部署方案。
前後端整合於同一容器,通過 Nginx 反向代理,僅對外暴露單一端口。

## 新增功能
- Docker 單容器架構(Frontend + Backend + Nginx)
- 多階段構建優化鏡像大小
- Supervisor 進程管理
- 健康檢查機制
- 完整部署文檔

## 技術細節
- 對外端口:12015(原 12010 已被佔用)
- 內部架構:Nginx(12015) → FastAPI(8000)
- 前端靜態文件由 Nginx 直接服務
- API 請求通過 Nginx 反向代理

## 系統依賴完善
- libmagic1:文件類型檢測
- LibreOffice:Office 文檔轉換
- paddlex[ocr]:PP-StructureV3 版面分析
- 中日韓字體支援

## 配置調整
- 環境變數路徑:macOS 路徑 → 容器絕對路徑
- 前端 API URL:修正為統一端口 12015
- Pip 安裝:延長超時至 600 秒,重試 5 次
- CRLF 轉換:自動處理 Windows 換行符

## 清理
- 移除臨時文檔(API_FIX_SUMMARY.md 等 7 個文檔)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
beabigegg
2025-11-13 13:12:59 +08:00
parent 57cf91271c
commit 0f81d5e70b
26 changed files with 1166 additions and 2985 deletions

89
docker/default.conf Normal file
View File

@@ -0,0 +1,89 @@
# 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;
}
}

41
docker/entrypoint.sh Normal file
View File

@@ -0,0 +1,41 @@
#!/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

40
docker/nginx.conf Normal file
View File

@@ -0,0 +1,40 @@
# 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;
}

28
docker/supervisord.conf Normal file
View File

@@ -0,0 +1,28 @@
[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"