The local file-parsing pipeline (upload -> Huey async parse -> generate projects/data/.../document.txt) is no longer needed: RAG retrieval runs against the backend vector store and does not read the local parse output, so removing this has zero impact on existing bot Q&A. - Delete task_queue/ (Huey queue, consumer, tasks, task status store) - Delete parsing utils: dataset_manager, single_file_processor, data_merger, project_manager - Delete db_manager.py (only managed task_status.db) - routes/files.py: keep only POST /api/v1/upload; drop all parse/queue/task endpoints - routes/projects.py: drop /tasks endpoint and task_status import - utils/__init__.py & api_models.py: remove exports/models for deleted modules and queue task models - start_unified.py & start_all_optimized.sh: no longer launch the queue consumer - Drop huey dependency (keep redis) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
96 lines
3.3 KiB
Python
96 lines
3.3 KiB
Python
import os
|
|
import uuid
|
|
import shutil
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
from fastapi import APIRouter, HTTPException, UploadFile, File, Form
|
|
import logging
|
|
|
|
logger = logging.getLogger('app')
|
|
|
|
from utils.fastapi_utils import get_versioned_filename
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.post("/api/v1/upload")
|
|
async def upload_file(file: UploadFile = File(...), folder: Optional[str] = Form(None)):
|
|
"""
|
|
File upload API endpoint that uploads files to the ./projects/uploads/ directory.
|
|
|
|
You can specify a custom folder name. If omitted, a date-based folder is used.
|
|
When a folder is specified, the original filename is used and version control is supported.
|
|
|
|
Args:
|
|
file: Uploaded file
|
|
folder: Optional custom folder name
|
|
|
|
Returns:
|
|
dict: Response containing file path and folder information
|
|
"""
|
|
try:
|
|
# Debug information
|
|
logger.info(f"Received folder parameter: {folder}")
|
|
logger.info(f"File received: {file.filename if file else 'None'}")
|
|
|
|
# Determine the upload folder
|
|
if folder:
|
|
# Use the specified custom folder
|
|
target_folder = folder
|
|
target_folder = os.path.basename(target_folder)
|
|
else:
|
|
# Get the current date and format it as YYYYMMDD
|
|
current_date = datetime.now()
|
|
target_folder = current_date.strftime("%Y%m%d")
|
|
# Create upload directory
|
|
upload_dir = os.path.join("projects", "uploads", target_folder)
|
|
os.makedirs(upload_dir, exist_ok=True)
|
|
|
|
# Process filename
|
|
if not file.filename:
|
|
raise HTTPException(status_code=400, detail="Filename cannot be empty")
|
|
|
|
# Parse filename and extension
|
|
original_filename = file.filename
|
|
name_without_ext, file_extension = os.path.splitext(original_filename)
|
|
|
|
# Choose naming strategy based on whether a folder is specified
|
|
if folder:
|
|
final_filename, version = get_versioned_filename(upload_dir, name_without_ext, file_extension)
|
|
file_path = os.path.join(upload_dir, final_filename)
|
|
|
|
# Save file
|
|
with open(file_path, "wb") as buffer:
|
|
shutil.copyfileobj(file.file, buffer)
|
|
|
|
return {
|
|
"success": True,
|
|
"message": f"File uploaded successfully{' (version: ' + str(version) + ')' if version > 1 else ''}",
|
|
"file_path": file_path,
|
|
"folder": target_folder,
|
|
"original_filename": original_filename,
|
|
"version": version
|
|
}
|
|
else:
|
|
# Use UUID unique filename (original logic)
|
|
unique_filename = f"{uuid.uuid4()}{file_extension}"
|
|
file_path = os.path.join(upload_dir, unique_filename)
|
|
|
|
# Save file
|
|
with open(file_path, "wb") as buffer:
|
|
shutil.copyfileobj(file.file, buffer)
|
|
|
|
return {
|
|
"success": True,
|
|
"message": "File uploaded successfully",
|
|
"file_path": file_path,
|
|
"folder": target_folder,
|
|
"original_filename": original_filename
|
|
}
|
|
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Error uploading file: {str(e)}")
|
|
raise HTTPException(status_code=500, detail=f"File upload failed: {str(e)}")
|