diff --git a/drizzle.config.ts b/drizzle.config.ts deleted file mode 100644 index ec9f4c4..0000000 --- a/drizzle.config.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { defineConfig } from 'drizzle-kit'; - -export default defineConfig({ - schema: './src-tauri/database/schemas/**.ts', - out: './src-tauri/database/migrations', - dialect: 'sqlite', - dbCredentials: { - url: './src-tauri/database/vault.db', - }, -}); diff --git a/nuxt.config.ts b/nuxt.config.ts index 6eb590c..2044f68 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -1,6 +1,11 @@ import tailwindcss from '@tailwindcss/vite' // https://nuxt.com/docs/api/configuration/nuxt-config + export default defineNuxtConfig({ + app: { + baseURL: './', + }, + modules: [ 'nuxt-zod-i18n', '@nuxtjs/i18n', @@ -89,12 +94,10 @@ export default defineNuxtConfig({ ssr: false, // Enables the development server to be discoverable by other devices when running on iOS physical devices devServer: { - host: process.env.TAURI_DEV_HOST || '0.0.0.0', + host: process.env.TAURI_DEV_HOST || 'localhost', }, vite: { - base: './', - plugins: [tailwindcss()], // Better support for Tauri CLI output clearScreen: false, diff --git a/src-tauri/.cargo/config.toml b/src-tauri/.cargo/config.toml new file mode 100644 index 0000000..b94834e --- /dev/null +++ b/src-tauri/.cargo/config.toml @@ -0,0 +1,9 @@ +# Nur dieser Inhalt in src-tauri/.cargo/config.toml + +[target.aarch64-linux-android] +# Ersetze die Pfade durch deine tatsächlichen NDK-Pfade +# Dein NDK-Basispfad: /home/haex/Android/Sdk/ndk/29.0.13113456 +# Stelle sicher, dass der clang-Name (mit API-Level, z.B. ...24-clang) korrekt ist. +linker = "/home/haex/Android/Sdk/ndk/29.0.13113456/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android33-clang" +#ar = "/home/haex/Android/Sdk/ndk/29.0.13113456/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar" +#ranlib = "/home/haex/Android/Sdk/ndk/29.0.13113456/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib" diff --git a/src-tauri/gen/android/app/src/main/AndroidManifest.xml b/src-tauri/gen/android/app/src/main/AndroidManifest.xml index b6a3941..0d17964 100644 --- a/src-tauri/gen/android/app/src/main/AndroidManifest.xml +++ b/src-tauri/gen/android/app/src/main/AndroidManifest.xml @@ -1,7 +1,8 @@ - + + diff --git a/src-tauri/gen/android/app/src/main/assets/database/vault.db b/src-tauri/gen/android/app/src/main/assets/database/vault.db new file mode 100644 index 0000000..7dfe673 Binary files /dev/null and b/src-tauri/gen/android/app/src/main/assets/database/vault.db differ diff --git a/src-tauri/src/database/mod.rs b/src-tauri/src/database/mod.rs index 4d575b5..88a0708 100644 --- a/src-tauri/src/database/mod.rs +++ b/src-tauri/src/database/mod.rs @@ -1,11 +1,15 @@ // database/mod.rs pub mod core; -use rusqlite::Connection; +use rusqlite::{Connection, OpenFlags, Result as RusqliteResult}; use serde_json::Value as JsonValue; -use std::path::Path; +use std::fs; +use std::path::{Path, PathBuf}; use std::sync::Mutex; -use tauri::{path::BaseDirectory, AppHandle, Manager, State}; +use tauri::utils::resources::Resource; +use tauri::{path::BaseDirectory, AppHandle, Manager, State, Wry}; +use tokio::io::join; + pub struct DbConnection(pub Mutex>); // Öffentliche Funktionen für direkten Datenbankzugriff @@ -29,7 +33,7 @@ pub async fn sql_execute( /// Erstellt eine verschlüsselte Datenbank #[tauri::command] -pub fn create_encrypted_database( +pub fn create_encrypted_database_old( app_handle: AppHandle, path: String, key: String, @@ -39,7 +43,7 @@ pub fn create_encrypted_database( let resource_path = app_handle .path() - .resolve("resources/vault.db", BaseDirectory::Resource) + .resolve("vault.db", BaseDirectory::Resource) .map_err(|e| format!("Fehler beim Auflösen des Ressourcenpfads: {}", e))?; // Prüfen, ob die Ressourcendatei existiert @@ -229,3 +233,325 @@ pub fn open_encrypted_database( Ok(format!("success")) } + +// Notwendige Imports an den Anfang des Moduls stellen +//use tauri::{AppHandle, Manager, State, path::BaseDirectory, Wry}; +//use rusqlite::{Connection, OpenFlags, Result as RusqliteResult}; +//use std::fs; +//use std::path::{Path, PathBuf}; +//use std::sync::Mutex; // Für den State + +// Stelle sicher, dass dein DbConnection-Typ hier bekannt ist. +// z.B. durch pub struct DbConnection(pub Mutex>); +// oder wenn es in einem anderen Modul ist: use crate::path_to::DbConnection; +// Für dieses Beispiel gehe ich davon aus, dass es in crate::DbConnection liegt. +// Ersetze `crate::DbConnection` mit dem korrekten Pfad zu deiner Definition. +//type SharedDbConnectionState = State<'_, crate::DbConnection>; + +/// Hilfsfunktion: Lädt ein Asset und kopiert es in eine temporäre Datei. +/// Gibt den Pfad zur temporären Datei zurück. +fn prepare_temporary_asset_db( + app_handle: &AppHandle, + asset_name: &str, + temp_base_dir: BaseDirectory, +) -> Result { + println!("Lade Asset '{}' aus dem App-Bundle...", asset_name); + + //.resolve("vault.db", BaseDirectory::Resource) + let asset_bytes = app_handle + .asset_resolver() + .get(asset_name.to_string()) + .ok_or_else(|| format!("Asset '{}' wurde nicht im Bundle gefunden.", asset_name))? + .bytes() + .to_vec(); + + println!( + "Asset '{}' erfolgreich geladen ({} bytes).", + asset_name, + asset_bytes.len() + ); + + let temp_db_filename = format!("temp_unencrypted_{}", asset_name); + let temp_db_path = app_handle + .path() + .resolve(&temp_db_filename, temp_base_dir) + .map_err(|e| { + format!( + "Fehler beim Auflösen des Pfads für die temporäre DB '{}': {}", + temp_db_filename, e + ) + })?; + + println!( + "Temporärer Pfad für unverschlüsselte DB: {}", + temp_db_path.display() + ); + + if let Some(parent) = temp_db_path.parent() { + if !parent.exists() { + fs::create_dir_all(parent).map_err(|e| { + format!( + "Fehler beim Erstellen des temporären Verzeichnisses '{}': {}", + parent.display(), + e + ) + })?; + println!("Temporäres Verzeichnis '{}' erstellt.", parent.display()); + } + } + + if temp_db_path.exists() { + fs::remove_file(&temp_db_path).map_err(|e| { + format!( + "Fehler beim Löschen der alten temporären DB '{}': {}", + temp_db_path.display(), + e + ) + })?; + println!("Alte temporäre DB '{}' gelöscht.", temp_db_path.display()); + } + + fs::write(&temp_db_path, &asset_bytes).map_err(|e| { + format!( + "Fehler beim Schreiben der Asset-DB nach '{}': {}", + temp_db_path.display(), + e + ) + })?; + println!( + "Asset-DB erfolgreich nach '{}' geschrieben.", + temp_db_path.display() + ); + + Ok(temp_db_path) +} + +/// Hilfsfunktion: Verschlüsselt eine Quelldatenbank in eine Zieldatenbank. +fn encrypt_database_from_source( + unencrypted_source_path: &Path, + target_encrypted_path_str: &str, + key: &str, +) -> Result<(), String> { + println!( + "Öffne temporäre Quelldatenbank '{}'...", + unencrypted_source_path.display() + ); + let source_conn = Connection::open(unencrypted_source_path).map_err(|e| { + format!( + "Fehler beim Öffnen der Quelldatenbank '{}': {}", + unencrypted_source_path.display(), + e + ) + })?; + println!( + "Verbindung zur Quelldatenbank '{}' geöffnet.", + unencrypted_source_path.display() + ); + + let final_encrypted_db_path = PathBuf::from(target_encrypted_path_str); + println!( + "Zielpfad für verschlüsselte DB: {}", + final_encrypted_db_path.display() + ); + + if let Some(parent) = final_encrypted_db_path.parent() { + if !parent.exists() { + fs::create_dir_all(parent).map_err(|e| { + format!( + "Fehler beim Erstellen des Zielverzeichnisses '{}': {}", + parent.display(), + e + ) + })?; + println!("Zielverzeichnis '{}' erstellt.", parent.display()); + } + } + if final_encrypted_db_path.exists() { + fs::remove_file(&final_encrypted_db_path).map_err(|e| { + format!( + "Fehler beim Löschen der alten verschlüsselten DB '{}': {}", + final_encrypted_db_path.display(), + e + ) + })?; + println!( + "Alte verschlüsselte DB '{}' gelöscht.", + final_encrypted_db_path.display() + ); + } + + let attach_path_str = final_encrypted_db_path.to_str().ok_or_else(|| { + format!( + "Ungültiger UTF-8 Pfad für ATTACH: {}", + final_encrypted_db_path.display() + ) + })?; + + println!( + "Hänge neue verschlüsselte DB an: '{}' mit KEY '{}'", + attach_path_str, key + ); + source_conn + .execute( + "ATTACH DATABASE ?1 AS encrypted_vault KEY ?2;", + &[attach_path_str, key], + ) + .map_err(|e| format!("Fehler bei ATTACH DATABASE an '{}': {}", attach_path_str, e))?; + println!("Verschlüsselte DB 'encrypted_vault' erfolgreich angehängt."); + + println!("Exportiere Daten von 'main' (Quelle) nach 'encrypted_vault'..."); + if let Err(e) = source_conn.execute("SELECT sqlcipher_export('encrypted_vault');", []) { + eprintln!("!!! FEHLER während sqlcipher_export: {}", e); + source_conn + .execute("DETACH DATABASE encrypted_vault;", []) + .ok(); // Best-effort cleanup + return Err(format!("Fehler bei sqlcipher_export: {}", e)); + } + println!("SQLCipher Export nach 'encrypted_vault' erfolgreich."); + + println!("Löse 'encrypted_vault'..."); + source_conn + .execute("DETACH DATABASE encrypted_vault;", []) + .map_err(|e| format!("Fehler bei DETACH DATABASE 'encrypted_vault': {}", e))?; + println!("'encrypted_vault' erfolgreich gelöst."); + + // Verbindung zur Quelldatenbank wird hier durch drop(source_conn) geschlossen. + Ok(()) +} + +/// Hilfsfunktion: Öffnet eine verschlüsselte Datenbank und verifiziert sie. +/// Gibt die geöffnete und verifizierte Verbindung zurück. +fn open_and_verify_encrypted_db(db_path: &Path, key: &str) -> Result { + println!( + "Öffne verschlüsselte DB '{}' zur Überprüfung...", + db_path.display() + ); + let conn = Connection::open(db_path).map_err(|e| { + format!( + "Fehler beim Öffnen der verschlüsselten DB '{}' für Check: {}", + db_path.display(), + e + ) + })?; + + conn.pragma_update(None, "key", key).map_err(|e| { + format!( + "Fehler beim Setzen des PRAGMA key für DB '{}': {}", + db_path.display(), + e + ) + })?; + println!("PRAGMA key für DB '{}' gesetzt.", db_path.display()); + + println!("Prüfe SQLCipher-Version auf DB '{}'...", db_path.display()); + match conn.query_row("PRAGMA cipher_version;", [], |row| row.get::<_, String>(0)) { + Ok(version) => { + println!( + "SQLCipher ist aktiv auf DB '{}'! Version: {}", + db_path.display(), + version + ); + match conn.query_row( + "SELECT count(*) FROM sqlite_master WHERE type='table';", + [], + |row| row.get::<_, i32>(0), + ) { + Ok(count) => println!( + "Testabfrage erfolgreich: {} Tabelle(n) in DB '{}' gefunden.", + count, + db_path.display() + ), + Err(e) => { + eprintln!( + "Fehler bei Testabfrage auf verschlüsselter DB '{}': {}", + db_path.display(), + e + ); + return Err(format!( + "Testabfrage auf verschlüsselter DB '{}' fehlgeschlagen: {}", + db_path.display(), + e + )); + } + } + } + Err(e) => { + eprintln!( + "FEHLER: SQLCipher scheint NICHT aktiv zu sein auf DB '{}'!", + db_path.display() + ); + eprintln!("'PRAGMA cipher_version;' schlug fehl: {}", e); + return Err(format!( + "SQLCipher Aktivitätscheck für DB '{}' fehlgeschlagen: {}", + db_path.display(), + e + )); + } + } + Ok(conn) +} + +/// Hauptfunktion: Erstellt eine verschlüsselte Datenbank aus einem gebündelten Asset. +#[tauri::command] +pub fn create_encrypted_database( + app_handle: AppHandle, + path: String, + key: String, + state: State<'_, DbConnection>, +) -> Result { + let asset_name = "database/vault.db"; + let temp_db_path: PathBuf; // Muss deklariert werden, um im Fehlerfall aufgeräumt werden zu können + + // Schritt 1: Asset vorbereiten + match prepare_temporary_asset_db(&app_handle, &asset_name, BaseDirectory::AppData) { + Ok(path) => temp_db_path = path, + Err(e) => return Err(e), + } + + // Schritt 2: Datenbank verschlüsseln + // Wir geben einen String-Slice für path, da die Funktion das erwartet. + if let Err(e) = encrypt_database_from_source(&temp_db_path, &path, &key) { + // Versuche, die temporäre Datei auch im Fehlerfall zu löschen + let _ = fs::remove_file(&temp_db_path); // Fehler beim Löschen hier ignorieren + return Err(e); + } + + // Schritt 3: Temporäre Datei aufräumen + if let Err(e) = fs::remove_file(&temp_db_path) { + // Logge den Fehler, aber fahre fort, da die verschlüsselte DB erstellt wurde + eprintln!("Warnung: Fehler beim Löschen der temporären DB '{}': {}. Die verschlüsselte DB wurde jedoch erstellt.", temp_db_path.display(), e); + } else { + println!( + "Temporäre DB '{}' erfolgreich gelöscht.", + temp_db_path.display() + ); + } + println!("Datenbank erfolgreich nach '{}' verschlüsselt.", path); + + // Schritt 4: Neu erstellte verschlüsselte Datenbank öffnen und verifizieren + let final_encrypted_db_path = PathBuf::from(path.clone()); // Klonen, da String für Pfad benötigt wird + let final_conn = match open_and_verify_encrypted_db(&final_encrypted_db_path, &key) { + Ok(conn) => conn, + Err(e) => { + // Wenn das Öffnen/Verifizieren fehlschlägt, existiert die Datei vielleicht, ist aber unbrauchbar. + // Je nach Strategie könnte man hier die `final_encrypted_db_path` löschen. + return Err(e); + } + }; + + // Schritt 5: Datenbankverbindung im State aktualisieren + println!( + "Aktualisiere DB-Verbindung im State mit '{}'", + final_encrypted_db_path.display() + ); + let mut db_state_lock = state + .0 + .lock() + .map_err(|e| format!("Mutex-Fehler beim Sperren des DB-Status: {}", e.to_string()))?; + *db_state_lock = Some(final_conn); + + Ok(format!( + "Verschlüsselte Datenbank erfolgreich erstellt, geprüft und im State gespeichert unter: {}", + final_encrypted_db_path.display() + )) +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index fd66225..56b3d83 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -12,6 +12,73 @@ pub fn run() { let protocol_name = "haex-extension"; tauri::Builder::default() + .setup(|app| { + // --- START DER ASSET-INVENTUR (Korrigierte Version) --- + println!("\n[INVENTUR] App-Setup wird ausgeführt. Liste alle Assets im Bundle auf..."); + + let mut found_assets_count = 0; + + // KORREKTE METHODE: Direkt über eine Referenz auf den AssetResolver iterieren + for asset_name in app.asset_resolver().iter() { + found_assets_count += 1; + // Wir geben jeden gefundenen Asset-Namen aus + println!("[INVENTUR] Gefundenes Asset: '{}'", asset_name.0); + } + + if found_assets_count == 0 { + println!("[INVENTUR] Es wurden KEINE Assets im Bundle gefunden!"); + } else { + println!( + "[INVENTUR] Inventur abgeschlossen. {} Assets gefunden.", + found_assets_count + ); + } + + println!("[INVENTUR] --- ENDE DER INVENTUR ---\n"); + // --- ENDE DER ASSET-INVENTUR --- + // --- START DES DEFINITIVEN ASSET-TESTS --- + println!("\n[DEBUG] App-Setup wird ausgeführt. Versuche, die Datenbank zu laden..."); + + // BITTE SICHERSTELLEN: Dieser String muss EXAKT dem SCHLÜSSEL (KEY) + // in deiner tauri.conf.json entsprechen! + let asset_to_find = "database/vault.db"; + + println!( + "[DEBUG] Suche nach Asset mit dem Alias: '{}'", + asset_to_find + ); + + match app.asset_resolver().get(asset_to_find.to_string()) { + Some(asset) => { + // ERFOLG! Das Asset wurde gefunden. + println!("\n✅ ✅ ✅ ERFOLG! ✅ ✅ ✅"); + println!( + "[DEBUG] Asset '{}' wurde im Bundle gefunden.", + asset_to_find + ); + println!("[DEBUG] Größe der Datenbank: {} Bytes.", asset.bytes.len()); + } + None => { + // FEHLER! Das Asset wurde nicht gefunden. + println!("\n❌ ❌ ❌ FEHLER! ❌ ❌ ❌"); + println!( + "[DEBUG] Asset '{}' wurde NICHT im Bundle gefunden.", + asset_to_find + ); + println!("[DEBUG] Mögliche Ursachen:"); + println!("[DEBUG] 1. Der Alias-String im Code ist falsch (Tippfehler?)."); + println!("[DEBUG] 2. Der Schlüssel in 'tauri.conf.json' ist anders."); + println!( + "[DEBUG] 3. Der Build-Cache ist veraltet (lösche 'src-tauri/target')." + ); + } + } + println!("[DEBUG] --- ENDE DES ASSET-TESTS ---\n"); + // --- ENDE DES DEFINITIVEN ASSET-TESTS --- + + // Hier kann dein restlicher Setup-Code stehen bleiben + Ok(()) + }) .register_uri_scheme_protocol(protocol_name, move |context, request| { match extension::core::extension_protocol_handler(&context, &request) { Ok(response) => response, // Wenn der Handler Ok ist, gib die Response direkt zurück @@ -42,11 +109,11 @@ pub fn run() { } } }) - .plugin(tauri_plugin_http::init()) .manage(DbConnection(Mutex::new(None))) .manage(ExtensionState::default()) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_fs::init()) + .plugin(tauri_plugin_http::init()) .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_store::Builder::new().build()) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index e0bc7e9..f1869cf 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -5,9 +5,9 @@ "identifier": "space.haex.hub", "build": { "beforeDevCommand": "pnpm dev", - "devUrl": "http://0.0.0.0:3001", + "devUrl": "http://localhost:3000", "beforeBuildCommand": "pnpm generate", - "frontendDist": "../.output/public" + "frontendDist": "../dist" }, "app": { "windows": [ @@ -40,7 +40,7 @@ }, "assetProtocol": { "enable": true, - "scope": ["*"] + "scope": ["$APPDATA", "$RESOURCE"] } } }, @@ -54,22 +54,6 @@ "icons/icon.icns", "icons/icon.ico" ], - "resources": { - "database/vault.db": "resources/vault.db" - }, - "linux": { - "appimage": { - "bundleMediaFramework": false, - "files": {} - }, - "deb": { - "files": {} - }, - "rpm": { - "epoch": 0, - "files": {}, - "release": "1" - } - } + "resources": ["test.txt"] } } diff --git a/src-tauri/test.txt b/src-tauri/test.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/app.vue b/src/app.vue index 2c27160..2bbc3ba 100644 --- a/src/app.vue +++ b/src/app.vue @@ -8,7 +8,6 @@ diff --git a/src/components/vault/button/create.vue b/src/components/vault/button/create.vue index 343f450..d1408b5 100644 --- a/src/components/vault/button/create.vue +++ b/src/components/vault/button/create.vue @@ -3,6 +3,7 @@ v-model:open="open" :title="t('title')" class="btn btn-primary btn-outline shadow-md md:btn-lg shrink-0 flex-1 whitespace-nowrap flex-nowrap" + @click="open = true" >