# /opt/docker/dev/service_finder/backend/app/api/v1/endpoints/documents.py import uuid from typing import Any, Dict from fastapi import APIRouter, Depends, UploadFile, File, Form, BackgroundTasks, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from app.db.session import get_db from app.api.deps import get_current_user from app.services.document_service import DocumentService from app.models.identity import User router = APIRouter() @router.post("/upload/{parent_type}/{parent_id}") async def upload_document( parent_type: str, parent_id: str, background_tasks: BackgroundTasks, doc_type: str = Form(..., description="A dokumentum típusa: 'invoice', 'registration_card', 'sale_contract'"), file: UploadFile = File(...), db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user) ): """ MB 2.0 Dokumentum Pipeline. 1. Ellenőrzi a felhasználó havi OCR kvótáját (Admin 2.0). 2. Optimalizálja és NAS-ra menti a képet (WebP konverzió). 3. Automatikusan elindítja a Robot 1-et (OCR), ha a típus engedélyezett. """ # 1. Bemeneti validáció valid_parents = ["organizations", "assets", "transfers"] if parent_type not in valid_parents: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"Érvénytelen cél-típus! Megengedett: {', '.join(valid_parents)}" ) try: # 2. Feldolgozás a szolgáltatás rétegben # Itt történik a kvóta-ellenőrzés és a Robot 1 triggerelése is doc = await DocumentService.process_upload( db=db, user_id=current_user.id, file=file, parent_type=parent_type, parent_id=parent_id, doc_type=doc_type, background_tasks=background_tasks ) # 3. Válasz összeállítása az állapot alapján response_data = { "document_id": doc.id, "original_name": doc.original_name, "status": doc.status, "thumbnail": doc.thumbnail_path, } if doc.status == "processing": response_data["message"] = "🤖 Robot 1 megkezdte a dokumentum elemzését. Értesítjük, ha kész!" else: response_data["message"] = "Dokumentum sikeresen archiválva a széfben." return response_data except HTTPException as he: # Közvetlenül átengedjük a service-ből jövő (pl. kvóta) hibákat raise he except ValueError as ve: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=str(ve)) except Exception as e: # Sentinel naplózás és általános hiba raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Kritikus hiba a dokumentum feldolgozása során." ) @router.get("/{document_id}/status") async def get_document_status( document_id: uuid.UUID, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user) ): """Lekérdezhető, hogy a robot végzett-e már a feldolgozással.""" # (Itt egy egyszerű lekérdezés a Document táblából a státuszra) pass # RBAC helper function def _check_premium_or_admin(user: User) -> bool: """Check if user has premium subscription or admin role.""" premium_plans = ['PREMIUM', 'PREMIUM_PLUS', 'VIP', 'VIP_PLUS'] if user.role == 'admin': return True if hasattr(user, 'subscription_plan') and user.subscription_plan in premium_plans: return True return False @router.post("/scan-instant") async def scan_instant( file: UploadFile = File(...), db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user) ): """ Szinkron végpont (Villámszkenner) - forgalmi/ID dokumentumokhoz. Azonnali OCR feldolgozás és válasz. RBAC: Csak prémium előfizetés vagy admin. """ # RBAC ellenőrzés if not _check_premium_or_admin(current_user): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Prémium előfizetés szükséges a funkcióhoz" ) try: # 1. Fájl feltöltése MinIO-ba (StorageService segítségével) # Jelenleg mock: feltételezzük, hogy a StorageService.upload_file létezik from app.services.storage_service import StorageService file_url = await StorageService.upload_file(file, prefix="instant_scan") # 2. Mock OCR hívás (valós implementációban AiOcrService-t hívnánk) mock_ocr_result = { "plate": "TEST-123", "vin": "TRX12345", "make": "Toyota", "model": "Corolla", "year": 2022, "fuel_type": "petrol", "engine_capacity": 1600 } # 3. Dokumentum rekord létrehozása system.documents táblában from app.models import Document from datetime import datetime, timezone import uuid doc = Document( id=uuid.uuid4(), user_id=current_user.id, original_name=file.filename, file_path=file_url, file_size=file.size, mime_type=file.content_type, status='processed', ocr_data=mock_ocr_result, created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc) ) db.add(doc) await db.commit() await db.refresh(doc) # 4. Válasz return { "document_id": str(doc.id), "status": "processed", "ocr_result": mock_ocr_result, "file_url": file_url, "message": "Dokumentum sikeresen feldolgozva" } except Exception as e: await db.rollback() raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Hiba a dokumentum feldolgozása során: {str(e)}" ) @router.post("/upload-async") async def upload_async( file: UploadFile = File(...), db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user) ): """ Aszinkron végpont (Költség/Számla nyelő) - háttérben futó OCR-nek. Azonnali 202 Accepted válasz, pending_ocr státusszal. RBAC: Csak prémium előfizetés vagy admin. """ # RBAC ellenőrzés if not _check_premium_or_admin(current_user): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Prémium előfizetés szükséges a funkcióhoz" ) try: # 1. Fájl feltöltése MinIO-ba from app.services.storage_service import StorageService file_url = await StorageService.upload_file(file, prefix="async_upload") # 2. Dokumentum rekord létrehozása pending_ocr státusszal from app.models import Document from datetime import datetime, timezone import uuid doc = Document( id=uuid.uuid4(), user_id=current_user.id, original_name=file.filename, file_path=file_url, file_size=file.size, mime_type=file.content_type, status='pending_ocr', # Fontos: a háttérrobot ezt fogja felvenni created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc) ) db.add(doc) await db.commit() await db.refresh(doc) # 3. 202 Accepted válasz return { "document_id": str(doc.id), "status": "pending_ocr", "message": "A dokumentum feltöltve, háttérben történő elemzése megkezdődött.", "file_url": file_url } except Exception as e: await db.rollback() raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Hiba a dokumentum feltöltése során: {str(e)}" )