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:
@@ -1,4 +1,4 @@
|
||||
import { Outlet, NavLink, useNavigate } from 'react-router-dom'
|
||||
import { Outlet, NavLink } from 'react-router-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useAuthStore } from '@/store/authStore'
|
||||
import { apiClient } from '@/services/api'
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
|
||||
export default function Layout() {
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
const logout = useAuthStore((state) => state.logout)
|
||||
const user = useAuthStore((state) => state.user)
|
||||
|
||||
|
||||
@@ -264,7 +264,7 @@ export default function ExportPage() {
|
||||
{t('export.options.confidenceThreshold')}
|
||||
</label>
|
||||
<span className="text-sm font-bold text-primary">
|
||||
{(options.confidence_threshold * 100).toFixed(0)}%
|
||||
{((options.confidence_threshold ?? 0.5) * 100).toFixed(0)}%
|
||||
</span>
|
||||
</div>
|
||||
<input
|
||||
@@ -348,7 +348,7 @@ export default function ExportPage() {
|
||||
className="w-full px-4 py-3 border border-border rounded-lg bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-colors"
|
||||
>
|
||||
{cssTemplates.map((template) => (
|
||||
<option key={template.filename} value={template.filename}>
|
||||
<option key={template.name} value={template.name}>
|
||||
{template.name} - {template.description}
|
||||
</option>
|
||||
))}
|
||||
@@ -391,7 +391,7 @@ export default function ExportPage() {
|
||||
<div>
|
||||
<div className="text-xs text-muted-foreground mb-1">準確率門檻</div>
|
||||
<div className="text-sm font-medium text-foreground">
|
||||
{(options.confidence_threshold * 100).toFixed(0)}%
|
||||
{((options.confidence_threshold ?? 0.5) * 100).toFixed(0)}%
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Badge } from '@/components/ui/badge'
|
||||
import { useToast } from '@/components/ui/toast'
|
||||
import { useUploadStore } from '@/store/uploadStore'
|
||||
import { apiClient } from '@/services/api'
|
||||
import { Play, CheckCircle, FileText, AlertCircle, Clock, Activity, Loader2, TrendingUp } from 'lucide-react'
|
||||
import { Play, CheckCircle, FileText, AlertCircle, Clock, Activity, Loader2 } from 'lucide-react'
|
||||
|
||||
export default function ProcessingPage() {
|
||||
const { t } = useTranslation()
|
||||
|
||||
@@ -19,7 +19,7 @@ export default function ResultsPage() {
|
||||
const [selectedFileId, setSelectedFileId] = useState<number | null>(null)
|
||||
|
||||
// Get batch status to show results
|
||||
const { data: batchStatus, isLoading } = useQuery({
|
||||
const { data: batchStatus } = useQuery({
|
||||
queryKey: ['batchStatus', batchId],
|
||||
queryFn: () => apiClient.getBatchStatus(batchId!),
|
||||
enabled: !!batchId,
|
||||
@@ -28,7 +28,7 @@ export default function ResultsPage() {
|
||||
// Get OCR result for selected file
|
||||
const { data: ocrResult, isLoading: isLoadingResult } = useQuery({
|
||||
queryKey: ['ocrResult', selectedFileId],
|
||||
queryFn: () => apiClient.getOCRResult(selectedFileId!.toString()),
|
||||
queryFn: () => apiClient.getOCRResult(selectedFileId!),
|
||||
enabled: !!selectedFileId,
|
||||
})
|
||||
|
||||
|
||||
@@ -13,14 +13,16 @@ import type {
|
||||
CSSTemplate,
|
||||
TranslateRequest,
|
||||
TranslateResponse,
|
||||
TranslationConfig,
|
||||
ApiError,
|
||||
} from '@/types/api'
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:12010'
|
||||
const envApiBaseUrl = import.meta.env.VITE_API_BASE_URL
|
||||
const API_BASE_URL = envApiBaseUrl !== undefined ? envApiBaseUrl : 'http://localhost:12015'
|
||||
const API_VERSION = 'v1'
|
||||
|
||||
class ApiClient {
|
||||
|
||||
Reference in New Issue
Block a user