ganesh-vilje's picture
Fix permission denied error by saving temp files to /tmp
3a10c0d
Raw
History Blame Contribute Delete
4.64 kB
from fastapi import FastAPI, HTTPException, UploadFile, File, BackgroundTasks
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from pydantic import BaseModel
import os
import uuid
import subprocess
import logging
# Initialize FastAPI
app = FastAPI(
title="HF Utility API",
description="Fast APIs for Embedding and Docx to PDF conversion"
)
# CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Initialize the embedding model globally
try:
from sentence_transformers import SentenceTransformer
# Load model. Using a small, fast model for <1s inference.
embedding_model = SentenceTransformer("all-MiniLM-L6-v2")
logger.info("SentenceTransformer model loaded successfully.")
except Exception as e:
logger.error(f"Failed to load embedding model: {e}")
embedding_model = None
class EmbedRequest(BaseModel):
text: str
@app.post("/api/embed")
async def embed_text(req: EmbedRequest):
if not embedding_model:
raise HTTPException(status_code=500, detail="Embedding model not initialized.")
try:
# Generate embeddings
embedding = embedding_model.encode(req.text).tolist()
return {"embedding": embedding}
except Exception as e:
logger.error(f"Embedding error: {e}")
raise HTTPException(status_code=500, detail=str(e))
def cleanup_files(*file_paths):
for path in file_paths:
if path and os.path.exists(path):
try:
os.remove(path)
logger.info(f"Cleaned up {path}")
except Exception as e:
logger.error(f"Failed to clean up {path}: {e}")
@app.post("/api/convert-docx")
async def convert_docx(background_tasks: BackgroundTasks, file: UploadFile = File(...)):
if not file.filename.endswith(".docx"):
raise HTTPException(status_code=400, detail="Only .docx files are supported.")
# Generate unique IDs for the files to avoid collisions
job_id = str(uuid.uuid4())
upload_dir = "/tmp/hf_api_uploads"
# Ensure dir exists
os.makedirs(upload_dir, exist_ok=True)
# sanitize filename a bit
safe_filename = file.filename.replace(" ", "_")
input_path = os.path.join(upload_dir, f"{job_id}_{safe_filename}")
output_filename = f"{job_id}_{safe_filename.replace('.docx', '.pdf')}"
output_path = os.path.join(upload_dir, output_filename)
try:
# Save uploaded file
with open(input_path, "wb") as f:
content = await file.read()
f.write(content)
logger.info(f"Saved docx to {input_path}. Starting conversion...")
# Run LibreOffice headless conversion
command = [
"libreoffice",
"--headless",
"--convert-to", "pdf",
"--outdir", upload_dir,
input_path
]
# Execute the command
process = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if process.returncode != 0:
logger.error(f"LibreOffice error: {process.stderr}")
# Clean up the input file on error
cleanup_files(input_path)
raise HTTPException(status_code=500, detail="Failed to convert document.")
logger.info(f"Successfully converted to {output_path}")
# Check if output file was created
if not os.path.exists(output_path):
cleanup_files(input_path)
raise HTTPException(status_code=500, detail="Output PDF not found after conversion.")
# Add cleanup to background tasks so they are deleted AFTER the response is sent
background_tasks.add_task(cleanup_files, input_path, output_path)
return FileResponse(
path=output_path,
filename=safe_filename.replace('.docx', '.pdf'),
media_type="application/pdf"
)
except HTTPException:
raise
except Exception as e:
logger.error(f"Conversion error: {e}")
cleanup_files(input_path, output_path)
raise HTTPException(status_code=500, detail=str(e))
@app.get("/api/health")
async def health_check():
return {"status": "healthy", "embedding_model_loaded": embedding_model is not None}
@app.get("/")
async def root():
return {"message": "HF Utility API is running"}