Spaces:
Running
Running
Anish-530 commited on
Commit ·
2b7c3ce
1
Parent(s): 92695a4
Fix: delete file FK violation (delete feedback first), fix sound autoplay policy
Browse files
backend/app/services/file_delete_service.py
CHANGED
|
@@ -1,28 +1,33 @@
|
|
| 1 |
-
import os
|
| 2 |
-
from sqlalchemy.orm import Session
|
| 3 |
-
from fastapi import HTTPException
|
| 4 |
-
from app.models.file_model import File
|
| 5 |
-
from app.core.storage import active_storage
|
| 6 |
-
|
| 7 |
-
def delete_file_service(db: Session, file_id: int, user_id: int):
|
| 8 |
-
file = db.query(File).filter(File.id == file_id).first()
|
| 9 |
-
|
| 10 |
-
if not file:
|
| 11 |
-
raise HTTPException(status_code=404, detail="File Not Found")
|
| 12 |
-
|
| 13 |
-
if file.owner_id != user_id:
|
| 14 |
-
raise HTTPException(status_code=403, detail="Not Authorized to delete this file")
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from sqlalchemy.orm import Session
|
| 3 |
+
from fastapi import HTTPException
|
| 4 |
+
from app.models.file_model import File
|
| 5 |
+
from app.core.storage import active_storage
|
| 6 |
+
|
| 7 |
+
def delete_file_service(db: Session, file_id: int, user_id: int):
|
| 8 |
+
file = db.query(File).filter(File.id == file_id).first()
|
| 9 |
+
|
| 10 |
+
if not file:
|
| 11 |
+
raise HTTPException(status_code=404, detail="File Not Found")
|
| 12 |
+
|
| 13 |
+
if file.owner_id != user_id:
|
| 14 |
+
raise HTTPException(status_code=403, detail="Not Authorized to delete this file")
|
| 15 |
+
|
| 16 |
+
# Delete physical files from storage first
|
| 17 |
+
if file.filepath:
|
| 18 |
+
active_storage.delete(file.filepath)
|
| 19 |
+
if file.heatmap_path:
|
| 20 |
+
active_storage.delete(file.heatmap_path)
|
| 21 |
+
|
| 22 |
+
# Delete all child rows that reference this file (FK constraints)
|
| 23 |
+
# Order matters: most-specific relations first, then the file row itself
|
| 24 |
+
from app.models.job_model import Job
|
| 25 |
+
from app.models.feedback_model import Feedback
|
| 26 |
+
|
| 27 |
+
db.query(Feedback).filter(Feedback.file_id == file.id).delete(synchronize_session=False)
|
| 28 |
+
db.query(Job).filter(Job.file_id == file.id).delete(synchronize_session=False)
|
| 29 |
+
|
| 30 |
+
db.delete(file)
|
| 31 |
+
db.commit()
|
| 32 |
+
|
| 33 |
+
return True
|
frontend/components/upload/UploadZone.tsx
CHANGED
|
@@ -17,10 +17,25 @@ export default function UploadZone({ autoAnalyze = false }: { autoAnalyze?: bool
|
|
| 17 |
const fileInputRef = useRef<HTMLInputElement>(null);
|
| 18 |
const [mockResult, setMockResult] = useState<{ label: string, conf: number } | null>(null);
|
| 19 |
const [uploadedFileId, setUploadedFileId] = useState<number | null>(null);
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
const handleFileUpload = async (file: File) => {
|
| 22 |
if (!file) return;
|
| 23 |
setError("");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
const url = URL.createObjectURL(file);
|
| 26 |
setMediaPreviewUrl(url);
|
|
@@ -102,10 +117,10 @@ export default function UploadZone({ autoAnalyze = false }: { autoAnalyze?: bool
|
|
| 102 |
setInteractionState('result');
|
| 103 |
|
| 104 |
const soundSetting = localStorage.getItem('spotix_sound');
|
| 105 |
-
if (soundSetting !== 'false') {
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
}
|
| 110 |
}, 400);
|
| 111 |
} else if (fileData.status === 'Failed' || fileData.status === 'error' || fileData.status === 'FAILED') {
|
|
|
|
| 17 |
const fileInputRef = useRef<HTMLInputElement>(null);
|
| 18 |
const [mockResult, setMockResult] = useState<{ label: string, conf: number } | null>(null);
|
| 19 |
const [uploadedFileId, setUploadedFileId] = useState<number | null>(null);
|
| 20 |
+
// Pre-unlocked audio element — created during the user-gesture (upload click)
|
| 21 |
+
// so it can be played later from the polling callback without autoplay blocking
|
| 22 |
+
const notifAudioRef = useRef<HTMLAudioElement | null>(null);
|
| 23 |
|
| 24 |
const handleFileUpload = async (file: File) => {
|
| 25 |
if (!file) return;
|
| 26 |
setError("");
|
| 27 |
+
|
| 28 |
+
// Pre-unlock audio during this user-gesture context so it can play later
|
| 29 |
+
// without triggering the browser's autoplay policy block
|
| 30 |
+
const soundSetting = localStorage.getItem('spotix_sound');
|
| 31 |
+
if (soundSetting !== 'false') {
|
| 32 |
+
try {
|
| 33 |
+
const audio = new Audio('/sounds/notif.wav');
|
| 34 |
+
audio.volume = 0;
|
| 35 |
+
audio.play().then(() => { audio.pause(); audio.currentTime = 0; audio.volume = 1.0; }).catch(() => {});
|
| 36 |
+
notifAudioRef.current = audio;
|
| 37 |
+
} catch (e) {}
|
| 38 |
+
}
|
| 39 |
|
| 40 |
const url = URL.createObjectURL(file);
|
| 41 |
setMediaPreviewUrl(url);
|
|
|
|
| 117 |
setInteractionState('result');
|
| 118 |
|
| 119 |
const soundSetting = localStorage.getItem('spotix_sound');
|
| 120 |
+
if (soundSetting !== 'false' && notifAudioRef.current) {
|
| 121 |
+
notifAudioRef.current.currentTime = 0;
|
| 122 |
+
notifAudioRef.current.volume = 1.0;
|
| 123 |
+
notifAudioRef.current.play().catch(e => console.warn("Audio play failed:", e));
|
| 124 |
}
|
| 125 |
}, 400);
|
| 126 |
} else if (fileData.status === 'Failed' || fileData.status === 'error' || fileData.status === 'FAILED') {
|