feat: bổ sung phần profile của user
This commit is contained in:
@@ -6,6 +6,7 @@ from fastapi import APIRouter, UploadFile, File, HTTPException, Query, Depends
|
||||
from fastapi.responses import FileResponse
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, List
|
||||
import json
|
||||
from app.config import settings
|
||||
from app.api.v1.auth import get_current_user
|
||||
from app.api.v1.projects import get_optional_user
|
||||
@@ -68,6 +69,15 @@ async def upload_audio(file: UploadFile = File(...), current_user: Optional[dict
|
||||
content = await file.read()
|
||||
f.write(content)
|
||||
|
||||
# Save original filename as sidecar metadata
|
||||
import json
|
||||
meta_path = os.path.join(settings.UPLOADS_DIR, file_id + ".meta")
|
||||
try:
|
||||
with open(meta_path, "w") as mf:
|
||||
json.dump({"original_name": file.filename}, mf)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Trigger celery task
|
||||
from app.tasks.worker import analyze_audio_task
|
||||
task = analyze_audio_task.delay(file_id)
|
||||
@@ -357,8 +367,18 @@ async def list_user_files(req: MyFilesRequest, current_user: dict = Depends(get_
|
||||
if filename in files_map:
|
||||
files_map[filename]["size_mb"] = round(files_map[filename]["size_mb"] + stat.st_size / (1024 * 1024), 2)
|
||||
else:
|
||||
original_name = filename
|
||||
meta_path = os.path.join(directory, filename + ".meta")
|
||||
if os.path.isfile(meta_path):
|
||||
try:
|
||||
with open(meta_path, "r") as mf:
|
||||
meta = json.load(mf)
|
||||
original_name = meta.get("original_name", filename)
|
||||
except Exception:
|
||||
pass
|
||||
files_map[filename] = {
|
||||
"file_id": filename,
|
||||
"original_name": original_name,
|
||||
"size_mb": round(stat.st_size / (1024 * 1024), 2),
|
||||
"created_at": stat.st_mtime,
|
||||
"type": type_label,
|
||||
@@ -390,6 +410,13 @@ async def delete_user_file(file_id: str, current_user: dict = Depends(get_curren
|
||||
deleted = True
|
||||
except Exception:
|
||||
pass
|
||||
# Clean up sidecar metadata file
|
||||
meta_path = os.path.join(directory, file_id + ".meta")
|
||||
if os.path.isfile(meta_path):
|
||||
try:
|
||||
os.remove(meta_path)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not deleted:
|
||||
raise HTTPException(status_code=404, detail="Không tìm thấy tệp trên server")
|
||||
|
||||
+27
-3
@@ -2752,6 +2752,9 @@ const ProfileModal = ({
|
||||
const [filesList, setFilesList] = useState([]);
|
||||
const [loadingFiles, setLoadingFiles] = useState(false);
|
||||
|
||||
// Confirmation modal state
|
||||
const [confirmModal, setConfirmModal] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
fetchProfile();
|
||||
@@ -2821,7 +2824,10 @@ const ProfileModal = ({
|
||||
|
||||
const handleDeleteProject = async (projectId, e) => {
|
||||
e.stopPropagation();
|
||||
if (!confirm("Bạn có chắc chắn muốn xóa dự án này khỏi Cloud?")) return;
|
||||
setConfirmModal({
|
||||
title: "Xóa dự án Cloud",
|
||||
message: "Bạn có chắc chắn muốn xóa dự án này khỏi Cloud? Hành động này không thể hoàn tác.",
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
await window.SonicAPI.deleteCloudProject(projectId);
|
||||
showToast("Đã xóa dự án thành công!", "success");
|
||||
@@ -2830,6 +2836,8 @@ const ProfileModal = ({
|
||||
} catch (err) {
|
||||
showToast(err.message || "Lỗi khi xóa dự án", "error");
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleDeleteFile = async (fileId) => {
|
||||
@@ -2883,8 +2891,24 @@ const ProfileModal = ({
|
||||
};
|
||||
|
||||
return /*#__PURE__*/React.createElement("div", {
|
||||
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm"
|
||||
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm relative"
|
||||
}, confirmModal && /*#__PURE__*/React.createElement("div", {
|
||||
className: "absolute inset-0 z-[60] flex items-center justify-center bg-black/50"
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
className: "bg-[#262626] border border-[#383838] rounded-lg shadow-2xl p-5 max-w-sm w-full text-slate-200"
|
||||
}, /*#__PURE__*/React.createElement("h4", {
|
||||
className: "text-sm font-bold text-rose-400 mb-2"
|
||||
}, confirmModal.title), /*#__PURE__*/React.createElement("p", {
|
||||
className: "text-xs text-slate-300 mb-4"
|
||||
}, confirmModal.message), /*#__PURE__*/React.createElement("div", {
|
||||
className: "flex justify-end gap-2"
|
||||
}, /*#__PURE__*/React.createElement("button", {
|
||||
onClick: () => setConfirmModal(null),
|
||||
className: "px-3 py-1.5 bg-zinc-700 hover:bg-zinc-600 text-slate-300 rounded text-xs font-semibold"
|
||||
}, "Hủy"), /*#__PURE__*/React.createElement("button", {
|
||||
onClick: () => { const fn = confirmModal.onConfirm; setConfirmModal(null); fn(); },
|
||||
className: "px-3 py-1.5 bg-rose-700 hover:bg-rose-600 text-white rounded text-xs font-semibold"
|
||||
}, "Xác nhận xóa")))), /*#__PURE__*/React.createElement("div", {
|
||||
className: "bg-[#262626] border border-[#383838] rounded-xl shadow-2xl w-full max-w-2xl p-6 text-slate-200 flex flex-col max-h-[85vh]"
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
className: "flex justify-between items-center pb-3 border-b border-[#383838] shrink-0"
|
||||
@@ -3024,7 +3048,7 @@ const ProfileModal = ({
|
||||
className: "flex items-center justify-between p-3 bg-[#1e1e1e] rounded border border-zinc-800 text-xs"
|
||||
}, [
|
||||
/*#__PURE__*/React.createElement("div", { key: "meta", className: "max-w-[70%]" }, [
|
||||
/*#__PURE__*/React.createElement("div", { className: "font-mono font-semibold text-slate-300 truncate" }, file.file_id.replace(/^user_[^_]+_/, '')),
|
||||
/*#__PURE__*/React.createElement("div", { className: "font-semibold text-slate-300 truncate" }, file.original_name || file.file_id),
|
||||
/*#__PURE__*/React.createElement("div", { className: "text-[10px] text-zinc-500 mt-0.5" }, `Loại: ${file.type} | Dung lượng: ${file.size_mb} MB | Tạo lúc: ${new Date(file.created_at * 1000).toLocaleString()}`)
|
||||
]),
|
||||
/*#__PURE__*/React.createElement("div", { key: "actions", className: "flex items-center gap-2" }, [
|
||||
|
||||
@@ -2788,6 +2788,9 @@ const ProfileModal = ({
|
||||
// Files list state
|
||||
const [filesList, setFilesList] = useState([]);
|
||||
const [loadingFiles, setLoadingFiles] = useState(false);
|
||||
|
||||
// Confirmation modal state
|
||||
const [confirmModal, setConfirmModal] = useState(null);
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
fetchProfile();
|
||||
@@ -2852,7 +2855,10 @@ const ProfileModal = ({
|
||||
};
|
||||
const handleDeleteProject = async (projectId, e) => {
|
||||
e.stopPropagation();
|
||||
if (!confirm("Bạn có chắc chắn muốn xóa dự án này khỏi Cloud?")) return;
|
||||
setConfirmModal({
|
||||
title: "Xóa dự án Cloud",
|
||||
message: "Bạn có chắc chắn muốn xóa dự án này khỏi Cloud? Hành động này không thể hoàn tác.",
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
await window.SonicAPI.deleteCloudProject(projectId);
|
||||
showToast("Đã xóa dự án thành công!", "success");
|
||||
@@ -2861,6 +2867,8 @@ const ProfileModal = ({
|
||||
} catch (err) {
|
||||
showToast(err.message || "Lỗi khi xóa dự án", "error");
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
const handleDeleteFile = async fileId => {
|
||||
if (!confirm(`Bạn có chắc chắn muốn xóa tệp tin ${fileId}?`)) return;
|
||||
@@ -2910,8 +2918,28 @@ const ProfileModal = ({
|
||||
}
|
||||
};
|
||||
return /*#__PURE__*/React.createElement("div", {
|
||||
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm"
|
||||
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm relative"
|
||||
}, confirmModal && /*#__PURE__*/React.createElement("div", {
|
||||
className: "absolute inset-0 z-[60] flex items-center justify-center bg-black/50"
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
className: "bg-[#262626] border border-[#383838] rounded-lg shadow-2xl p-5 max-w-sm w-full text-slate-200"
|
||||
}, /*#__PURE__*/React.createElement("h4", {
|
||||
className: "text-sm font-bold text-rose-400 mb-2"
|
||||
}, confirmModal.title), /*#__PURE__*/React.createElement("p", {
|
||||
className: "text-xs text-slate-300 mb-4"
|
||||
}, confirmModal.message), /*#__PURE__*/React.createElement("div", {
|
||||
className: "flex justify-end gap-2"
|
||||
}, /*#__PURE__*/React.createElement("button", {
|
||||
onClick: () => setConfirmModal(null),
|
||||
className: "px-3 py-1.5 bg-zinc-700 hover:bg-zinc-600 text-slate-300 rounded text-xs font-semibold"
|
||||
}, "Hủy"), /*#__PURE__*/React.createElement("button", {
|
||||
onClick: () => {
|
||||
const fn = confirmModal.onConfirm;
|
||||
setConfirmModal(null);
|
||||
fn();
|
||||
},
|
||||
className: "px-3 py-1.5 bg-rose-700 hover:bg-rose-600 text-white rounded text-xs font-semibold"
|
||||
}, "Xác nhận xóa")))), /*#__PURE__*/React.createElement("div", {
|
||||
className: "bg-[#262626] border border-[#383838] rounded-xl shadow-2xl w-full max-w-2xl p-6 text-slate-200 flex flex-col max-h-[85vh]"
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
className: "flex justify-between items-center pb-3 border-b border-[#383838] shrink-0"
|
||||
@@ -3067,8 +3095,8 @@ const ProfileModal = ({
|
||||
key: "meta",
|
||||
className: "max-w-[70%]"
|
||||
}, [/*#__PURE__*/React.createElement("div", {
|
||||
className: "font-mono font-semibold text-slate-300 truncate"
|
||||
}, file.file_id.replace(/^user_[^_]+_/, '')), /*#__PURE__*/React.createElement("div", {
|
||||
className: "font-semibold text-slate-300 truncate"
|
||||
}, file.original_name || file.file_id), /*#__PURE__*/React.createElement("div", {
|
||||
className: "text-[10px] text-zinc-500 mt-0.5"
|
||||
}, `Loại: ${file.type} | Dung lượng: ${file.size_mb} MB | Tạo lúc: ${new Date(file.created_at * 1000).toLocaleString()}`)]), /*#__PURE__*/React.createElement("div", {
|
||||
key: "actions",
|
||||
|
||||
Reference in New Issue
Block a user