""" 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