feat: 增加删除文件功能
This commit is contained in:
parent
dea3a6bd6a
commit
fbdeba5088
|
|
@ -6,7 +6,7 @@ import os
|
|||
from ...core.config import settings
|
||||
from ...core.database import get_db
|
||||
from ...models.file import File as FileModel, FileStatus
|
||||
from ...services.file_service import process_file
|
||||
from ...services.file_service import process_file, delete_file
|
||||
from ...schemas.file import FileResponse as FileResponseSchema, FileList
|
||||
import asyncio
|
||||
from fastapi import WebSocketDisconnect
|
||||
|
|
@ -115,4 +115,24 @@ async def websocket_endpoint(websocket: WebSocket, file_id: str, db: Session = D
|
|||
|
||||
await asyncio.sleep(1)
|
||||
except WebSocketDisconnect:
|
||||
pass
|
||||
pass
|
||||
|
||||
@router.delete("/files/{file_id}")
|
||||
async def delete_file_endpoint(
|
||||
file_id: str,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
Delete a file and its associated records.
|
||||
This will remove:
|
||||
1. The database record
|
||||
2. The original uploaded file
|
||||
3. The processed markdown file (if it exists)
|
||||
"""
|
||||
try:
|
||||
delete_file(file_id)
|
||||
return {"message": "File deleted successfully"}
|
||||
except HTTPException as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
|
@ -7,6 +7,7 @@ import sys
|
|||
import os
|
||||
from ..core.services.document_service import DocumentService
|
||||
from pathlib import Path
|
||||
from fastapi import HTTPException
|
||||
|
||||
|
||||
celery = Celery(
|
||||
|
|
@ -15,6 +16,39 @@ celery = Celery(
|
|||
backend=settings.CELERY_RESULT_BACKEND
|
||||
)
|
||||
|
||||
def delete_file(file_id: str):
|
||||
"""
|
||||
Delete a file and its associated records.
|
||||
This will:
|
||||
1. Delete the database record
|
||||
2. Delete the original uploaded file
|
||||
3. Delete the processed markdown file (if it exists)
|
||||
"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
# Get the file record
|
||||
file = db.query(File).filter(File.id == file_id).first()
|
||||
if not file:
|
||||
raise HTTPException(status_code=404, detail="File not found")
|
||||
|
||||
# Delete the original file if it exists
|
||||
if file.original_path and os.path.exists(file.original_path):
|
||||
os.remove(file.original_path)
|
||||
|
||||
# Delete the processed file if it exists
|
||||
if file.processed_path and os.path.exists(file.processed_path):
|
||||
os.remove(file.processed_path)
|
||||
|
||||
# Delete the database record
|
||||
db.delete(file)
|
||||
db.commit()
|
||||
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
raise HTTPException(status_code=500, detail=f"Error deleting file: {str(e)}")
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
@celery.task
|
||||
def process_file(file_id: str):
|
||||
db = SessionLocal()
|
||||
|
|
|
|||
|
|
@ -11,8 +11,13 @@ import {
|
|||
Checkbox,
|
||||
Button,
|
||||
Chip,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import { Download as DownloadIcon } from '@mui/icons-material';
|
||||
import { Download as DownloadIcon, Delete as DeleteIcon } from '@mui/icons-material';
|
||||
import { File, FileStatus } from '../types/file';
|
||||
import { api } from '../services/api';
|
||||
|
||||
|
|
@ -23,6 +28,8 @@ interface FileListProps {
|
|||
|
||||
const FileList: React.FC<FileListProps> = ({ files, onFileStatusChange }) => {
|
||||
const [selectedFiles, setSelectedFiles] = useState<string[]>([]);
|
||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||
const [fileToDelete, setFileToDelete] = useState<string | null>(null);
|
||||
|
||||
const handleSelectFile = (fileId: string) => {
|
||||
setSelectedFiles((prev) =>
|
||||
|
|
@ -60,6 +67,29 @@ const FileList: React.FC<FileListProps> = ({ files, onFileStatusChange }) => {
|
|||
}
|
||||
};
|
||||
|
||||
const handleDeleteClick = (fileId: string) => {
|
||||
setFileToDelete(fileId);
|
||||
setDeleteDialogOpen(true);
|
||||
};
|
||||
|
||||
const handleDeleteConfirm = async () => {
|
||||
if (fileToDelete) {
|
||||
try {
|
||||
await api.deleteFile(fileToDelete);
|
||||
onFileStatusChange();
|
||||
} catch (error) {
|
||||
console.error('Error deleting file:', error);
|
||||
}
|
||||
}
|
||||
setDeleteDialogOpen(false);
|
||||
setFileToDelete(null);
|
||||
};
|
||||
|
||||
const handleDeleteCancel = () => {
|
||||
setDeleteDialogOpen(false);
|
||||
setFileToDelete(null);
|
||||
};
|
||||
|
||||
const getStatusColor = (status: FileStatus) => {
|
||||
switch (status) {
|
||||
case FileStatus.SUCCESS:
|
||||
|
|
@ -81,6 +111,7 @@ const FileList: React.FC<FileListProps> = ({ files, onFileStatusChange }) => {
|
|||
color="primary"
|
||||
onClick={handleDownloadSelected}
|
||||
disabled={selectedFiles.length === 0}
|
||||
sx={{ mr: 1 }}
|
||||
>
|
||||
Download Selected
|
||||
</Button>
|
||||
|
|
@ -123,10 +154,19 @@ const FileList: React.FC<FileListProps> = ({ files, onFileStatusChange }) => {
|
|||
{new Date(file.created_at).toLocaleString()}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<IconButton
|
||||
onClick={() => handleDeleteClick(file.id)}
|
||||
size="small"
|
||||
color="error"
|
||||
sx={{ mr: 1 }}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
{file.status === FileStatus.SUCCESS && (
|
||||
<IconButton
|
||||
onClick={() => handleDownload(file.id)}
|
||||
size="small"
|
||||
color="primary"
|
||||
>
|
||||
<DownloadIcon />
|
||||
</IconButton>
|
||||
|
|
@ -137,6 +177,24 @@ const FileList: React.FC<FileListProps> = ({ files, onFileStatusChange }) => {
|
|||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
|
||||
<Dialog
|
||||
open={deleteDialogOpen}
|
||||
onClose={handleDeleteCancel}
|
||||
>
|
||||
<DialogTitle>Confirm Delete</DialogTitle>
|
||||
<DialogContent>
|
||||
<Typography>
|
||||
Are you sure you want to delete this file? This action cannot be undone.
|
||||
</Typography>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleDeleteCancel}>Cancel</Button>
|
||||
<Button onClick={handleDeleteConfirm} color="error" variant="contained">
|
||||
Delete
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -31,4 +31,8 @@ export const api = {
|
|||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
deleteFile: async (fileId: string): Promise<void> => {
|
||||
await axios.delete(`${API_BASE_URL}/files/files/${fileId}`);
|
||||
},
|
||||
};
|
||||
Loading…
Reference in New Issue