""" Linux BenchTools - Documents API """ import os import hashlib from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File, Form from fastapi.responses import FileResponse from sqlalchemy.orm import Session from typing import List from datetime import datetime from app.db.session import get_db from app.core.config import settings from app.schemas.document import DocumentResponse from app.models.document import Document from app.models.device import Device router = APIRouter() def generate_file_hash(content: bytes) -> str: """Generate a unique hash for file storage""" return hashlib.sha256(content).hexdigest()[:16] @router.get("/devices/{device_id}/docs", response_model=List[DocumentResponse]) async def get_device_documents( device_id: int, db: Session = Depends(get_db) ): """Get all documents for a device""" device = db.query(Device).filter(Device.id == device_id).first() if not device: raise HTTPException(status_code=404, detail="Device not found") docs = db.query(Document).filter(Document.device_id == device_id).all() return [ DocumentResponse( id=doc.id, device_id=doc.device_id, doc_type=doc.doc_type, filename=doc.filename, mime_type=doc.mime_type, size_bytes=doc.size_bytes, uploaded_at=doc.uploaded_at.isoformat() ) for doc in docs ] @router.post("/devices/{device_id}/docs", response_model=DocumentResponse, status_code=status.HTTP_201_CREATED) async def upload_document( device_id: int, file: UploadFile = File(...), doc_type: str = Form(...), db: Session = Depends(get_db) ): """Upload a document for a device""" device = db.query(Device).filter(Device.id == device_id).first() if not device: raise HTTPException(status_code=404, detail="Device not found") # Read file content content = await file.read() file_size = len(content) # Check file size if file_size > settings.MAX_UPLOAD_SIZE: raise HTTPException( status_code=413, detail=f"File too large. Maximum size: {settings.MAX_UPLOAD_SIZE} bytes" ) # Generate unique filename file_hash = generate_file_hash(content) ext = os.path.splitext(file.filename)[1] stored_filename = f"{file_hash}_{device_id}{ext}" stored_path = os.path.join(settings.UPLOAD_DIR, stored_filename) # Ensure upload directory exists os.makedirs(settings.UPLOAD_DIR, exist_ok=True) # Save file with open(stored_path, "wb") as f: f.write(content) # Create database record doc = Document( device_id=device_id, doc_type=doc_type, filename=file.filename, stored_path=stored_path, mime_type=file.content_type or "application/octet-stream", size_bytes=file_size, uploaded_at=datetime.utcnow() ) db.add(doc) db.commit() db.refresh(doc) return DocumentResponse( id=doc.id, device_id=doc.device_id, doc_type=doc.doc_type, filename=doc.filename, mime_type=doc.mime_type, size_bytes=doc.size_bytes, uploaded_at=doc.uploaded_at.isoformat() ) @router.get("/docs/{doc_id}/download") async def download_document( doc_id: int, db: Session = Depends(get_db) ): """Download a document""" doc = db.query(Document).filter(Document.id == doc_id).first() if not doc: raise HTTPException(status_code=404, detail="Document not found") if not os.path.exists(doc.stored_path): raise HTTPException(status_code=404, detail="File not found on disk") return FileResponse( path=doc.stored_path, filename=doc.filename, media_type=doc.mime_type ) @router.delete("/docs/{doc_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_document( doc_id: int, db: Session = Depends(get_db) ): """Delete a document""" doc = db.query(Document).filter(Document.id == doc_id).first() if not doc: raise HTTPException(status_code=404, detail="Document not found") # Delete file from disk if os.path.exists(doc.stored_path): os.remove(doc.stored_path) # Delete from database db.delete(doc) db.commit() return None