fix(qconnect): trigger startup auto-connect after OAuth restore, not during setup
The auto-connect spawn was firing at the end of the Tauri setup hook, before the frontend invoked runtime_bootstrap. At that point the API client is not yet initialized (bundle tokens not extracted, no auth), so service.connect always failed silently and the lifecycle stayed Off even when startup_mode was On or Remember-last-on. Move the trigger into v2_runtime_bootstrap, right after the OAuth token restore + session activation + CoreBridge auth complete. The CLI override (--enable-qconnect / --disable-qconnect) is exposed via a managed QconnectCliOverride wrapper so bootstrap can read it. cc
This commit is contained in:
3 files changed
+88
-50
No files matched your search
@@ -41,6 +41,7 @@ pub async fn runtime_bootstrap(
|
||||
app_state: State<'_, AppState>,
|
||||
core_bridge: State<'_, CoreBridgeState>,
|
||||
audio_settings: State<'_, AudioSettingsState>,
|
||||
qconnect_cli_override: State<'_, crate::qconnect::startup::QconnectCliOverride>,
|
||||
) -> Result<RuntimeStatus, RuntimeError> {
|
||||
let manager = runtime.manager();
|
||||
|
||||
@@ -213,6 +214,15 @@ pub async fn runtime_bootstrap(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// QConnect auto-connect-on-startup. Only fires here, after a successful
|
||||
// OAuth restore + session activation, because service.connect requires
|
||||
// a fully initialized client.
|
||||
crate::qconnect::startup::maybe_auto_connect_after_bootstrap(
|
||||
&app,
|
||||
qconnect_cli_override.0,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
Err(e) => {
|
||||
// Token expired — clear it and let user re-login via OAuth
|
||||
|
||||
+5
-50
@@ -809,6 +809,7 @@ pub fn run(qconnect_cli_override: Option<bool>) {
|
||||
.manage(qbzd_discovery::QbzdDiscoveryState::new())
|
||||
.manage(runtime::RuntimeManagerState::new())
|
||||
.manage(qconnect::QconnectServiceState::new())
|
||||
.manage(qconnect::startup::QconnectCliOverride(qconnect_cli_override))
|
||||
.manage(user_data_paths)
|
||||
.setup(move |app| {
|
||||
log::info!(
|
||||
@@ -1261,56 +1262,10 @@ pub fn run(qconnect_cli_override: Option<bool>) {
|
||||
}
|
||||
});
|
||||
|
||||
// QConnect auto-connect-on-startup decision.
|
||||
// Placed last so CoreBridge and all other managed state is initialized
|
||||
// before the spawned task runs service.connect.
|
||||
{
|
||||
use qconnect_app::compute_effective_startup;
|
||||
let mode = crate::qconnect::startup::load_startup_mode();
|
||||
let last = crate::qconnect::startup::load_last_known_state();
|
||||
let should_connect = compute_effective_startup(mode, qconnect_cli_override, last);
|
||||
|
||||
log::info!(
|
||||
"[QConnect] startup decision: mode={} cli_override={:?} last_known={:?} -> {}",
|
||||
mode.as_str(),
|
||||
qconnect_cli_override,
|
||||
last,
|
||||
should_connect
|
||||
);
|
||||
|
||||
if should_connect {
|
||||
let app_handle = app.handle().clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
// Best-effort: pull the managed states by handle.
|
||||
let service = app_handle.state::<crate::qconnect::QconnectServiceState>();
|
||||
let core_bridge = app_handle.state::<crate::core_bridge::CoreBridgeState>();
|
||||
let app_state = app_handle.state::<crate::AppState>();
|
||||
|
||||
// Use default options; auto-discovery via qws/createToken resolves
|
||||
// endpoint+JWT (see transport.rs::resolve_transport_config).
|
||||
let config = match crate::qconnect::transport::resolve_transport_config(
|
||||
Default::default(),
|
||||
&app_state,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
log::warn!("[QConnect] startup auto-connect transport resolve failed: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = service.connect(app_handle.clone(), core_bridge.0.clone(), config).await {
|
||||
log::warn!("[QConnect] startup auto-connect failed: {e}");
|
||||
// Lifecycle stays Off; existing reconnect loop only fires for
|
||||
// established sessions, not for failed initial connects.
|
||||
} else {
|
||||
log::info!("[QConnect] startup auto-connect succeeded");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
// QConnect auto-connect is now triggered from inside v2_runtime_bootstrap
|
||||
// after OAuth restore + session activation, so the connect call runs against
|
||||
// a fully initialized client. The CLI override is exposed via managed state
|
||||
// (QconnectCliOverride) so bootstrap can read it.
|
||||
|
||||
Ok(())
|
||||
})
|
||||
|
||||
@@ -89,3 +89,76 @@ pub fn save_last_known_state(state: bool) {
|
||||
rusqlite::params![value],
|
||||
);
|
||||
}
|
||||
|
||||
/// Tauri-managed wrapper for the volatile CLI override
|
||||
/// (`--enable-qconnect` / `--disable-qconnect`).
|
||||
///
|
||||
/// Set once in `pub fn run` and read from inside the bootstrap command,
|
||||
/// because the auto-connect dispatch must wait until OAuth restore + session
|
||||
/// activation are complete.
|
||||
pub struct QconnectCliOverride(pub Option<bool>);
|
||||
|
||||
/// Trigger QConnect auto-connect AFTER the runtime is fully bootstrapped
|
||||
/// (client init + OAuth restore + CoreBridge auth + session activation).
|
||||
///
|
||||
/// Called from inside `v2_runtime_bootstrap` after the OAuth success path
|
||||
/// completes. If startup_mode + last_known_state + cli_override resolve to
|
||||
/// "should not connect", returns early. Otherwise spawns a fire-and-forget
|
||||
/// task that mirrors the `v2_qconnect_connect` connect path.
|
||||
///
|
||||
/// On failure the lifecycle stays Off; the existing reconnect loop only
|
||||
/// fires for established sessions, not for failed initial connects.
|
||||
pub async fn maybe_auto_connect_after_bootstrap(
|
||||
app: &tauri::AppHandle,
|
||||
cli_override: Option<bool>,
|
||||
) {
|
||||
use qconnect_app::compute_effective_startup;
|
||||
use tauri::Manager;
|
||||
|
||||
let mode = load_startup_mode();
|
||||
let last = load_last_known_state();
|
||||
let should_connect = compute_effective_startup(mode, cli_override, last);
|
||||
|
||||
log::info!(
|
||||
"[QConnect] post-bootstrap startup decision: mode={} cli_override={:?} last_known={:?} -> {}",
|
||||
mode.as_str(),
|
||||
cli_override,
|
||||
last,
|
||||
should_connect
|
||||
);
|
||||
|
||||
if !should_connect {
|
||||
return;
|
||||
}
|
||||
|
||||
let app_handle = app.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let service = app_handle.state::<crate::qconnect::QconnectServiceState>();
|
||||
let app_state = app_handle.state::<crate::AppState>();
|
||||
let core_bridge = app_handle.state::<crate::core_bridge::CoreBridgeState>();
|
||||
|
||||
// Use default options; auto-discovery via qws/createToken resolves
|
||||
// endpoint+JWT (see transport.rs::resolve_transport_config).
|
||||
let config = match crate::qconnect::transport::resolve_transport_config(
|
||||
Default::default(),
|
||||
&app_state,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
log::warn!("[QConnect] startup auto-connect transport resolve failed: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = service
|
||||
.connect(app_handle.clone(), core_bridge.0.clone(), config)
|
||||
.await
|
||||
{
|
||||
log::warn!("[QConnect] startup auto-connect failed: {e}");
|
||||
} else {
|
||||
log::info!("[QConnect] startup auto-connect succeeded");
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user