From 71a2290b8f52220e0aed771677580c50c6abc8fb Mon Sep 17 00:00:00 2001 From: reya Date: Wed, 6 Mar 2024 14:40:00 +0700 Subject: [PATCH] feat: add backup dialog --- apps/desktop2/src/components/backup.tsx | 92 +++++++++++++++++-------- packages/ark/src/ark.ts | 16 +++-- src-tauri/src/commands/folder.rs | 6 +- src-tauri/src/main.rs | 27 ++++++-- src-tauri/src/nostr/keys.rs | 31 +++++++-- 5 files changed, 121 insertions(+), 51 deletions(-) diff --git a/apps/desktop2/src/components/backup.tsx b/apps/desktop2/src/components/backup.tsx index c124e665..24f68e55 100644 --- a/apps/desktop2/src/components/backup.tsx +++ b/apps/desktop2/src/components/backup.tsx @@ -1,13 +1,36 @@ -import { CancelIcon } from "@lume/icons"; +import { ArrowRightIcon, CancelIcon } from "@lume/icons"; import * as Dialog from "@radix-ui/react-dialog"; +import { Link, useParams } from "@tanstack/react-router"; +import { invoke } from "@tauri-apps/api/core"; import { useState } from "react"; +import { toast } from "sonner"; export function BackupDialog() { - const [key, setKey] = useState(""); + // @ts-ignore, magic!!! + const { account } = useParams({ strict: false }); + + const [key, setKey] = useState(null); const [passphase, setPassphase] = useState(""); + const [loading, setLoading] = useState(false); const encryptKey = async () => { - console.log("****"); + try { + setLoading(true); + + const encrypted: string = await invoke("get_encrypted_key", { + npub: account, + password: passphase, + }); + + if (encrypted) { + setKey(encrypted); + } + + setLoading(false); + } catch (e) { + setLoading(false); + toast.error(String(e)); + } }; return ( @@ -41,20 +64,7 @@ export function BackupDialog() {
- - -
-
- +
setPassphase(e.target.value)} className="h-11 w-full resize-none rounded-lg border-transparent bg-neutral-100 placeholder:text-neutral-600 focus:border-blue-500 focus:ring focus:ring-blue-100 dark:bg-neutral-900 dark:focus:ring-blue-900" /> - {passphase.length ? ( -
- -
- ) : null}
+ {key ? ( +
+ + +
+ ) : null} +
+
+ {!key ? ( + + ) : ( + + I've safely store my account key + + )}
diff --git a/packages/ark/src/ark.ts b/packages/ark/src/ark.ts index 340864fa..bc79b58e 100644 --- a/packages/ark/src/ark.ts +++ b/packages/ark/src/ark.ts @@ -22,15 +22,17 @@ export class Ark { public async get_all_accounts() { try { const accounts: Account[] = []; - const cmd: string[] = await invoke("get_all_nsecs"); + const cmd: string[] = await invoke("get_accounts"); - for (const item of cmd) { - accounts.push({ npub: item.replace(".npub", "") }); + if (cmd) { + for (const item of cmd) { + accounts.push({ npub: item.replace(".npub", "") }); + } + + this.accounts = accounts; + return accounts; } - - this.accounts = accounts; - return accounts; - } catch (e) { + } catch { return []; } } diff --git a/src-tauri/src/commands/folder.rs b/src-tauri/src/commands/folder.rs index 2ab711e9..723d1895 100644 --- a/src-tauri/src/commands/folder.rs +++ b/src-tauri/src/commands/folder.rs @@ -49,10 +49,10 @@ pub async fn show_in_folder(path: String) { } #[tauri::command] -pub fn get_all_nsecs(app_handle: tauri::AppHandle) -> Result, ()> { - let dir = app_handle.path().app_config_dir().unwrap(); +pub fn get_accounts(app_handle: tauri::AppHandle) -> Result, ()> { + let dir = app_handle.path().home_dir().unwrap(); - if let Ok(paths) = std::fs::read_dir(dir) { + if let Ok(paths) = std::fs::read_dir(dir.join("Lume/")) { let files = paths .filter_map(|res| res.ok()) .map(|dir_entry| dir_entry.path()) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 1cae5a35..e3b54913 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -7,6 +7,8 @@ pub mod commands; pub mod nostr; pub mod tray; +use std::fs; + use nostr_sdk::prelude::*; use tauri::Manager; use tauri_plugin_autostart::MacosLauncher; @@ -20,11 +22,15 @@ fn main() { .setup(|app| { let _tray = tray::create_tray(app.handle()).unwrap(); let handle = app.handle().clone(); - let config_dir = handle.path().app_config_dir().unwrap(); + let resource_dir = handle.path().resource_dir().unwrap(); + let home_dir = handle.path().home_dir().unwrap(); + + // create data folder if not exist + fs::create_dir_all(home_dir.join("Lume/")).unwrap(); tauri::async_runtime::spawn(async move { // Create nostr database connection - let nostr_db = SQLiteDatabase::open(config_dir.join("nostr.db")) + let nostr_db = SQLiteDatabase::open(resource_dir.join("lume.db")) .await .expect("Open database failed."); @@ -79,6 +85,7 @@ fn main() { .invoke_handler(tauri::generate_handler![ nostr::keys::create_keys, nostr::keys::save_key, + nostr::keys::get_encrypted_key, nostr::keys::verify_signer, nostr::keys::load_selected_account, nostr::keys::event_to_bech32, @@ -108,15 +115,21 @@ fn main() { nostr::event::upvote, nostr::event::downvote, commands::folder::show_in_folder, - commands::folder::get_all_nsecs, + commands::folder::get_accounts, commands::opg::fetch_opg, ]) .build(tauri::generate_context!()) .expect("error while running tauri application") - .run(|_app_handle, event| match event { - tauri::RunEvent::ExitRequested { api, .. } => { - api.prevent_exit(); + .run(|app, event| { + if let tauri::RunEvent::Opened { urls } = event { + if let Some(w) = app.get_webview_window("main") { + let urls = urls + .iter() + .map(|u| u.as_str()) + .collect::>() + .join(","); + let _ = w.eval(&format!("window.onFileOpen(`{urls}`)")); + } } - _ => {} }); } diff --git a/src-tauri/src/nostr/keys.rs b/src-tauri/src/nostr/keys.rs index 33bf2959..7bf6e960 100644 --- a/src-tauri/src/nostr/keys.rs +++ b/src-tauri/src/nostr/keys.rs @@ -53,13 +53,14 @@ pub async fn save_key( let npub = nostr_keys.public_key().to_bech32().unwrap(); let nsec = nostr_keys.secret_key().unwrap().to_bech32().unwrap(); - if let Ok(config_dir) = app_handle.path().app_config_dir() { - let file_path = npub.clone() + ".npub"; - let _ = File::create(config_dir.join(file_path)).unwrap(); + let home_dir = app_handle.path().home_dir().unwrap(); + let app_dir = home_dir.join("Lume/"); - let keyring = Entry::new("Lume Secret Storage", &npub).unwrap(); - let _ = keyring.set_password(&nsec); - } + let file_path = npub.clone() + ".npub"; + let _ = File::create(app_dir.join(file_path)).unwrap(); + + let keyring = Entry::new("Lume Secret Storage", &npub).unwrap(); + let _ = keyring.set_password(&nsec); let signer = NostrSigner::Keys(nostr_keys); let client = &state.client; @@ -84,6 +85,24 @@ pub async fn verify_signer(state: State<'_, Nostr>) -> Result { } } +#[tauri::command] +pub fn get_encrypted_key(npub: &str, password: &str) -> Result { + let keyring = Entry::new("Lume Secret Storage", npub).unwrap(); + + if let Ok(nsec) = keyring.get_password() { + let secret_key = SecretKey::from_bech32(nsec).expect("Get secret key failed"); + let new_key = EncryptedSecretKey::new(&secret_key, password, 16, KeySecurity::Medium); + + if let Ok(key) = new_key { + Ok(key.to_bech32().unwrap()) + } else { + Err("Encrypt key failed".into()) + } + } else { + Err("Key not found".into()) + } +} + #[tauri::command] pub async fn load_selected_account(npub: &str, state: State<'_, Nostr>) -> Result { let client = &state.client;