feat: persist API keys across restarts via tauri-plugin-store
All checks were successful
Android Universal Build / build-android (push) Successful in 12m9s

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 <noreply@anthropic.com>
This commit is contained in:
2026-03-16 15:44:36 +01:00
parent a91537fccf
commit f91cdb0801
5 changed files with 46 additions and 10 deletions

View File

@@ -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" }

View File

@@ -5,6 +5,7 @@
"windows": ["main"],
"permissions": [
"core:default",
"opener:default"
"opener:default",
"store:default"
]
}

View File

@@ -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");

View File

@@ -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<RssFeed> {
}
#[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(())
}

View File

@@ -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::<Vec<RssFeed>>(res) {
rss_feeds.set(feeds);