V45.39: fix SERVER OFFLINE lau + UI load cham - engine ghi port file (engine_port.txt), UI bootstrap doc port engine cua chinh no (get_engine_port) + timeout/retry 60s, health poll self-heal reload, khong taskkill /IM daw_engine (giet engine instance khac), /api/v1/plugins/available chay scan trong threadpool (khong chan event loop -> /health het timeout)

This commit is contained in:
2026-09-01 16:45:06 +07:00
parent 1e0314249d
commit 2c4d6c358b
7 changed files with 2849 additions and 2756 deletions
+24 -2
View File
@@ -294,7 +294,8 @@ pub fn run() {
transport_control,
set_fx_chain,
restart_bridge_with_sample_rate,
dbg_log
dbg_log,
get_engine_port
])
.setup(|app| {
// V10 (FIX_DRAG_DROP_WINDOWS): disable Tauri's built-in drag-drop
@@ -741,7 +742,12 @@ pub fn run() {
// taskkill chi nhan MOT /IM moi lan goi — goi rieng cho tung
// process, khong gop /IM ... /IM (truoc day chi cai dau chay,
// fx_vst_bridge/plugin_host song sot thanh orphan).
for im in ["daw_engine.exe", "daw_vst_bridge.exe",
// KHONG taskkill /IM daw_engine.exe: image name dung chung moi
// instance — se giet ca engine cua app KHAC dang chay (2 app
// cung luc: dong 1 cai → app kia mat server → SERVER OFFLINE).
// Engine cua chinh minh da duoc kill_process_tree o tren; con
// sot (crash/khong parent) tu thoat nho watchdog SF_PARENT_PID.
for im in ["daw_vst_bridge.exe",
"fx_vst_bridge.exe", "plugin_host.exe"] {
let _ = std::process::Command::new("taskkill")
.args(["/IM", im, "/T", "/F"])
@@ -904,6 +910,22 @@ fn bridge_status(app: AppHandle) -> Result<BridgeStatus, String> {
/// V28: JS timing diagnostics — append `{unix_ms} {tag} {extra}` to
/// %APPDATA%/SonicForgeDAW/logs/dbg.log (create dirs as needed). Fire-and-forget
/// từ app.jsx (_dbgLog) để định vị delay Play/Stop không cần devtools.
/// Port engine đã chọn (desktop_engine.py ghi %APPDATA%/SonicForgeDAW/
/// engine_port.txt ngay sau _pick_port). UI bootstrap ưu tiên dùng port này
/// để kết nối ĐÚNG engine của chính mình; -1 khi chưa có (engine đang khởi
/// động) → bootstrap retry/fallback quét 8000-8010.
#[tauri::command]
fn get_engine_port() -> i32 {
let appdata = std::env::var("APPDATA").unwrap_or_else(|_| ".".into());
let path = std::path::Path::new(&appdata)
.join("SonicForgeDAW")
.join("engine_port.txt");
std::fs::read_to_string(path)
.ok()
.and_then(|s| s.trim().parse::<i32>().ok())
.unwrap_or(-1)
}
#[tauri::command]
fn dbg_log(tag: String, extra: String) -> Result<(), String> {
let Some(appdata) = std::env::var("APPDATA").ok() else { return Ok(()) };
+34 -5
View File
@@ -20,16 +20,45 @@
// (origin tauri:// giữ nguyên -> __TAURI__ sống), base href + API_BASE_URL
// trỏ engine HTTP (CORS * đã bật).
(async function () {
// Đợi engine của CHÍNH app này (port do desktop_engine.py ghi vào
// engine_port.txt, đọc qua lệnh Tauri get_engine_port) tối đa 60s.
// Fallback: quét 8000-8010 (engine cũ/cài chồng) nhưng CÓ timeout mỗi
// port — trước đây fetch không timeout + không retry: port giữ kết nối
// không phản hồi → UI treo "Đang khởi động" vĩnh viễn; engine khởi
// động chậm (cold start) → "Không tìm thấy" dù sau đó engine sống.
var base = null;
for (var p = 8000; p <= 8010 && !base; p++) {
var deadline = Date.now() + 60000;
function withTimeout(ms) {
var ctl = new AbortController();
var t = setTimeout(function () { ctl.abort(); }, ms);
return { ctl: ctl, t: t };
}
while (!base && Date.now() < deadline) {
try {
var r = await fetch('http://127.0.0.1:' + p + '/health', { cache: 'no-store' });
if (r.ok) base = 'http://127.0.0.1:' + p + '/';
} catch (e) { /* port chưa mở */ }
if (window.__TAURI__ && window.__TAURI__.core && window.__TAURI__.core.invoke) {
var port = await window.__TAURI__.core.invoke('get_engine_port');
if (port && port > 0) {
var h = withTimeout(2000);
try {
var r = await fetch('http://127.0.0.1:' + port + '/health', { signal: h.ctl.signal, cache: 'no-store' });
if (r.ok) base = 'http://127.0.0.1:' + port + '/';
} finally { clearTimeout(h.t); }
}
}
} catch (e) { /* engine chưa ghi port — retry */ }
if (base) break;
for (var p = 8000; p <= 8010 && !base; p++) {
var h2 = withTimeout(1000);
try {
var r2 = await fetch('http://127.0.0.1:' + p + '/health', { signal: h2.ctl.signal, cache: 'no-store' });
if (r2.ok) base = 'http://127.0.0.1:' + p + '/';
} catch (e) { /* port chưa mở */ } finally { clearTimeout(h2.t); }
}
if (!base) await new Promise(function (res) { setTimeout(res, 1500); });
}
if (!base) {
document.getElementById('msg').textContent =
'Không tìm thấy SonicForge Engine (port 8000–8010). Hãy đóng và chạy lại ứng dụng. ' +
'Không tìm thấy SonicForge Engine (port 8000–8010) sau 60s. Hãy đóng và chạy lại ứng dụng. ' +
'Nếu lặp lại, xem log: %APPDATA%\\SonicForgeDAW\\logs\\engine.log';
return;
}