ge-tool/backend/routes/raw_api_routes.py
2025-12-10 13:41:43 +07:00

252 lines
8.1 KiB
Python
Executable File

"""
Raw Download Routes - API Mode
Handles NAS FileStation API downloads (với OTP authentication).
"""
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from typing import List, Dict, Optional
import logging
from ..services import mongodb_service, nas_service, supabase_service, downloads_service
from ..common import get_download_destination_path
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/raw-files", tags=["Raw API"])
# ==================== REQUEST MODELS ====================
class RawFileListRequest(BaseModel):
ge_id: str
lang: str
class OTPAuthRequest(BaseModel):
otp_code: str
class RawFileDownloadRequest(BaseModel):
files: List[Dict] # List of file objects with name, path, isdir
ge_id: str
lang: str
class RawFolderListRequest(BaseModel):
folder_path: str
ge_id: str # For context, though not strictly needed for folder listing
lang: str
# ==================== FILE LISTING ENDPOINTS ====================
@router.post('/list')
def list_raw_files(payload: RawFileListRequest):
"""
List raw files for a given GE ID and language.
Returns one of:
- success: List of files/folders
- otp_required: Session expired, OTP needed
- error: Various errors (no path found, NAS connection failed, etc.)
"""
try:
# Get path from MongoDB only (API mode)
nas_path = mongodb_service.get_path_from_tms_data(
payload.ge_id, payload.lang)
if not nas_path:
return {
"status": "error",
"message": f"Không tìm thấy đường dẫn cho GE ID {payload.ge_id} (Lang: {payload.lang})"
}
# Try to list files from NAS
status, files, message = nas_service.get_files_for_path(nas_path)
if status == "success":
return {
"status": "success",
"files": files,
"path": nas_path,
"message": message
}
elif status == "otp_required":
return {
"status": "otp_required",
"message": message
}
else: # error
# Append path to error message for context
enhanced_message = message
if message and "Thư mục không tồn tại" in message:
enhanced_message = f"{message}\n(Đường dẫn từ MongoDB: {nas_path})"
return {
"status": "error",
"message": enhanced_message
}
except Exception as e:
logger.error(f"❌ [list_raw_files] Exception: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"Lỗi hệ thống: {e}")
@router.post('/list-folder')
def list_folder_contents(payload: RawFolderListRequest):
"""
List contents of a specific folder in NAS.
Used for navigation when user double-clicks a folder.
"""
try:
status, files, message = nas_service.get_files_for_path(
payload.folder_path)
if status == "success":
return {
"status": "success",
"files": files,
"path": payload.folder_path,
"message": message
}
elif status == "otp_required":
return {
"status": "otp_required",
"message": message
}
else: # error
return {
"status": "error",
"message": message
}
except Exception as e:
logger.error(f"Error in list_folder_contents: {e}")
raise HTTPException(status_code=500, detail=f"Lỗi hệ thống: {e}")
# ==================== AUTHENTICATION ENDPOINTS ====================
@router.post('/auth-otp')
def authenticate_otp(payload: OTPAuthRequest):
"""
Authenticate with OTP to establish NAS session.
Returns success or error status.
"""
try:
status, message = nas_service.authenticate_with_otp(payload.otp_code)
if status == "success":
return {
"status": "success",
"message": message
}
else: # error
return {
"status": "error",
"message": message
}
except Exception as e:
logger.error(f"Error in authenticate_otp: {e}")
raise HTTPException(status_code=500, detail=f"Lỗi hệ thống: {e}")
# ==================== DOWNLOAD ENDPOINTS ====================
@router.post('/download')
def download_raw_files(payload: RawFileDownloadRequest):
"""
Create a download job in the queue.
Returns job_id immediately for client to poll status.
Background worker will process the actual download.
"""
try:
logger.debug(
f"Creating download job: {payload.ge_id} {payload.lang}, {len(payload.files)} files")
if not payload.files:
return {
"status": "error",
"message": "Không có file nào được chọn để tải xuống"
}
if not payload.ge_id or not payload.lang:
return {
"status": "error",
"message": "GE ID và Lang là bắt buộc"
}
# Calculate destination path using helper
destination_path = get_download_destination_path(
payload.ge_id, payload.lang)
# Get MongoDB path from first file
mongodb_path = None
if payload.files and len(payload.files) > 0:
first_file_path = payload.files[0].get('path', '')
if first_file_path:
# Extract parent directory from path (remove filename)
mongodb_path = '/'.join(first_file_path.split('/')
[:-1]) if '/' in first_file_path else first_file_path
# ✅ FIX: Use downloads_service (NEW) instead of supabase_service (OLD)
result = downloads_service.create_downloads_batch(
files=payload.files,
ge_id=payload.ge_id,
lang=payload.lang,
mode='api', # FileStation API mode
mongodb_path=mongodb_path,
destination_path=destination_path
)
if not result['success']:
return {
"status": "error",
"message": result.get('message', 'Không thể tạo batch downloads')
}
logger.debug(
f"Created downloads batch: {result['batch_id']} ({result['file_count']} files)")
return {
"status": "pending",
"message": "Batch đã được tạo và đang chờ xử lý",
"batch_id": result['batch_id'],
"download_ids": result['download_ids'],
"file_count": result['file_count'],
"mongodb_path": mongodb_path,
"destination_path": destination_path
}
except Exception as e:
logger.error(f"Error creating download job: {e}")
raise HTTPException(status_code=500, detail=f"Lỗi hệ thống: {e}")
@router.get('/download-status/{batch_id}')
def get_download_status(batch_id: str):
"""
Get the status of a download batch.
Returns batch summary with all files' status.
"""
try:
summary = downloads_service.get_batch_summary(batch_id)
if not summary:
raise HTTPException(status_code=404, detail="Batch không tồn tại")
return {
"success": True,
"batch": summary
}
except HTTPException:
raise
except Exception as e:
logger.error(f"Error getting job status: {e}")
raise HTTPException(status_code=500, detail=f"Lỗi hệ thống: {e}")
# ==================== OLD ENDPOINTS REMOVED ====================
# Download management endpoints đã được chuyển sang /api/downloads và /api/batches
# Xem downloads_routes.py để sử dụng API mới