root, files, prompts running
This commit is contained in:
parent
8c9492715a
commit
fc662fbe59
5 changed files with 265 additions and 131 deletions
|
|
@ -31,27 +31,6 @@ logger = logging.getLogger(__name__)
|
||||||
# Singleton factory for Chat instances with AI service per context
|
# Singleton factory for Chat instances with AI service per context
|
||||||
_chatInterfaces = {}
|
_chatInterfaces = {}
|
||||||
|
|
||||||
# Custom exceptions for file handling
|
|
||||||
class FileError(Exception):
|
|
||||||
"""Base class for file handling exceptions."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
class FileNotFoundError(FileError):
|
|
||||||
"""Exception raised when a file is not found."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
class FileStorageError(FileError):
|
|
||||||
"""Exception raised when there's an error storing a file."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
class FilePermissionError(FileError):
|
|
||||||
"""Exception raised when there's a permission issue with a file."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
class FileDeletionError(FileError):
|
|
||||||
"""Exception raised when there's an error deleting a file."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
class ChatInterface:
|
class ChatInterface:
|
||||||
"""
|
"""
|
||||||
Interface to Chat database and AI Connectors.
|
Interface to Chat database and AI Connectors.
|
||||||
|
|
|
||||||
|
|
@ -323,7 +323,7 @@ class ServiceManagement:
|
||||||
raise PermissionError("No permission to create prompts")
|
raise PermissionError("No permission to create prompts")
|
||||||
|
|
||||||
# Create prompt record
|
# Create prompt record
|
||||||
createdRecord = self.db.recordCreate("prompts", promptData.to_dict())
|
createdRecord = self.db.recordCreate("prompts", promptData)
|
||||||
if not createdRecord or not createdRecord.get("id"):
|
if not createdRecord or not createdRecord.get("id"):
|
||||||
raise ValueError("Failed to create prompt record")
|
raise ValueError("Failed to create prompt record")
|
||||||
|
|
||||||
|
|
@ -369,15 +369,23 @@ class ServiceManagement:
|
||||||
"""Calculates a SHA-256 hash for the file content"""
|
"""Calculates a SHA-256 hash for the file content"""
|
||||||
return hashlib.sha256(fileContent).hexdigest()
|
return hashlib.sha256(fileContent).hexdigest()
|
||||||
|
|
||||||
def checkForDuplicateFile(self, fileHash: str) -> Optional[Dict[str, Any]]:
|
def checkForDuplicateFile(self, fileHash: str) -> Optional[FileItem]:
|
||||||
"""Checks if a file with the same hash already exists for the current user and mandate."""
|
"""Checks if a file with the same hash already exists for the current user and mandate."""
|
||||||
files = self.db.getRecordset("files", recordFilter={
|
files = self.db.getRecordset("files", recordFilter={
|
||||||
"fileHash": fileHash,
|
"fileHash": fileHash,
|
||||||
"mandateId": self.currentUser.get("mandateId"),
|
"mandateId": self.currentUser.mandateId,
|
||||||
"_createdBy": self.currentUser.get("id")
|
"_createdBy": self.currentUser.id
|
||||||
})
|
})
|
||||||
if files:
|
if files:
|
||||||
return files[0]
|
return FileItem(
|
||||||
|
id=files[0]["id"],
|
||||||
|
mandateId=files[0]["mandateId"],
|
||||||
|
filename=files[0]["filename"],
|
||||||
|
mimeType=files[0]["mimeType"],
|
||||||
|
workflowId=files[0]["workflowId"],
|
||||||
|
fileHash=files[0]["fileHash"],
|
||||||
|
fileSize=files[0]["fileSize"]
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def getMimeType(self, filename: str) -> str:
|
def getMimeType(self, filename: str) -> str:
|
||||||
|
|
@ -412,34 +420,85 @@ class ServiceManagement:
|
||||||
|
|
||||||
# File methods - metadata-based operations
|
# File methods - metadata-based operations
|
||||||
|
|
||||||
def getAllFiles(self) -> List[Dict[str, Any]]:
|
def getAllFiles(self) -> List[FileItem]:
|
||||||
"""Returns files based on user access level."""
|
"""Returns files based on user access level."""
|
||||||
allFiles = self.db.getRecordset("files")
|
allFiles = self.db.getRecordset("files")
|
||||||
return self._uam("files", allFiles)
|
filteredFiles = self._uam("files", allFiles)
|
||||||
|
|
||||||
|
# Convert database records to FileItem instances
|
||||||
|
fileItems = []
|
||||||
|
for file in filteredFiles:
|
||||||
|
try:
|
||||||
|
# Get creation date from record or use current time
|
||||||
|
creationDate = file.get("creationDate")
|
||||||
|
if not creationDate:
|
||||||
|
creationDate = datetime.now().isoformat()
|
||||||
|
|
||||||
|
fileItem = FileItem(
|
||||||
|
id=file.get("id"),
|
||||||
|
mandateId=file.get("mandateId"),
|
||||||
|
filename=file.get("filename"),
|
||||||
|
mimeType=file.get("mimeType"),
|
||||||
|
workflowId=file.get("workflowId"),
|
||||||
|
fileHash=file.get("fileHash"),
|
||||||
|
fileSize=file.get("fileSize"),
|
||||||
|
creationDate=creationDate
|
||||||
|
)
|
||||||
|
fileItems.append(fileItem)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Skipping invalid file record: {str(e)}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
return fileItems
|
||||||
|
|
||||||
def getFile(self, fileId: str) -> Optional[Dict[str, Any]]:
|
def getFile(self, fileId: str) -> Optional[FileItem]:
|
||||||
"""Returns a file by ID if user has access."""
|
"""Returns a file by ID if user has access."""
|
||||||
files = self.db.getRecordset("files", recordFilter={"id": fileId})
|
files = self.db.getRecordset("files", recordFilter={"id": fileId})
|
||||||
if not files:
|
if not files:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
filteredFiles = self._uam("files", files)
|
filteredFiles = self._uam("files", files)
|
||||||
return filteredFiles[0] if filteredFiles else None
|
if not filteredFiles:
|
||||||
|
return None
|
||||||
|
|
||||||
|
file = filteredFiles[0]
|
||||||
|
try:
|
||||||
|
# Get creation date from record or use current time
|
||||||
|
creationDate = file.get("creationDate")
|
||||||
|
if not creationDate:
|
||||||
|
creationDate = datetime.now().isoformat()
|
||||||
|
|
||||||
|
return FileItem(
|
||||||
|
id=file.get("id"),
|
||||||
|
mandateId=file.get("mandateId"),
|
||||||
|
filename=file.get("filename"),
|
||||||
|
mimeType=file.get("mimeType"),
|
||||||
|
workflowId=file.get("workflowId"),
|
||||||
|
fileHash=file.get("fileHash"),
|
||||||
|
fileSize=file.get("fileSize"),
|
||||||
|
creationDate=creationDate
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error converting file record: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
def createFile(self, name: str, mimeType: str, size: int = None, fileHash: str = None) -> Dict[str, Any]:
|
def createFile(self, name: str, mimeType: str, size: int = None, fileHash: str = None) -> FileItem:
|
||||||
"""Creates a new file entry if user has permission."""
|
"""Creates a new file entry if user has permission."""
|
||||||
if not self._canModify("files"):
|
if not self._canModify("files"):
|
||||||
raise PermissionError("No permission to create files")
|
raise PermissionError("No permission to create files")
|
||||||
|
|
||||||
fileData = {
|
# Create FileItem instance
|
||||||
"mandateId": self.currentUser.get("mandateId"),
|
fileItem = FileItem(
|
||||||
"name": name,
|
mandateId=self.currentUser.mandateId,
|
||||||
"mimeType": mimeType,
|
filename=name,
|
||||||
"size": size,
|
mimeType=mimeType,
|
||||||
"fileHash": fileHash,
|
fileSize=size,
|
||||||
"creationDate": self._getCurrentTimestamp()
|
fileHash=fileHash
|
||||||
}
|
)
|
||||||
return self.db.recordCreate("files", fileData)
|
|
||||||
|
# Store in database
|
||||||
|
self.db.recordCreate("files", fileItem.to_dict())
|
||||||
|
return fileItem
|
||||||
|
|
||||||
def updateFile(self, fileId: str, updateData: Dict[str, Any]) -> Dict[str, Any]:
|
def updateFile(self, fileId: str, updateData: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Updates file metadata if user has access."""
|
"""Updates file metadata if user has access."""
|
||||||
|
|
@ -467,10 +526,10 @@ class ServiceManagement:
|
||||||
raise PermissionError(f"No permission to delete file {fileId}")
|
raise PermissionError(f"No permission to delete file {fileId}")
|
||||||
|
|
||||||
# Check for other references to this file (by hash)
|
# Check for other references to this file (by hash)
|
||||||
fileHash = file.get("fileHash")
|
fileHash = file.fileHash
|
||||||
if fileHash:
|
if fileHash:
|
||||||
otherReferences = [f for f in self.db.getRecordset("files", recordFilter={"fileHash": fileHash})
|
otherReferences = [f for f in self.db.getRecordset("files", recordFilter={"fileHash": fileHash})
|
||||||
if f.get("id") != fileId]
|
if f["id"] != fileId]
|
||||||
|
|
||||||
# Only delete associated fileData if no other references exist
|
# Only delete associated fileData if no other references exist
|
||||||
if not otherReferences:
|
if not otherReferences:
|
||||||
|
|
@ -507,7 +566,7 @@ class ServiceManagement:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Determine if this is a text-based format
|
# Determine if this is a text-based format
|
||||||
mimeType = file.get("mimeType", "application/octet-stream")
|
mimeType = file.mimeType
|
||||||
isTextFormat = isTextMimeType(mimeType)
|
isTextFormat = isTextMimeType(mimeType)
|
||||||
|
|
||||||
base64Encoded = False
|
base64Encoded = False
|
||||||
|
|
@ -581,6 +640,52 @@ class ServiceManagement:
|
||||||
logger.error(f"Error processing file data for {fileId}: {str(e)}")
|
logger.error(f"Error processing file data for {fileId}: {str(e)}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def getFilePreview(self, fileId: str) -> Optional[Dict[str, Any]]:
|
||||||
|
"""Returns a preview of the file content if user has access."""
|
||||||
|
try:
|
||||||
|
# Get file metadata
|
||||||
|
file = self.getFile(fileId)
|
||||||
|
if not file:
|
||||||
|
logger.warning(f"No access to file ID {fileId}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Get file content
|
||||||
|
fileContent = self.getFileData(fileId)
|
||||||
|
if not fileContent:
|
||||||
|
logger.warning(f"No content found for file ID {fileId}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Determine if content is text based on MIME type
|
||||||
|
isText = file.mimeType.startswith(('text/', 'application/json', 'application/xml', 'application/javascript'))
|
||||||
|
|
||||||
|
# For text content, decode to string
|
||||||
|
if isText:
|
||||||
|
try:
|
||||||
|
content = fileContent.decode('utf-8')
|
||||||
|
encoding = 'utf-8'
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
try:
|
||||||
|
content = fileContent.decode('latin-1')
|
||||||
|
encoding = 'latin-1'
|
||||||
|
except:
|
||||||
|
content = fileContent
|
||||||
|
encoding = None
|
||||||
|
else:
|
||||||
|
content = fileContent
|
||||||
|
encoding = None
|
||||||
|
|
||||||
|
return {
|
||||||
|
"content": content,
|
||||||
|
"mimeType": file.mimeType,
|
||||||
|
"filename": file.filename,
|
||||||
|
"isText": isText,
|
||||||
|
"encoding": encoding,
|
||||||
|
"size": len(fileContent)
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error getting file preview for {fileId}: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
def updateFileData(self, fileId: str, data: Union[bytes, str]) -> bool:
|
def updateFileData(self, fileId: str, data: Union[bytes, str]) -> bool:
|
||||||
"""Updates file data if user has access."""
|
"""Updates file data if user has access."""
|
||||||
# Check file access
|
# Check file access
|
||||||
|
|
@ -597,7 +702,7 @@ class ServiceManagement:
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
# Determine if this is a text-based format
|
# Determine if this is a text-based format
|
||||||
mimeType = file.get("mimeType", "application/octet-stream")
|
mimeType = file.mimeType
|
||||||
isTextFormat = isTextMimeType(mimeType)
|
isTextFormat = isTextMimeType(mimeType)
|
||||||
|
|
||||||
base64Encoded = False
|
base64Encoded = False
|
||||||
|
|
@ -667,7 +772,7 @@ class ServiceManagement:
|
||||||
logger.error(f"Error updating data for file {fileId}: {str(e)}")
|
logger.error(f"Error updating data for file {fileId}: {str(e)}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def saveUploadedFile(self, fileContent: bytes, fileName: str) -> Dict[str, Any]:
|
def saveUploadedFile(self, fileContent: bytes, fileName: str) -> FileItem:
|
||||||
"""Saves an uploaded file if user has permission."""
|
"""Saves an uploaded file if user has permission."""
|
||||||
try:
|
try:
|
||||||
# Check file creation permission
|
# Check file creation permission
|
||||||
|
|
@ -687,7 +792,7 @@ class ServiceManagement:
|
||||||
# Check for duplicate within same user/mandate
|
# Check for duplicate within same user/mandate
|
||||||
existingFile = self.checkForDuplicateFile(fileHash)
|
existingFile = self.checkForDuplicateFile(fileHash)
|
||||||
if existingFile:
|
if existingFile:
|
||||||
logger.debug(f"Duplicate found for {fileName}: {existingFile['id']}")
|
logger.debug(f"Duplicate found for {fileName}: {existingFile.id}")
|
||||||
return existingFile
|
return existingFile
|
||||||
|
|
||||||
# Determine MIME type and size
|
# Determine MIME type and size
|
||||||
|
|
@ -696,7 +801,7 @@ class ServiceManagement:
|
||||||
|
|
||||||
# Save metadata
|
# Save metadata
|
||||||
logger.debug(f"Saving file metadata to database for file: {fileName}")
|
logger.debug(f"Saving file metadata to database for file: {fileName}")
|
||||||
dbFile = self.createFile(
|
fileItem = self.createFile(
|
||||||
name=fileName,
|
name=fileName,
|
||||||
mimeType=mimeType,
|
mimeType=mimeType,
|
||||||
size=fileSize,
|
size=fileSize,
|
||||||
|
|
@ -705,10 +810,10 @@ class ServiceManagement:
|
||||||
|
|
||||||
# Save binary data
|
# Save binary data
|
||||||
logger.debug(f"Saving file content to database for file: {fileName}")
|
logger.debug(f"Saving file content to database for file: {fileName}")
|
||||||
self.createFileData(dbFile["id"], fileContent)
|
self.createFileData(fileItem.id, fileContent)
|
||||||
|
|
||||||
logger.debug(f"File upload process completed for: {fileName}")
|
logger.debug(f"File upload process completed for: {fileName}")
|
||||||
return dbFile
|
return fileItem
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error in saveUploadedFile for {fileName}: {str(e)}", exc_info=True)
|
logger.error(f"Error in saveUploadedFile for {fileName}: {str(e)}", exc_info=True)
|
||||||
|
|
@ -731,9 +836,9 @@ class ServiceManagement:
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"id": fileId,
|
"id": fileId,
|
||||||
"name": file.get("name", f"file_{fileId}"),
|
"name": file.filename,
|
||||||
"contentType": file.get("mimeType", "application/octet-stream"),
|
"contentType": file.mimeType,
|
||||||
"size": file.get("size", len(fileContent)),
|
"size": file.fileSize,
|
||||||
"content": fileContent
|
"content": fileContent
|
||||||
}
|
}
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ Updated to match the Entity Relation Diagram structure.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from typing import List, Dict, Any, Optional
|
from typing import List, Dict, Any, Optional, Union
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
|
@ -22,6 +22,14 @@ class FileItem(BaseModel, ModelMixin):
|
||||||
workflowId: Optional[str] = Field(None, description="Foreign key to workflow")
|
workflowId: Optional[str] = Field(None, description="Foreign key to workflow")
|
||||||
fileHash: str = Field(description="Hash of the file")
|
fileHash: str = Field(description="Hash of the file")
|
||||||
fileSize: int = Field(description="Size of the file in bytes")
|
fileSize: int = Field(description="Size of the file in bytes")
|
||||||
|
creationDate: str = Field(default_factory=lambda: datetime.now().isoformat(), description="Date when the file was created")
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
"""Convert model to dictionary with proper datetime handling"""
|
||||||
|
data = super().to_dict()
|
||||||
|
if isinstance(data.get("creationDate"), datetime):
|
||||||
|
data["creationDate"] = data["creationDate"].isoformat()
|
||||||
|
return data
|
||||||
|
|
||||||
# Register labels for FileItem
|
# Register labels for FileItem
|
||||||
register_model_labels(
|
register_model_labels(
|
||||||
|
|
@ -34,7 +42,40 @@ register_model_labels(
|
||||||
"mimeType": {"en": "MIME Type", "fr": "Type MIME"},
|
"mimeType": {"en": "MIME Type", "fr": "Type MIME"},
|
||||||
"workflowId": {"en": "Workflow ID", "fr": "ID du flux de travail"},
|
"workflowId": {"en": "Workflow ID", "fr": "ID du flux de travail"},
|
||||||
"fileHash": {"en": "File Hash", "fr": "Hash du fichier"},
|
"fileHash": {"en": "File Hash", "fr": "Hash du fichier"},
|
||||||
"fileSize": {"en": "File Size", "fr": "Taille du fichier"}
|
"fileSize": {"en": "File Size", "fr": "Taille du fichier"},
|
||||||
|
"creationDate": {"en": "Creation Date", "fr": "Date de création"}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
class FilePreview(BaseModel, ModelMixin):
|
||||||
|
"""Data model for file preview"""
|
||||||
|
content: Union[str, bytes] = Field(description="File content (text or binary)")
|
||||||
|
mimeType: str = Field(description="MIME type of the file")
|
||||||
|
filename: str = Field(description="Original filename")
|
||||||
|
isText: bool = Field(description="Whether the content is text (True) or binary (False)")
|
||||||
|
encoding: Optional[str] = Field(None, description="Text encoding if content is text")
|
||||||
|
size: int = Field(description="Size of the content in bytes")
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
"""Convert model to dictionary with proper content handling"""
|
||||||
|
data = super().to_dict()
|
||||||
|
# Convert bytes to base64 string if content is binary
|
||||||
|
if isinstance(data.get("content"), bytes):
|
||||||
|
import base64
|
||||||
|
data["content"] = base64.b64encode(data["content"]).decode('utf-8')
|
||||||
|
return data
|
||||||
|
|
||||||
|
# Register labels for FilePreview
|
||||||
|
register_model_labels(
|
||||||
|
"FilePreview",
|
||||||
|
{"en": "File Preview", "fr": "Aperçu du fichier"},
|
||||||
|
{
|
||||||
|
"content": {"en": "Content", "fr": "Contenu"},
|
||||||
|
"mimeType": {"en": "MIME Type", "fr": "Type MIME"},
|
||||||
|
"filename": {"en": "Filename", "fr": "Nom de fichier"},
|
||||||
|
"isText": {"en": "Is Text", "fr": "Est du texte"},
|
||||||
|
"encoding": {"en": "Encoding", "fr": "Encodage"},
|
||||||
|
"size": {"en": "Size", "fr": "Taille"}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from fastapi import APIRouter, HTTPException, Depends, File, UploadFile, Form, Path, Request, status, Query, Response, Body
|
from fastapi import APIRouter, HTTPException, Depends, File, UploadFile, Form, Path, Request, status, Query, Response, Body
|
||||||
from fastapi.responses import JSONResponse, FileResponse
|
from fastapi.responses import JSONResponse, FileResponse
|
||||||
from typing import List, Dict, Any, Optional
|
from typing import List, Dict, Any, Optional, Union
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
@ -15,7 +15,7 @@ from modules.security.auth import limiter, getCurrentUser
|
||||||
|
|
||||||
# Import interfaces
|
# Import interfaces
|
||||||
import modules.interfaces.serviceManagementClass as serviceManagementClass
|
import modules.interfaces.serviceManagementClass as serviceManagementClass
|
||||||
from modules.interfaces.serviceManagementModel import FileItem
|
from modules.interfaces.serviceManagementModel import FileItem, FilePreview
|
||||||
from modules.shared.attributeUtils import getModelAttributeDefinitions, AttributeResponse, AttributeDefinition
|
from modules.shared.attributeUtils import getModelAttributeDefinitions, AttributeResponse, AttributeDefinition
|
||||||
from modules.interfaces.serviceAppModel import User
|
from modules.interfaces.serviceAppModel import User
|
||||||
|
|
||||||
|
|
@ -50,7 +50,9 @@ async def get_files(
|
||||||
|
|
||||||
# Get all files generically - only metadata, no binary data
|
# Get all files generically - only metadata, no binary data
|
||||||
files = managementInterface.getAllFiles()
|
files = managementInterface.getAllFiles()
|
||||||
return [FileItem(**file) for file in files]
|
|
||||||
|
# Return files directly since they are already FileItem objects
|
||||||
|
return files
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error getting files: {str(e)}")
|
logger.error(f"Error getting files: {str(e)}")
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|
@ -82,13 +84,16 @@ async def upload_file(
|
||||||
)
|
)
|
||||||
|
|
||||||
# Save file via LucyDOM interface in the database
|
# Save file via LucyDOM interface in the database
|
||||||
fileMeta = managementInterface.saveUploadedFile(fileContent, file.filename)
|
fileItem = managementInterface.saveUploadedFile(fileContent, file.filename)
|
||||||
|
|
||||||
# If workflowId is provided, update the file information
|
# If workflowId is provided, update the file information
|
||||||
if workflowId:
|
if workflowId:
|
||||||
updateData = {"workflowId": workflowId}
|
updateData = {"workflowId": workflowId}
|
||||||
managementInterface.updateFile(fileMeta["id"], updateData)
|
managementInterface.updateFile(fileItem.id, updateData)
|
||||||
fileMeta["workflowId"] = workflowId
|
fileItem.workflowId = workflowId
|
||||||
|
|
||||||
|
# Convert FileItem to dictionary for JSON response
|
||||||
|
fileMeta = fileItem.to_dict()
|
||||||
|
|
||||||
# Successful response
|
# Successful response
|
||||||
return JSONResponse({
|
return JSONResponse({
|
||||||
|
|
@ -245,12 +250,12 @@ async def get_file_stats(
|
||||||
|
|
||||||
# Calculate statistics
|
# Calculate statistics
|
||||||
totalFiles = len(allFiles)
|
totalFiles = len(allFiles)
|
||||||
totalSize = sum(file.get("size", 0) for file in allFiles)
|
totalSize = sum(file.fileSize for file in allFiles)
|
||||||
|
|
||||||
# Group by file type
|
# Group by file type
|
||||||
fileTypes = {}
|
fileTypes = {}
|
||||||
for file in allFiles:
|
for file in allFiles:
|
||||||
fileType = file.get("mimeType", "unknown").split("/")[0]
|
fileType = file.mimeType.split("/")[0]
|
||||||
if fileType not in fileTypes:
|
if fileType not in fileTypes:
|
||||||
fileTypes[fileType] = 0
|
fileTypes[fileType] = 0
|
||||||
fileTypes[fileType] += 1
|
fileTypes[fileType] += 1
|
||||||
|
|
@ -268,3 +273,76 @@ async def get_file_stats(
|
||||||
detail=f"Error retrieving file statistics: {str(e)}"
|
detail=f"Error retrieving file statistics: {str(e)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@router.get("/{fileId}/download")
|
||||||
|
@limiter.limit("30/minute")
|
||||||
|
async def download_file(
|
||||||
|
request: Request,
|
||||||
|
fileId: str = Path(..., description="ID of the file to download"),
|
||||||
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
) -> Response:
|
||||||
|
"""Download a file"""
|
||||||
|
try:
|
||||||
|
managementInterface = serviceManagementClass.getInterface(currentUser)
|
||||||
|
|
||||||
|
# Get file data
|
||||||
|
fileData = managementInterface.getFile(fileId)
|
||||||
|
if not fileData:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail=f"File with ID {fileId} not found"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get file content
|
||||||
|
fileContent = managementInterface.getFileData(fileId)
|
||||||
|
if not fileContent:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail=f"File content not found for ID {fileId}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Return file as response
|
||||||
|
return Response(
|
||||||
|
content=fileContent,
|
||||||
|
media_type=fileData.mimeType,
|
||||||
|
headers={
|
||||||
|
"Content-Disposition": f"attachment; filename={fileData.filename}"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error downloading file: {str(e)}")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
detail=f"Error downloading file: {str(e)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
@router.get("/{fileId}/preview", response_model=FilePreview)
|
||||||
|
@limiter.limit("30/minute")
|
||||||
|
async def preview_file(
|
||||||
|
request: Request,
|
||||||
|
fileId: str = Path(..., description="ID of the file to preview"),
|
||||||
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
) -> FilePreview:
|
||||||
|
"""Preview a file's content"""
|
||||||
|
try:
|
||||||
|
managementInterface = serviceManagementClass.getInterface(currentUser)
|
||||||
|
|
||||||
|
# Get file preview
|
||||||
|
preview = managementInterface.getFilePreview(fileId)
|
||||||
|
if not preview:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail=f"File with ID {fileId} not found or no content available"
|
||||||
|
)
|
||||||
|
|
||||||
|
return FilePreview(**preview)
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error previewing file: {str(e)}")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
detail=f"Error previewing file: {str(e)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -402,75 +402,6 @@ async def delete_file_from_message(
|
||||||
detail=f"Error deleting file reference: {str(e)}"
|
detail=f"Error deleting file reference: {str(e)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# File preview and download routes
|
|
||||||
|
|
||||||
@router.get("/files/{fileId}/preview", response_model=ChatDocument)
|
|
||||||
@limiter.limit("30/minute")
|
|
||||||
async def preview_file(
|
|
||||||
request: Request,
|
|
||||||
fileId: str = Path(..., description="ID of the file to preview"),
|
|
||||||
currentUser: User = Depends(getCurrentUser)
|
|
||||||
) -> ChatDocument:
|
|
||||||
"""Preview a file's content."""
|
|
||||||
try:
|
|
||||||
# Get service container
|
|
||||||
service = createServiceContainer(currentUser)
|
|
||||||
|
|
||||||
# Get file document
|
|
||||||
document = service.base.getFileDocument(fileId)
|
|
||||||
if not document:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_404_NOT_FOUND,
|
|
||||||
detail=f"File with ID {fileId} not found"
|
|
||||||
)
|
|
||||||
|
|
||||||
return ChatDocument(**document)
|
|
||||||
except HTTPException:
|
|
||||||
raise
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error previewing file: {str(e)}", exc_info=True)
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
||||||
detail=f"Error previewing file: {str(e)}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@router.get("/files/{fileId}/download")
|
|
||||||
@limiter.limit("30/minute")
|
|
||||||
async def download_file(
|
|
||||||
request: Request,
|
|
||||||
fileId: str = Path(..., description="ID of the file to download"),
|
|
||||||
currentUser: User = Depends(getCurrentUser)
|
|
||||||
) -> Response:
|
|
||||||
"""Download a file."""
|
|
||||||
try:
|
|
||||||
# Get service container
|
|
||||||
service = createServiceContainer(currentUser)
|
|
||||||
|
|
||||||
# Get file data
|
|
||||||
fileInfo = service.base.downloadFile(fileId)
|
|
||||||
if not fileInfo:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_404_NOT_FOUND,
|
|
||||||
detail=f"File with ID {fileId} not found"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Return file as response
|
|
||||||
return Response(
|
|
||||||
content=fileInfo["content"],
|
|
||||||
media_type=fileInfo["contentType"],
|
|
||||||
headers={
|
|
||||||
"Content-Disposition": f"attachment; filename={fileInfo['name']}"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
except HTTPException:
|
|
||||||
raise
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error downloading file: {str(e)}", exc_info=True)
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
||||||
detail=f"Error downloading file: {str(e)}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@router.get("/workflows", response_model=List[ChatWorkflow])
|
@router.get("/workflows", response_model=List[ChatWorkflow])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_workflows(
|
async def get_workflows(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue