From f91cdb0801df1e9f6148c98481215b418c3dc629 Mon Sep 17 00:00:00 2001 From: Bytemalte Date: Mon, 16 Mar 2026 15:44:36 +0100 Subject: [PATCH] feat: persist API keys across restarts via tauri-plugin-store Keys are saved to keys.json in the app's private data directory on both Linux and Android. load_keys is called on app mount to restore them into NewsState before the first news fetch. Co-Authored-By: Claude Sonnet 4.6 --- src-tauri/Cargo.toml | 1 + src-tauri/capabilities/default.json | 3 +- src-tauri/src/lib.rs | 6 ++-- src-tauri/src/news.rs | 43 +++++++++++++++++++++++++---- src/pages/news.rs | 3 +- 5 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index dead5be..73a5716 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -15,6 +15,7 @@ tauri-build = { version = "2", features = [] } [dependencies] tauri = { version = "2", features = [] } tauri-plugin-opener = "2" +tauri-plugin-store = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" easy-nostr = { path = "./easy-nostr" } diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 4cdbf49..5970ae6 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -5,6 +5,7 @@ "windows": ["main"], "permissions": [ "core:default", - "opener:default" + "opener:default", + "store:default" ] } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index ade47a9..5a1ae79 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -5,16 +5,18 @@ mod news; pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_opener::init()) + .plugin(tauri_plugin_store::Builder::default().build()) // Registriert den Nostr-State .manage(news::NewsState::default()) .invoke_handler(tauri::generate_handler![ home::fetch_nostr_posts, news::save_openrouter_key, + news::save_groq_key, + news::load_keys, news::load_rss_config, - news::save_rss_urls, // GeƤndert von save_rss_url zu save_rss_urls + news::save_rss_urls, news::fetch_ai_news, news::fetch_rss_news, - news::save_groq_key, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src-tauri/src/news.rs b/src-tauri/src/news.rs index 8a17e81..ac14f7e 100644 --- a/src-tauri/src/news.rs +++ b/src-tauri/src/news.rs @@ -3,6 +3,7 @@ use std::fs; use std::path::PathBuf; use std::sync::Mutex; use tauri::{AppHandle, Manager, State}; +use tauri_plugin_store::StoreExt; #[derive(Serialize, Deserialize, Default, Clone)] pub struct RssConfig { @@ -114,16 +115,46 @@ fn default_feeds() -> Vec { } #[tauri::command] -pub async fn save_openrouter_key(key: String, state: State<'_, NewsState>) -> Result<(), String> { - let mut lock = state.openrouter_key.lock().map_err(|_| "Lock failed")?; - *lock = key.trim().to_string(); +pub async fn load_keys(app: AppHandle, state: State<'_, NewsState>) -> Result<(), String> { + let store = app.store("keys.json").map_err(|e| e.to_string())?; + if let Some(v) = store.get("openrouter_key") { + if let Some(k) = v.as_str() { + *state.openrouter_key.lock().unwrap() = k.to_string(); + } + } + if let Some(v) = store.get("groq_key") { + if let Some(k) = v.as_str() { + *state.groq_key.lock().unwrap() = k.to_string(); + } + } Ok(()) } #[tauri::command] -pub async fn save_groq_key(key: String, state: State<'_, NewsState>) -> Result<(), String> { - let mut lock = state.groq_key.lock().map_err(|_| "Lock failed")?; - *lock = key.trim().to_string(); +pub async fn save_openrouter_key( + key: String, + app: AppHandle, + state: State<'_, NewsState>, +) -> Result<(), String> { + let trimmed = key.trim().to_string(); + *state.openrouter_key.lock().map_err(|_| "Lock failed")? = trimmed.clone(); + let store = app.store("keys.json").map_err(|e| e.to_string())?; + store.set("openrouter_key", trimmed); + store.save().map_err(|e| e.to_string())?; + Ok(()) +} + +#[tauri::command] +pub async fn save_groq_key( + key: String, + app: AppHandle, + state: State<'_, NewsState>, +) -> Result<(), String> { + let trimmed = key.trim().to_string(); + *state.groq_key.lock().map_err(|_| "Lock failed")? = trimmed.clone(); + let store = app.store("keys.json").map_err(|e| e.to_string())?; + store.set("groq_key", trimmed); + store.save().map_err(|e| e.to_string())?; Ok(()) } diff --git a/src/pages/news.rs b/src/pages/news.rs index ed67dd1..3f02f53 100644 --- a/src/pages/news.rs +++ b/src/pages/news.rs @@ -69,11 +69,12 @@ pub fn news() -> Html { let new_url_ref = use_node_ref(); let new_cat_ref = use_node_ref(); - // Init: RSS Feeds laden + // Init: Keys und RSS Feeds laden { let rss_feeds = rss_feeds.clone(); use_effect_with((), move |_| { spawn_local(async move { + let _ = invoke("load_keys", JsValue::NULL).await; if let Ok(res) = invoke("load_rss_config", JsValue::NULL).await { if let Ok(feeds) = serde_wasm_bindgen::from_value::>(res) { rss_feeds.set(feeds);