diff --git a/backend/routers/voice.py b/backend/routers/voice.py index a908f614..264a0643 100644 --- a/backend/routers/voice.py +++ b/backend/routers/voice.py @@ -27,7 +27,7 @@ IssueCategory ) from backend.voice_service import get_voice_service -from backend.utils import generate_reference_id +from backend.utils import generate_reference_id, save_issue_db logger = logging.getLogger(__name__) @@ -255,37 +255,24 @@ def _save_audio_file(): with open(audio_file_path, 'wb') as f: f.write(audio_content) - def _delete_audio_file_best_effort(): - try: - if os.path.exists(audio_file_path): - os.remove(audio_file_path) - except Exception as cleanup_error: - logger.warning("Failed to delete orphaned audio file %s: %s", audio_file_path, cleanup_error) - await run_in_threadpool(_save_audio_file) - try: - # Store relative path for portability - relative_audio_path = os.path.join("data", "audio_recordings", audio_filename) - - # Blockchain feature: calculate integrity hash for the report - # Performance Boost: Use thread-safe cache to eliminate DB query for last hash - prev_hash = blockchain_last_hash_cache.get("last_hash") - if prev_hash is None: - # Cache miss: Fetch only the last hash from DB - prev_issue = await run_in_threadpool( - lambda: db.query(Issue.integrity_hash).order_by(Issue.id.desc()).first() - ) - prev_hash = prev_issue[0] if prev_issue and prev_issue[0] else "" - blockchain_last_hash_cache.set(data=prev_hash, key="last_hash") + # Store relative path for portability + relative_audio_path = os.path.join("data", "audio_recordings", audio_filename) + + # Blockchain feature: calculate integrity hash for the report + # Performance Boost: Use thread-safe cache to eliminate DB query for last hash + prev_hash = blockchain_last_hash_cache.get("last_hash") + if prev_hash is None: + # Cache miss: Fetch only the last hash from DB + # Use await run_in_threadpool for DB query if needed, or just do it in-thread + prev_issue = db.query(Issue.integrity_hash).order_by(Issue.id.desc()).first() + prev_hash = prev_issue[0] if prev_issue and prev_issue[0] else "" + blockchain_last_hash_cache.set(data=prev_hash, key="last_hash") - # Simple but effective SHA-256 chaining - # Format must match backend/routers/issues.py for a consistent chain - hash_content = f"{final_description}|{issue_category.value}|{prev_hash}" - except Exception: - db.rollback() - await run_in_threadpool(_delete_audio_file_best_effort) - raise + # Simple but effective SHA-256 chaining + # Format must match backend/routers/issues.py for a consistent chain + hash_content = f"{final_description}|{issue_category.value}|{prev_hash}" integrity_hash = hashlib.sha256(hash_content.encode()).hexdigest() # Create issue in database @@ -301,6 +288,7 @@ def _delete_audio_file_best_effort(): location=location, source='voice', status='open', + # Blockchain integrity fields integrity_hash=integrity_hash, previous_integrity_hash=prev_hash, # Voice-specific fields @@ -312,10 +300,11 @@ def _delete_audio_file_best_effort(): audio_file_path=relative_audio_path # Store relative path ) + # Standard synchronous DB operations for simplicity and thread-safety db.add(new_issue) db.commit() db.refresh(new_issue) - + # Update cache for next report AFTER successful DB commit blockchain_last_hash_cache.set(data=integrity_hash, key="last_hash") @@ -331,6 +320,12 @@ def _delete_audio_file_best_effort(): raise except Exception as e: logger.error(f"Error submitting voice issue: {e}", exc_info=True) + # Clean up audio file if database transaction fails + if 'audio_file_path' in locals() and os.path.exists(audio_file_path): + try: + os.remove(audio_file_path) + except Exception as cleanup_error: + logger.warning(f"Failed to cleanup audio file: {cleanup_error}") raise HTTPException(status_code=500, detail=f"Failed to submit voice issue: {str(e)}")