diff --git a/src-tauri/src/api_client.rs b/src-tauri/src/api_client.rs index f0f68a0..a89ab3c 100644 --- a/src-tauri/src/api_client.rs +++ b/src-tauri/src/api_client.rs @@ -5,8 +5,7 @@ use std::time::Duration; use chrono::Utc; use rand::Rng; -use tauri::AppHandle; -use tauri::Manager; +use tauri::{AppHandle, Emitter, Manager}; use reqwest::header::{HeaderMap, HeaderName, HeaderValue}; use serde_json::Value; @@ -161,6 +160,7 @@ pub async fn send_heartbeat(token: &str) -> Result<(), String> { let server_code = response_server_verify_code(&value); if server_code != Some(expected.as_str()) { + return Err("会员已过期".into()); } @@ -192,8 +192,11 @@ pub fn spawn_heartbeat_loop(app: AppHandle) { continue; } match send_heartbeat(&token).await { - Ok(()) => log::debug!(target: "heartbeat", "heartbeat ok"), - Err(e) => log::warn!(target: "heartbeat", "heartbeat failed: {e}"), + Ok(()) => {} + Err(e) => { + println!("[heartbeat] failed: {e}"); + let _ = app.emit("auth-heartbeat-failed", e); + } } } }); diff --git a/src/main.js b/src/main.js index afa49a4..4446687 100644 --- a/src/main.js +++ b/src/main.js @@ -1,5 +1,6 @@ import { createApp } from "vue"; import { createPinia } from "pinia"; +import { listen } from "@tauri-apps/api/event"; import PrimeVue from "primevue/config"; import { AiclientPreset } from "./theme/aiclient-preset.js"; import Button from "primevue/button"; @@ -71,6 +72,29 @@ app.component("DataTable", DataTable); app.component("Column", Column); app.component("Tag", Tag); -useAuthStore(pinia).hydrateRustSession(); +const authStore = useAuthStore(pinia); +authStore.hydrateRustSession(); + +function setupHeartbeatLogoutListener() { + if (typeof window === "undefined" || !("__TAURI_INTERNALS__" in window)) { + return; + } + listen("auth-heartbeat-failed", async (event) => { + const message = + typeof event.payload === "string" + ? event.payload + : event.payload?.message ?? "会话已失效"; + console.warn("[heartbeat] failed:", message); + if (!authStore.isAuthenticated) { + return; + } + await authStore.logout(); + if (router.currentRoute.value.name !== "login") { + await router.replace({ name: "login" }); + } + }); +} + +setupHeartbeatLogoutListener(); app.mount("#app");