Files
clio/backend/app/routers/documents.py
2026-02-10 23:05:41 +00:00

144 lines
5.3 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func, desc
from typing import Optional
import base64
import os
from app.database import get_db
from app.models.user import User
from app.models.document import Document
from app.schemas.document import ScanRequest, DocumentResponse, DocumentListResponse
from app.services.ai_service import analyze_document
from app.utils.security import get_current_user
from app.config import settings
router = APIRouter(prefix="/api/documents", tags=["documents"])
@router.post("/scan", response_model=DocumentResponse)
async def scan_document(
req: ScanRequest,
user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db)
):
# Check scan limit for free users
if user.plan == "free" and user.scan_count_today >= settings.FREE_SCAN_LIMIT:
raise HTTPException(status_code=429, detail="Limite de scans diários atingido. Faça upgrade para Premium.")
# Calculate file size
image_data = req.image
if "," in image_data:
image_data_clean = image_data.split(",", 1)[1]
else:
image_data_clean = image_data
file_size = len(base64.b64decode(image_data_clean))
# AI analysis
try:
result = await analyze_document(req.image)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Erro na análise IA: {str(e)}")
# Save document
doc = Document(
user_id=user.id,
title=result.get("title", "Documento sem título"),
category=result.get("category", "outro"),
original_image=req.image,
extracted_text=result.get("extracted_text", ""),
summary=result.get("summary", ""),
extracted_data=result.get("extracted_data", {}),
risk_alerts=result.get("risk_alerts", []),
tags=result.get("tags", []),
file_size=file_size
)
db.add(doc)
# Update scan count
user.scan_count_today += 1
await db.commit()
await db.refresh(doc)
return DocumentResponse(
id=doc.id, title=doc.title, category=doc.category,
extracted_text=doc.extracted_text, summary=doc.summary,
extracted_data=doc.extracted_data, risk_alerts=doc.risk_alerts,
tags=doc.tags, file_size=doc.file_size, created_at=doc.created_at
)
@router.get("/", response_model=DocumentListResponse)
async def list_documents(
search: Optional[str] = None,
category: Optional[str] = None,
page: int = Query(1, ge=1),
limit: int = Query(20, ge=1, le=100),
user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db)
):
query = select(Document).where(Document.user_id == user.id)
count_query = select(func.count(Document.id)).where(Document.user_id == user.id)
if category:
query = query.where(Document.category == category)
count_query = count_query.where(Document.category == category)
if search:
search_filter = Document.extracted_text.ilike(f"%{search}%")
query = query.where(search_filter)
count_query = count_query.where(search_filter)
total = (await db.execute(count_query)).scalar()
result = await db.execute(query.order_by(desc(Document.created_at)).offset((page-1)*limit).limit(limit))
docs = result.scalars().all()
return DocumentListResponse(
documents=[DocumentResponse(
id=d.id, title=d.title, category=d.category,
extracted_text=d.extracted_text, summary=d.summary,
extracted_data=d.extracted_data, risk_alerts=d.risk_alerts,
tags=d.tags, file_size=d.file_size, created_at=d.created_at
) for d in docs],
total=total
)
@router.get("/{doc_id}", response_model=DocumentResponse)
async def get_document(
doc_id: int,
user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db)
):
result = await db.execute(select(Document).where(Document.id == doc_id, Document.user_id == user.id))
doc = result.scalar_one_or_none()
if not doc:
raise HTTPException(status_code=404, detail="Documento não encontrado")
return DocumentResponse(
id=doc.id, title=doc.title, category=doc.category,
extracted_text=doc.extracted_text, summary=doc.summary,
extracted_data=doc.extracted_data, risk_alerts=doc.risk_alerts,
tags=doc.tags, file_size=doc.file_size, created_at=doc.created_at
)
@router.get("/{doc_id}/image")
async def get_document_image(
doc_id: int,
user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db)
):
result = await db.execute(select(Document).where(Document.id == doc_id, Document.user_id == user.id))
doc = result.scalar_one_or_none()
if not doc:
raise HTTPException(status_code=404, detail="Documento não encontrado")
return {"image": doc.original_image}
@router.delete("/{doc_id}")
async def delete_document(
doc_id: int,
user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db)
):
result = await db.execute(select(Document).where(Document.id == doc_id, Document.user_id == user.id))
doc = result.scalar_one_or_none()
if not doc:
raise HTTPException(status_code=404, detail="Documento não encontrado")
await db.delete(doc)
await db.commit()
return {"message": "Documento excluído"}