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
- if file.filepath:
17
- active_storage.delete(file.filepath)
18
- if file.heatmap_path:
19
- active_storage.delete(file.heatmap_path)
20
-
21
- # Delete related jobs first to prevent FK IntegrityError in SQLite
22
- from app.models.job_model import Job
23
- db.query(Job).filter(Job.file_id == file.id).delete()
24
-
25
- db.delete(file)
26
- db.commit()
27
-
28
- return True
 
 
 
 
 
 
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
- const audio = new Audio('/sounds/notif.wav');
107
- audio.volume = 1.0;
108
- audio.play().catch(e => console.error("Audio play failed:", e));
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') {