FIX: lỗi màn hình đen trên docker

This commit is contained in:
2026-08-08 10:51:58 +07:00
parent 3fc2087ee1
commit 24051a2794
7 changed files with 101 additions and 30 deletions
+30 -13
View File
@@ -15543,6 +15543,16 @@ const App = () => {
// Auth / User State
const [currentUser, setCurrentUser] = useState(null);
const [authModalOpen, setAuthModalOpen] = useState(false);
// 401 bt k m màn login (bug 09:45 màn đen khi token hng/hết hn)
React.useEffect(() => {
window.__onAuthFail = () => {
setCurrentUser(null);
setIsMandatoryLogin(true);
setAuthMode('login');
setAuthModalOpen(true);
};
return () => { delete window.__onAuthFail; };
}, []);
const [authMode, setAuthMode] = useState('login'); // 'login' | 'register' | 'force_change'
const [isMandatoryLogin, setIsMandatoryLogin] = useState(false);
const [profileModalOpen, setProfileModalOpen] = useState(false);
@@ -15847,20 +15857,27 @@ const App = () => {
restoreLastSessionProject();
}
} catch (err) {
const cached = localStorage.getItem('sonic_user');
if (cached) {
try { setCurrentUser(JSON.parse(cached)); } catch (_) { }
setIsMandatoryLogin(false);
setAuthModalOpen(false);
restoreLastSessionProject();
} else {
localStorage.removeItem('sonic_token');
localStorage.removeItem('sonic_user');
setCurrentUser(null);
setIsMandatoryLogin(true);
setAuthMode('login');
setAuthModalOpen(true);
// 401 (token hng/đi SECRET_KEY bug 09:45: dùng cached user vào app
// nhưng mi API 401 màn đen không login) B qua cached m LOGIN.
// detail server: "Thiếu Token xác thc..." / "Token đã hết hn..." (auth.py)
const errMsg = String((err && err.message) || err || '');
const isAuthErr = errMsg.includes('Token') || errMsg.includes('401') || errMsg.includes('Unauthorized') || errMsg.includes('unauthorized') || errMsg.includes('Không xác thực');
if (!isAuthErr) {
const cached = localStorage.getItem('sonic_user');
if (cached) {
try { setCurrentUser(JSON.parse(cached)); } catch (_) { }
setIsMandatoryLogin(false);
setAuthModalOpen(false);
restoreLastSessionProject();
return;
}
}
localStorage.removeItem('sonic_token');
localStorage.removeItem('sonic_user');
setCurrentUser(null);
setIsMandatoryLogin(true);
setAuthMode('login');
setAuthModalOpen(true);
}
};
checkAuthStatus();
+6 -2
View File
@@ -810,7 +810,8 @@ const prevIsPlayingRef=useRef(false);useEffect(()=>{const wasPlaying=prevIsPlayi
try{const _tf=window.__trigFrame=(window.__trigFrame||0)+1;if(_tf%20===1){console.log('[VUTrig]',trackId,'vel=',velocity,'peak=',peak.toFixed(2),'play=',isPlaying,'tab=',activeTabRef.current);}}catch(e){}};window.triggerMidiVuActivity=triggerMidiVuActivity;// ── Temp Edit Tab (LOOP_EDITOR_2.md §1.2 Sub Tab) ──
const[tempTabActive,setTempTabActive]=useState(false);const[tempTabBuffer,setTempTabBuffer]=useState(null);const[tempTabTrackId,setTempTabTrackId]=useState(null);const[tempTabOrigStart,setTempTabOrigStart]=useState(0);const[tempTabOrigEnd,setTempTabOrigEnd]=useState(0);const tempTabCanvasRef=useRef(null);// Effect parameters for temp tab
const[tempTabEffects,setTempTabEffects]=useState({reverse:false,gainDb:0,fadeInMs:0,fadeOutMs:0});// ── Auth / User State ──
const[currentUser,setCurrentUser]=useState(null);const[authModalOpen,setAuthModalOpen]=useState(false);const[authMode,setAuthMode]=useState('login');// 'login' | 'register' | 'force_change'
const[currentUser,setCurrentUser]=useState(null);const[authModalOpen,setAuthModalOpen]=useState(false);// 401 bất kỳ → mở màn login (bug 09:45 — màn đen khi token hỏng/hết hạn)
React.useEffect(()=>{window.__onAuthFail=()=>{setCurrentUser(null);setIsMandatoryLogin(true);setAuthMode('login');setAuthModalOpen(true);};return()=>{delete window.__onAuthFail;};},[]);const[authMode,setAuthMode]=useState('login');// 'login' | 'register' | 'force_change'
const[isMandatoryLogin,setIsMandatoryLogin]=useState(false);const[profileModalOpen,setProfileModalOpen]=useState(false);const[systemManagerModalOpen,setSystemManagerModalOpen]=useState(false);const[aiConfigModalOpen,setAiConfigModalOpen]=useState(false);const[aiPresetModalOpen,setAiPresetModalOpen]=useState(false);const[aiPresetVersion,setAiPresetVersion]=useState(0);const[showMasteringModal,setShowMasteringModal]=useState(false);// Unified FX Rack target context (unified_fx_rack_panel.md): { trackId, trackName } | null
const[fxRackTarget,setFxRackTarget]=useState(null);window.__openFxRack=(trackId,trackName)=>setFxRackTarget({trackId,trackName});const[masteringSettings,setMasteringSettings]=useState({masterConnected:false,activeModule:'eq',eqActive:true,imagerActive:true,maximizerActive:true,eqLowGain:1.5,eqMid1Gain:-1.0,eqMid2Gain:2.0,eqHighGain:1.8,w1:0,w2:115,w3:135,w4:150,imagerScale:'v2',maxGain:5.4,maxUpward:2.0,maxSoftClip:15,maxTransient:25,ceiling:-0.1,isBypassed:false,chain:DEFAULT_MASTER_CHAIN.map(m=>({...m})),compActive:false,compThreshold:-16,compRatio:3,compMakeup:0,limActive:false,limThreshold:-1.0,excActive:false,excDrive:40,rebalActive:false,rebalMid:0,rebalSide:0});useEffect(()=>{window.currentMasteringSettings=masteringSettings;if(audioCtx&&masterBus){toggleMasteringOnMaster(masteringSettings.masterConnected,masteringSettings.isBypassed);applyMasteringSettings(masteringSettings);// Re-sync live track routes: mastering ON → mọi track qua chain (♪ bị
// override bởi effMidiBypass/effAudioBypass) — nút PWR bật/tắt phải áp
@@ -835,7 +836,10 @@ lastId=latest.id;lastName=latest.name||lastName;const proj=await window.SonicAPI
if(result.zoom>1&&setZoom)setZoom(result.zoom);if(result.masteringSettings)setMasteringSettings(result.masteringSettings);}else{restoredTracks=(parsed.tracks||[]).map(function(t){var rest=Object.assign({},t);delete rest.height;return Object.assign({},rest,{buffer:null,channelInfo:t.channelInfo||null,clips:t.clips||[],serverFileId:t.serverFileId||null});});}setTracks(restoredTracks);loadAudioBuffersForTracks(restoredTracks).catch(function(err){console.warn('loadAudioBuffersForTracks error:',err);});trackMidiChannelsRef.current={};preloadTrackInstruments(restoredTracks).catch(function(err){console.warn('preloadTrackInstruments error:',err);});setBpm(restoredBpm.toString());setSelectedTrackId(restoredTracks[0]?.id||'1');setProjectName(lastName);setCurrentProjectId(lastId);localStorage.setItem('sonic_project_id',lastId);setSessionTabs(restoredSessionTabs);setSubTabs(restoredSubTabs);// DIAG section-tab block: log tracks + sections của section tab sau restore
try{(restoredSessionTabs||[]).forEach(function(st){var secCount=0,midiCount=0,clipCount=0;(st.tracks||[]).forEach(function(tr){secCount+=(tr.sections||[]).length;midiCount+=(tr.midiItems||[]).length;clipCount+=(tr.clips||[]).length;});console.log('[Restore] sessionTab',st.id,'sectionId',st.sectionId,'tracks',(st.tracks||[]).length,'sections',secCount,'midi',midiCount,'clips',clipCount);});}catch(e){}var restoredItemCount=0;restoredTracks.forEach(function(rt){if(rt.clips)restoredItemCount+=rt.clips.length;if(rt.midiItems)restoredItemCount+=rt.midiItems.length;if(rt.sections)restoredItemCount+=rt.sections.length;});showToast('Đã khôi phục dự án "'+lastName+'" ('+restoredItemCount+' items).','info');}catch(e){console.warn('restoreLastSessionProject failed:',e);// Stale session id (project deleted / DB reset): clear it so the error
// does not repeat on every page load.
localStorage.removeItem('sonic_project_id');localStorage.removeItem('sonic_project_name');}};useEffect(()=>{const checkAuthStatus=async()=>{const savedToken=localStorage.getItem('sonic_token');if(!savedToken){setIsMandatoryLogin(true);setAuthMode('login');setAuthModalOpen(true);return;}try{const profile=await window.SonicAPI.getProfile();setCurrentUser(profile);localStorage.setItem('sonic_user',JSON.stringify(profile));if(profile.must_change_password){setIsMandatoryLogin(true);setAuthMode('force_change');setAuthModalOpen(true);}else{setIsMandatoryLogin(false);setAuthModalOpen(false);restoreLastSessionProject();}}catch(err){const cached=localStorage.getItem('sonic_user');if(cached){try{setCurrentUser(JSON.parse(cached));}catch(_){}setIsMandatoryLogin(false);setAuthModalOpen(false);restoreLastSessionProject();}else{localStorage.removeItem('sonic_token');localStorage.removeItem('sonic_user');setCurrentUser(null);setIsMandatoryLogin(true);setAuthMode('login');setAuthModalOpen(true);}}};checkAuthStatus();},[]);const loadPendingSfsProject=()=>{const proj=window.__pendingSfsProject;if(!proj)return;try{let restoredTracks=[];let restoredBpm=bpm;let restoredSessionTabs=[];let restoredSubTabs=[];if(proj.main_session){const result=deserializeProjectFromSchema(proj);restoredTracks=result.tracks;restoredBpm=result.bpm;restoredSessionTabs=result.sessionTabs;restoredSubTabs=result.subTabs;if(result.masteringSettings){setMasteringSettings(result.masteringSettings);}else{setMasteringSettings({masterConnected:false,activeModule:'eq',eqActive:true,imagerActive:true,maximizerActive:true,eqLowGain:1.5,eqMid1Gain:-1.0,eqMid2Gain:2.0,eqHighGain:1.8,w1:0,w2:115,w3:135,w4:150,imagerScale:'v2',maxGain:5.4,maxUpward:2.0,maxSoftClip:15,maxTransient:25,ceiling:-0.1,isBypassed:false,chain:DEFAULT_MASTER_CHAIN.map(m=>({...m})),compActive:false,compThreshold:-16,compRatio:3,compMakeup:0,limActive:false,limThreshold:-1.0,excActive:false,excDrive:40,rebalActive:false,rebalMid:0,rebalSide:0});}}else{restoredTracks=(proj.tracks||[]).map(t=>{const{height:_h,...rest}=t;return{...rest,buffer:null,channelInfo:t.channelInfo||null,clips:t.clips||[],serverFileId:t.serverFileId||null};});}if(restoredTracks.length>0){setTracks(restoredTracks);setBpm(restoredBpm.toString());if(restoredSessionTabs.length>0){setSessionTabs(restoredSessionTabs);}if(restoredSubTabs.length>0){setSubTabs(restoredSubTabs);}showToast(`Đã tải dự án "${proj.metadata?.title||proj.name||'Dự án mới'}" từ liên kết .sfs thành công!`,"success");}}catch(e){showToast("Lỗi tải dự án từ .sfs","error");}finally{window.__pendingSfsProject=null;}};const handleAuthSuccess=user=>{setCurrentUser(user);if(user.must_change_password){setIsMandatoryLogin(true);setAuthMode('force_change');setAuthModalOpen(true);}else{setIsMandatoryLogin(false);setAuthModalOpen(false);restoreLastSessionProject();const loadPrefs=p=>{if(!p)return;if(p.showAIPanel!==undefined)setShowAIPanel(p.showAIPanel);if(p.showExportPanel!==undefined)setShowExportPanel(p.showExportPanel);if(p.showSelectionPanel!==undefined)setShowSelectionPanel(p.showSelectionPanel);if(p.showPythonToolsPanel!==undefined)setShowPythonToolsPanel(p.showPythonToolsPanel);if(p.showMediaExplorer!==undefined)setShowMediaExplorer(p.showMediaExplorer);if(p.showFxRack!==undefined)setShowFxRack(p.showFxRack);if(p.showMidiEvents!==undefined)setShowMidiEvents(p.showMidiEvents);if(p.panelPositions)setPanelPositions(p.panelPositions);if(p.rightSidebarWidth)setRightSidebarWidth(p.rightSidebarWidth);if(p.mediaExplorerHeight)setMediaExplorerHeight(p.mediaExplorerHeight);if(p.selectedProviderId)setSelectedProviderId(p.selectedProviderId);};(async()=>{try{const data=await window.SonicAPI.getPreferences();if(data&&data.preferences)loadPrefs(data.preferences);else{const cached=localStorage.getItem('sonic_preferences');if(cached)loadPrefs(JSON.parse(cached));}}catch(e){const cached=localStorage.getItem('sonic_preferences');if(cached)loadPrefs(JSON.parse(cached));}try{const data=await window.SonicAPI.getAIConfigs();if(data&&data.providers){setAiProviders(data.providers);const active=data.providers.find(p=>p.is_active)||data.providers[0];if(active)setSelectedProviderId(active.id);}}catch(e){}try{window.SonicAPI.getSoundfontCatalog().then(cat=>{window.__soundfontCatalog=cat;}).catch(()=>{});}catch(e){}// Re-fetch instrument data after auth (useEffect on mount runs before token is set)
localStorage.removeItem('sonic_project_id');localStorage.removeItem('sonic_project_name');}};useEffect(()=>{const checkAuthStatus=async()=>{const savedToken=localStorage.getItem('sonic_token');if(!savedToken){setIsMandatoryLogin(true);setAuthMode('login');setAuthModalOpen(true);return;}try{const profile=await window.SonicAPI.getProfile();setCurrentUser(profile);localStorage.setItem('sonic_user',JSON.stringify(profile));if(profile.must_change_password){setIsMandatoryLogin(true);setAuthMode('force_change');setAuthModalOpen(true);}else{setIsMandatoryLogin(false);setAuthModalOpen(false);restoreLastSessionProject();}}catch(err){// 401 (token hỏng/đổi SECRET_KEY — bug 09:45: dùng cached user → vào app
// nhưng mọi API 401 → màn đen không login) → BỎ qua cached → mở LOGIN.
// detail server: "Thiếu Token xác thực..." / "Token đã hết hạn..." (auth.py)
const errMsg=String(err&&err.message||err||'');const isAuthErr=errMsg.includes('Token')||errMsg.includes('401')||errMsg.includes('Unauthorized')||errMsg.includes('unauthorized')||errMsg.includes('Không xác thực');if(!isAuthErr){const cached=localStorage.getItem('sonic_user');if(cached){try{setCurrentUser(JSON.parse(cached));}catch(_){}setIsMandatoryLogin(false);setAuthModalOpen(false);restoreLastSessionProject();return;}}localStorage.removeItem('sonic_token');localStorage.removeItem('sonic_user');setCurrentUser(null);setIsMandatoryLogin(true);setAuthMode('login');setAuthModalOpen(true);}};checkAuthStatus();},[]);const loadPendingSfsProject=()=>{const proj=window.__pendingSfsProject;if(!proj)return;try{let restoredTracks=[];let restoredBpm=bpm;let restoredSessionTabs=[];let restoredSubTabs=[];if(proj.main_session){const result=deserializeProjectFromSchema(proj);restoredTracks=result.tracks;restoredBpm=result.bpm;restoredSessionTabs=result.sessionTabs;restoredSubTabs=result.subTabs;if(result.masteringSettings){setMasteringSettings(result.masteringSettings);}else{setMasteringSettings({masterConnected:false,activeModule:'eq',eqActive:true,imagerActive:true,maximizerActive:true,eqLowGain:1.5,eqMid1Gain:-1.0,eqMid2Gain:2.0,eqHighGain:1.8,w1:0,w2:115,w3:135,w4:150,imagerScale:'v2',maxGain:5.4,maxUpward:2.0,maxSoftClip:15,maxTransient:25,ceiling:-0.1,isBypassed:false,chain:DEFAULT_MASTER_CHAIN.map(m=>({...m})),compActive:false,compThreshold:-16,compRatio:3,compMakeup:0,limActive:false,limThreshold:-1.0,excActive:false,excDrive:40,rebalActive:false,rebalMid:0,rebalSide:0});}}else{restoredTracks=(proj.tracks||[]).map(t=>{const{height:_h,...rest}=t;return{...rest,buffer:null,channelInfo:t.channelInfo||null,clips:t.clips||[],serverFileId:t.serverFileId||null};});}if(restoredTracks.length>0){setTracks(restoredTracks);setBpm(restoredBpm.toString());if(restoredSessionTabs.length>0){setSessionTabs(restoredSessionTabs);}if(restoredSubTabs.length>0){setSubTabs(restoredSubTabs);}showToast(`Đã tải dự án "${proj.metadata?.title||proj.name||'Dự án mới'}" từ liên kết .sfs thành công!`,"success");}}catch(e){showToast("Lỗi tải dự án từ .sfs","error");}finally{window.__pendingSfsProject=null;}};const handleAuthSuccess=user=>{setCurrentUser(user);if(user.must_change_password){setIsMandatoryLogin(true);setAuthMode('force_change');setAuthModalOpen(true);}else{setIsMandatoryLogin(false);setAuthModalOpen(false);restoreLastSessionProject();const loadPrefs=p=>{if(!p)return;if(p.showAIPanel!==undefined)setShowAIPanel(p.showAIPanel);if(p.showExportPanel!==undefined)setShowExportPanel(p.showExportPanel);if(p.showSelectionPanel!==undefined)setShowSelectionPanel(p.showSelectionPanel);if(p.showPythonToolsPanel!==undefined)setShowPythonToolsPanel(p.showPythonToolsPanel);if(p.showMediaExplorer!==undefined)setShowMediaExplorer(p.showMediaExplorer);if(p.showFxRack!==undefined)setShowFxRack(p.showFxRack);if(p.showMidiEvents!==undefined)setShowMidiEvents(p.showMidiEvents);if(p.panelPositions)setPanelPositions(p.panelPositions);if(p.rightSidebarWidth)setRightSidebarWidth(p.rightSidebarWidth);if(p.mediaExplorerHeight)setMediaExplorerHeight(p.mediaExplorerHeight);if(p.selectedProviderId)setSelectedProviderId(p.selectedProviderId);};(async()=>{try{const data=await window.SonicAPI.getPreferences();if(data&&data.preferences)loadPrefs(data.preferences);else{const cached=localStorage.getItem('sonic_preferences');if(cached)loadPrefs(JSON.parse(cached));}}catch(e){const cached=localStorage.getItem('sonic_preferences');if(cached)loadPrefs(JSON.parse(cached));}try{const data=await window.SonicAPI.getAIConfigs();if(data&&data.providers){setAiProviders(data.providers);const active=data.providers.find(p=>p.is_active)||data.providers[0];if(active)setSelectedProviderId(active.id);}}catch(e){}try{window.SonicAPI.getSoundfontCatalog().then(cat=>{window.__soundfontCatalog=cat;}).catch(()=>{});}catch(e){}// Re-fetch instrument data after auth (useEffect on mount runs before token is set)
try{window.SonicAPI.listPlugins().then(async data=>{try{const catResp=(await window.SonicAPI.getSoundfontCatalog?.())??(await fetch('/api/v1/plugins/soundfonts/catalog').then(r=>r.json()));const catalog=catResp.full_catalog||{};data.soundfonts=(data.soundfonts||[]).map(sf=>{const sfId=sf.id.replace('sf_','');const catEntry=catalog[sfId.toLowerCase()]||catalog[sfId];if(catEntry&&catEntry.instruments)return{...sf,presets:catEntry.instruments};return sf;});}catch(e){console.warn('Catalog fetch error:',e);}setInstrumentSelectorData(data);}).catch(()=>{});}catch(e){}})();}};const handleLogout=()=>{localStorage.removeItem('sonic_token');localStorage.removeItem('sonic_user');setCurrentUser(null);setIsMandatoryLogin(true);setAuthMode('login');setAuthModalOpen(true);};// ── Temp project auto-save (local + server) ──
useEffect(()=>{const serializeSafe=arr=>(arr||[]).map(t=>({id:t.id,name:t.name,startTime:t.startTime,height:t.height,volumeDb:t.volumeDb,pan:t.pan,muted:t.muted,solo:t.solo,color:t.color,markers:t.markers||[],serverFileId:t.serverFileId||null,channelInfo:t.channelInfo?{channels:t.channelInfo.channels,isStereo:t.channelInfo.isStereo,label:t.channelInfo.label}:null}));window.SonicStorage.scheduleTempAutoSave(()=>{return serializeProjectToSchema(currentProjectId||'temp_project',projectName||'Dự án tạm chưa lưu',bpm,tracks,subTabs,sessionTabs,masteringSettings);});},[tracks,subTabs,sessionTabs,masteringSettings]);// ── Timer-based auto-save (5 min) + backup (30 min) ──
useEffect(()=>{const BACKUP_MAX_KEY='sonic_backup_max_count';if(!localStorage.getItem(BACKUP_MAX_KEY)){localStorage.setItem(BACKUP_MAX_KEY,'10');}const interval5=setInterval(()=>{if(!currentProjectId)return;// Auto-save: giống handleSaveProject
+2
View File
@@ -22,6 +22,8 @@ window.API_BASE_URL = window.API_BASE_URL || window.location.origin;
if (response.status === 401) {
localStorage.removeItem('sonic_token');
localStorage.removeItem('sonic_user');
// Mở màn hình đăng nhập ngay (bug 09:45 — màn đen khi session hết hạn)
if (window.__onAuthFail) window.__onAuthFail();
}
const data = await response.json().catch(() => ({}));
+2 -2
View File
@@ -12,7 +12,7 @@
<script src="https://unpkg.com/react@18.3.1/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.production.min.js"></script>
<script src="/static/js/services/fluidsynthLoader.js?v=202607271245"></script>
<script src="/static/js/services/api.js?v=202607271016"></script>
<script src="/static/js/services/api.js?v=202608080950"></script>
<script src="/static/js/services/audioEngine.js?v=202607271016"></script>
<script src="/static/js/services/storage.js?v=202608038200"></script>
<script src="/static/js/services/soundfontStorage.js?v=202607271016"></script>
@@ -24,7 +24,7 @@
<script src="/static/js/services/midiExtractor.js?v=202607281052"></script>
<script src="/static/js/services/promptTemplateManager.js?v=202607281039"></script>
<script src="/static/js/services/undoRedoEngine.js?v=202607290941"></script>
<script src="/static/js/app.precompiled.js?v=202608080820" defer></script>
<script src="/static/js/app.precompiled.js?v=202608080950" defer></script>
<link rel="stylesheet" href="/static/css/styles.css?v=202607271016">
<style>
:root {
+4 -10
View File
@@ -12,9 +12,7 @@ services:
- "${WEB_PORT:-8000}:8000"
env_file: .env
volumes:
- sf_uploads:/app/app/storage/uploads
- sf_processed:/app/app/storage/processed
- sf_db:/app/app/storage
- sf_data:/app/app/storage
- ${VST3_DIR:-./vst_plugins}:/opt/daw_engine/vst3:ro
- ${SOUNDFONTS_DIR:-./soundfonts}:/opt/daw_engine/soundfonts:ro
- ${PIANOBOOK_DIR:-./samples/pianobook}:/opt/daw_engine/samples/pianobook:ro
@@ -33,9 +31,7 @@ services:
command: celery -A app.tasks.worker.celery_app worker --loglevel=info
env_file: .env
volumes:
- sf_uploads:/app/app/storage/uploads
- sf_processed:/app/app/storage/processed
- sf_db:/app/app/storage
- sf_data:/app/app/storage
- ${VST3_DIR:-./vst_plugins}:/opt/daw_engine/vst3:ro
- ${SOUNDFONTS_DIR:-./soundfonts}:/opt/daw_engine/soundfonts:ro
- ${PIANOBOOK_DIR:-./samples/pianobook}:/opt/daw_engine/samples/pianobook:ro
@@ -48,11 +44,9 @@ services:
command: celery -A app.tasks.worker.celery_app beat --loglevel=info
env_file: .env
volumes:
- sf_db:/app/app/storage
- sf_data:/app/app/storage
depends_on:
- redis
volumes:
sf_uploads:
sf_processed:
sf_db:
sf_data:
+25 -3
View File
@@ -1,13 +1,35 @@
"""Entry point cho daw_engine.exe (PyInstaller — spec desktop 2026-08-08).
Chạy FastAPI backend trên 127.0.0.1:8000 (Dual-Process Desktop Architecture).
KHÔNG tự mở browser — Tauri shell (window url=localhost:8000) đảm nhiệm;
mở browser thủ công khi chạy standalone (bug 09:30: exe mở tab liên tục)."""
KHÔNG tự mở browser — Tauri shell (window url=localhost:8000) đảm nhiệm.
SINGLE-INSTANCE: nếu port 8000 đã có server → EXIT ngay (bug 09:55 — mỗi lần mở
app spawn thêm daw_engine → nhiều instance + port chiếm → màn đen)."""
import os
import socket
import sys
os.environ.setdefault("SFDATA_DIR", os.path.join(os.path.expanduser("~"), "SonicForgeDAW"))
import uvicorn # noqa: E402
PORT = 8000
def _port_in_use(port: int) -> bool:
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.bind(("127.0.0.1", port))
return False
except OSError:
return True
finally:
s.close()
except Exception:
return False
if __name__ == "__main__":
uvicorn.run("app.main:app", host="127.0.0.1", port=8000, log_level="warning")
if _port_in_use(PORT):
# Server đang chạy (instance khác — window sẽ dùng nó). KHÔNG spawn thêm.
sys.exit(0)
uvicorn.run("app.main:app", host="127.0.0.1", port=PORT, log_level="warning")
+32
View File
@@ -3030,3 +3030,35 @@
(3) `build_windows.bat``taskkill /F /T /IM` (tree).
- **Các file ảnh hưởng:** main.py, src-tauri/src/lib.rs, build_windows.bat, wiki.md.
- **Ghi chú/Test:** build lại exe + tauri → đóng app → Task Manager không còn daw_engine (cả tree) + không mở tab mới. Kill tay lúc này: `taskkill /F /T /IM daw_engine.exe`.
### [2026-08-08 09:40] Task: Docker compose không chạy — THIẾU .env (tạo ở task 08-07 nhưng chưa có file .env)
- **Kiểm tra:** `docker-compose.prod.yml` (tạo task 08-07) — `env_file: .env` × 3 services (web/worker/beat) — NHƯNG **`.env` KHÔNG TỒN TẠI** (chỉ `.env.example` 516B) → `docker compose up` → lỗi `env file .env not found` → không chạy.
- **FIX:**
(1) TẠO `.env` (từ .env.example — REDIS_URL/CELERY/OPENAI/SECRET_KEY/IMAGE — SECRET_KEY đổi giá trị mặc định; ghi chú đổi trước production).
(2) `docker-compose.prod.yml` — tối giản volumes: bỏ 3 volume chồng (sf_uploads/sf_processed/sf_db — sf_db bao cả storage) → **1 volume `sf_data:/app/app/storage`**.
- **Lưu ý khác:** image `${IMAGE:-sonicforge-studio:latest}` — phải `docker build -t sonicforge-studio:latest .` TRƯỚC (hoặc set IMAGE=registry...); `docker-compose.prod.yml` dùng cho PRODUCTION (không mount source) — dev dùng `docker-compose.yml` cũ.
- **Các file ảnh hưởng:** .env (mới), docker-compose.prod.yml, wiki.md.
- **Ghi chú/Test:** `docker compose -f docker-compose.prod.yml up -d` — hết lỗi env file; nếu image chưa build → `docker build -t sonicforge-studio:latest .` trước.
### [2026-08-08 09:45] Task: FIX màn đen — 401 không mở màn login (dùng cached user sai)
- **Log bản mới:** `GET /api/v1/auth/profile 401` + `/plugins/available 401` — root rỗng (màn đen).
- **Kịch bản:** browser còn `sonic_token` CŨ (ký SECRET_KEY cũ — .env mới đổi secret) + `sonic_user` CACHED → `checkAuthStatus` profile 401 → `catch` DÙNG cached user → vào app "như đã login" → mọi API 401 (token đã bị xóa) → giao diện không dữ liệu = đen, KHÔNG mở login.
- **FIX:**
(1) `app.jsx checkAuthStatus catch` — phát hiện 401 (errMsg chứa 401/Unauthorized/Không xác thực) → BỎ QUA cached → xóa token + mở AuthModal(login); cached chỉ dùng khi lỗi MẠNG (offline).
(2) `services/api.js` — 401 bất kỳ → `window.__onAuthFail()` (mở login ngay).
(3) `app.jsx` — đăng ký `window.__onAuthFail` (setCurrentUser null + mở login).
- **Các file ảnh hưởng:** app/static/js/app.jsx, app/static/js/services/api.js, app/templates/index.html (?v=202608080945), wiki.md. Build PASS.
- **Ghi chú/Test:** hard refresh server → 401 → HIỆN MÀN ĐĂNG NHẬP (thay vì đen) → đăng nhập admin (DEFAULT_ADMIN_PASSWORD trong .env) → vào app. (User cũ có token hỏng → tự động về login.)
### [2026-08-08 09:50] Task: FIX màn đen (2) — 401 detail tiếng Việt không khớp matcher
- **Vẫn đen sau 0945:** log `GET /api/v1/plugins/available 401` — checkAuthStatus catch — isAuthErr FALSE vì 401 detail của server (auth.py) = "Thiếu Token xác thực hoặc Token không hợp lệ" / "Token đã hết hạn hoặc không hợp lệ" — KHÔNG chứa '401'/'Unauthorized' → cached user vẫn được dùng → đen.
- **FIX (app.jsx):** matcher thêm `errMsg.includes('Token')` (phủ cả 2 detail của auth.py).
- **Các file ảnh hưởng:** app/static/js/app.jsx, app/templates/index.html (?v=202608080950), wiki.md. Build PASS.
- **Ghi chú/Test:** hard refresh (Ctrl+Shift+R — nạp ?v=0950) → 401 → MÀN ĐĂNG NHẬP hiện → login admin/admin123 (hoặc DEFAULT_ADMIN_PASSWORD trong .env) → vào app.
### [2026-08-08 09:55] Task: FIX desktop — nhiều daw_engine + không tắt được + màn đen
- **Lỗi:** mỗi lần mở app → spawn thêm daw_engine (cũ không tắt — kill thiếu /T hoặc Tauri crash không kịp kill) → nhiều instance → port 8000 chiếm → lần mở sau: window load vào server cũ / không bind được → MÀN ĐEN (giống docker — nhưng nguyên nhân khác: instance/port).
- **FIX (main.py — entry daw_engine.exe):** SINGLE-INSTANCE — kiểm tra port 8000 (socket bind test) — NẾU ĐÃ CÓ server → `sys.exit(0)` (window dùng server đang chạy — không spawn thêm).
- **SECRET_KEY:** đã ổn định (env > {STORAGE}/.secret_key persist > ephemeral) — không phải nguyên nhân.
- **Các file ảnh hưởng:** main.py, wiki.md. (Cần build lại exe: pyinstaller engine.spec --clean --noconfirm → copy sidecar → npx tauri build.)
- **Kill tay lúc này:** `taskkill /F /T /IM daw_engine.exe` (có /T — giết CẢ tree — PyInstaller onefile parent+child).