feat: migrate to WSL Ubuntu native development environment
從 Docker/macOS+Conda 部署遷移到 WSL2 Ubuntu 原生開發環境 主要變更: - 移除所有 Docker 相關配置檔案 (Dockerfile, docker-compose.yml, .dockerignore 等) - 移除 macOS/Conda 設置腳本 (SETUP.md, setup_conda.sh) - 新增 WSL Ubuntu 自動化環境設置腳本 (setup_dev_env.sh) - 新增後端/前端快速啟動腳本 (start_backend.sh, start_frontend.sh) - 統一開發端口配置 (backend: 8000, frontend: 5173) - 改進資料庫連接穩定性(連接池、超時設置、重試機制) - 更新專案文檔以反映當前 WSL 開發環境 Technical improvements: - Database connection pooling with health checks and auto-reconnection - Retry logic for long-running OCR tasks to prevent DB timeouts - Extended JWT token expiration to 24 hours - Support for Office documents (pptx, docx) via LibreOffice headless - Comprehensive system dependency installation in single script Environment: - OS: WSL2 Ubuntu 24.04 - Python: 3.12 (venv) - Node.js: 24.x LTS (nvm) - Backend Port: 8000 - Frontend Port: 5173 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user