mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-16 14:10:52 +01:00
added hlc logic
This commit is contained in:
7
src-tauri/Cargo.lock
generated
7
src-tauri/Cargo.lock
generated
@ -1565,6 +1565,7 @@ dependencies = [
|
||||
"tauri-plugin-store",
|
||||
"tokio",
|
||||
"uhlc",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5019,12 +5020,14 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.16.0"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
|
||||
checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
|
||||
dependencies = [
|
||||
"getrandom 0.3.2",
|
||||
"js-sys",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@ -45,6 +45,7 @@ tauri-plugin-http = "2.5"
|
||||
tauri-plugin-notification = "2.3"
|
||||
tauri-plugin-persisted-scope = "2.0.0"
|
||||
tauri-plugin-android-fs = "9.5.0"
|
||||
uuid = { version = "1.17.0", features = ["v4"] }
|
||||
#tauri-plugin-sql = { version = "2", features = ["sqlite"] }
|
||||
|
||||
|
||||
|
||||
@ -6,8 +6,8 @@ export const haexCrdtMessages = sqliteTable('haex_crdt_messages', {
|
||||
row_pks: text({ mode: 'json' }),
|
||||
op_type: text({ enum: ['INSERT', 'UPDATE', 'DELETE'] }),
|
||||
column_name: text(),
|
||||
new_value: blob(),
|
||||
old_value: blob(),
|
||||
new_value: text({ mode: 'json' }),
|
||||
old_value: text({ mode: 'json' }),
|
||||
})
|
||||
export type InsertHaexCrdtMessages = typeof haexCrdtMessages.$inferInsert
|
||||
export type SelectHaexCrdtMessages = typeof haexCrdtMessages.$inferSelect
|
||||
@ -19,3 +19,9 @@ export const haexCrdtSnapshots = sqliteTable('haex_crdt_snapshots', {
|
||||
location_url: text(),
|
||||
file_size_bytes: integer(),
|
||||
})
|
||||
|
||||
export const haexCrdtSettings = sqliteTable('haex_crdt_settings', {
|
||||
id: text().primaryKey(),
|
||||
type: text({ enum: ['hlc_timestamp'] }).unique(),
|
||||
value: text(),
|
||||
})
|
||||
|
||||
67
src-tauri/src/crdt/hlc.rs
Normal file
67
src-tauri/src/crdt/hlc.rs
Normal file
@ -0,0 +1,67 @@
|
||||
use rusqlite::{params, Connection, Result};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use uhlc::{Timestamp, HLC};
|
||||
use uuid::Uuid;
|
||||
|
||||
const HLC_SETTING_TYPE: &str = "hlc_timestamp";
|
||||
|
||||
pub const GET_HLC_FUNCTION: &str = "get_hlc_timestamp";
|
||||
pub const CRDT_SETTINGS_TABLE: &str = "haex_crdt_settings";
|
||||
pub struct HlcService(pub Arc<Mutex<HLC>>);
|
||||
|
||||
pub fn setup_hlc(conn: &mut Connection) -> Result<()> {
|
||||
// 1. Lade den letzten HLC-Zustand oder erstelle einen neuen.
|
||||
let hlc = conn
|
||||
.query_row(
|
||||
"SELECT value FROM {CRDT_SETTINGS_TABLE} meta WHERE type = ?1",
|
||||
params![HLC_SETTING_TYPE],
|
||||
|row| {
|
||||
let state_str: String = row.get(0)?;
|
||||
let timestamp = Timestamp::from_str(&state_str)
|
||||
.map_err(|_| rusqlite::Error::ExecuteReturnedResults)?; // Konvertiere den Fehler
|
||||
Ok(HLC::new(timestamp))
|
||||
},
|
||||
)
|
||||
.unwrap_or_else(|_| HLC::default()); // Bei Fehler (z.B. nicht gefunden) -> neuen HLC erstellen.
|
||||
|
||||
let hlc_arc = Arc::new(Mutex::new(hlc));
|
||||
|
||||
// 2. Erstelle eine Klon für die SQL-Funktion und speichere den Zustand bei jeder neuen Timestamp-Generierung.
|
||||
let hlc_clone = hlc_arc.clone();
|
||||
let db_conn_arc = Arc::new(Mutex::new(conn.try_clone()?));
|
||||
|
||||
conn.create_scalar_function(
|
||||
GET_HLC_FUNCTION,
|
||||
0,
|
||||
rusqlite::functions::FunctionFlags::SQLITE_UTF8
|
||||
| rusqlite::functions::FunctionFlags::SQLITE_DETERMINISTIC,
|
||||
move |_| {
|
||||
let mut hlc = hlc_clone.lock().unwrap();
|
||||
let new_timestamp = hlc.new_timestamp();
|
||||
let timestamp_str = new_timestamp.to_string();
|
||||
|
||||
// 3. Speichere den neuen Zustand sofort zurück in die DB.
|
||||
// UPSERT-Logik: Ersetze den Wert, falls der Schlüssel existiert, sonst füge ihn ein.
|
||||
let db_conn = db_conn_arc.lock().unwrap();
|
||||
db_conn
|
||||
.execute(
|
||||
"INSERT INTO {CRDT_SETTINGS_TABLE} (id, type, value) VALUES (?1, ?2, ?3)
|
||||
ON CONFLICT(type) DO UPDATE SET value = excluded.value",
|
||||
params![
|
||||
Uuid::new_v4().to_string(), // Generiere eine neue ID für den Fall eines INSERTs
|
||||
HLC_SETTING_TYPE,
|
||||
×tamp_str
|
||||
],
|
||||
)
|
||||
.expect("HLC state could not be persisted."); // In Prod sollte hier ein besseres Error-Handling hin.
|
||||
|
||||
Ok(timestamp_str)
|
||||
},
|
||||
)?;
|
||||
|
||||
// Hinweis: Den HLC-Service im Tauri-State zu managen ist nicht mehr zwingend,
|
||||
// da die SQL-Funktion nun alles Notwendige über geklonte Arcs erhält.
|
||||
// Falls du ihn dennoch für andere Commands brauchst, kannst du ihn im State speichern.
|
||||
|
||||
Ok(())
|
||||
}
|
||||
4
src-tauri/src/crdt/mod.rs
Normal file
4
src-tauri/src/crdt/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub mod hlc;
|
||||
pub mod log;
|
||||
pub mod proxy;
|
||||
pub mod trigger;
|
||||
@ -1,7 +1,6 @@
|
||||
// In src-tauri/src/sql_proxy.rs
|
||||
|
||||
use rusqlite::Connection;
|
||||
use sqlparser::ast::Statement;
|
||||
use sqlparser::ast::{ColumnDef, DataType, Expr, Ident, Query, Statement, TableWithJoins, Value};
|
||||
use sqlparser::dialect::SQLiteDialect;
|
||||
use sqlparser::parser::Parser;
|
||||
@ -9,11 +8,9 @@ use sqlparser::visit_mut::{self, VisitorMut};
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
// Der Name der Tombstone-Spalte als Konstante, um "Magic Strings" zu vermeiden.
|
||||
pub const TOMBSTONE_COLUMN_NAME: &str = "tombstone";
|
||||
const EXCLUDED_TABLES: &[&str] = &["crdt_log"];
|
||||
pub const TOMBSTONE_COLUMN_NAME: &str = "haex_tombstone";
|
||||
const EXCLUDED_TABLES: &[&str] = &["haex_crdt_log"];
|
||||
|
||||
// Die Hauptstruktur unseres Proxys.
|
||||
// Sie ist zustandslos, da wir uns gegen einen Schema-Cache entschieden haben.
|
||||
pub struct SqlProxy;
|
||||
|
||||
impl SqlProxy {
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// In src-tauri/src/trigger_manager.rs -> impl<'a> TriggerManager<'a>
|
||||
|
||||
// In einem neuen Modul, z.B. src-tauri/src/trigger_manager.rs
|
||||
use crate::sql_proxy::ColumnInfo;
|
||||
use rusqlite::{Result, Transaction};
|
||||
use crate::crdt::proxy::ColumnInfo;
|
||||
use rusqlite::{params, Connection, Result, Transaction};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tauri::AppHandle;
|
||||
|
||||
pub struct TriggerManager<'a> {
|
||||
tx: &'a Transaction<'a>,
|
||||
@ -94,7 +96,7 @@ impl<'a> TriggerManager<'a> {
|
||||
)).collect::<Vec<_>>().join("\n");
|
||||
|
||||
// Erstellt die Logik für den Soft-Delete
|
||||
let delete_logic = format!(
|
||||
let soft_delete_logic = format!(
|
||||
r#"
|
||||
-- Protokolliere den Soft-Delete
|
||||
INSERT INTO crdt_log (hlc_timestamp, op_type, table_name, row_pk)
|
||||
@ -117,7 +119,7 @@ impl<'a> TriggerManager<'a> {
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
{column_updates}
|
||||
{delete_logic}
|
||||
{soft_delete_logic}
|
||||
END;"
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
// database/core.rs
|
||||
use crate::crdt::hlc;
|
||||
use crate::database::DbConnection;
|
||||
use base64::{engine::general_purpose::STANDARD, Engine as _};
|
||||
use rusqlite::{
|
||||
@ -6,8 +7,6 @@ use rusqlite::{
|
||||
Connection, OpenFlags, ToSql,
|
||||
};
|
||||
use serde_json::Value as JsonValue;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use tauri::State;
|
||||
// --- Hilfsfunktion: Konvertiert JSON Value zu etwas, das rusqlite versteht ---
|
||||
// Diese Funktion ist etwas knifflig wegen Ownership und Lifetimes.
|
||||
@ -39,8 +38,6 @@ fn json_to_rusqlite_value(json_val: &JsonValue) -> Result<RusqliteValue, String>
|
||||
}
|
||||
}
|
||||
|
||||
// --- Tauri Command für INSERT/UPDATE/DELETE ---
|
||||
#[tauri::command]
|
||||
pub async fn execute(
|
||||
sql: String,
|
||||
params: Vec<JsonValue>,
|
||||
@ -67,7 +64,6 @@ pub async fn execute(
|
||||
Ok(affected_rows)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn select(
|
||||
sql: String,
|
||||
params: Vec<JsonValue>,
|
||||
@ -194,32 +190,6 @@ pub fn open_and_init_db(path: &str, key: &str, create: bool) -> Result<Connectio
|
||||
Ok(conn)
|
||||
}
|
||||
|
||||
/// Kopiert eine Datei von einem Pfad zu einem anderen
|
||||
pub fn copy_file<S: AsRef<Path>, T: AsRef<Path>>(
|
||||
source_path: S,
|
||||
target_path: T,
|
||||
) -> Result<(), String> {
|
||||
let source = source_path.as_ref();
|
||||
let target = target_path.as_ref();
|
||||
|
||||
// Check if source file exists
|
||||
if !source.exists() {
|
||||
return Err(format!("Source file '{}' does not exist", source.display()));
|
||||
}
|
||||
|
||||
// Check if source is a file (not a directory)
|
||||
if !source.is_file() {
|
||||
return Err(format!("Source '{}' is not a file", source.display()));
|
||||
}
|
||||
|
||||
// Copy the file and preserve metadata (permissions, timestamps)
|
||||
fs::copy(source, target)
|
||||
.map(|_| ())
|
||||
.map_err(|e| format!("Failed to copy file: {}", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Hilfsfunktionen für SQL-Parsing
|
||||
pub fn extract_tables_from_query(query: &sqlparser::ast::Query) -> Vec<String> {
|
||||
let mut tables = Vec::new();
|
||||
|
||||
@ -177,7 +177,6 @@ pub fn create_encrypted_database(
|
||||
Ok(format!("Verschlüsselte CRDT-Datenbank erstellt",))
|
||||
}
|
||||
|
||||
use tauri_plugin_dialog::{Dialog, DialogExt, MessageDialogKind};
|
||||
#[tauri::command]
|
||||
pub fn open_encrypted_database(
|
||||
app_handle: AppHandle,
|
||||
@ -205,312 +204,6 @@ pub fn open_encrypted_database(
|
||||
Ok(format!("success"))
|
||||
}
|
||||
|
||||
fn prepare_temporary_asset_db(
|
||||
app_handle: &AppHandle<Wry>,
|
||||
asset_name: &str,
|
||||
temp_base_dir: BaseDirectory,
|
||||
) -> Result<PathBuf, String> {
|
||||
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_owned())
|
||||
.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<Connection, String> {
|
||||
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_new(
|
||||
app_handle: AppHandle<Wry>,
|
||||
path: String,
|
||||
key: String,
|
||||
state: State<'_, DbConnection>,
|
||||
) -> Result<String, String> {
|
||||
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::Resource) {
|
||||
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()
|
||||
))
|
||||
}
|
||||
|
||||
fn get_target_triple() -> Result<String, String> {
|
||||
let target_triple = if cfg!(target_os = "linux") {
|
||||
if cfg!(target_arch = "x86_64") {
|
||||
@ -569,23 +262,6 @@ fn get_target_triple() -> Result<String, String> {
|
||||
Ok(target_triple)
|
||||
}
|
||||
|
||||
fn get_crsqlite_path(app_handle: AppHandle) -> Result<PathBuf, String> {
|
||||
// Laden der cr-sqlite Erweiterung
|
||||
let target_triple = get_target_triple()?;
|
||||
|
||||
println!("target_triple: {}", target_triple);
|
||||
|
||||
let crsqlite_path = app_handle
|
||||
.path()
|
||||
.resource_dir()
|
||||
.map_err(|e| format!("Fehler beim Ermitteln des Ressourcenverzeichnisses: {}", e))?
|
||||
.join(format!("crsqlite-{}", target_triple));
|
||||
|
||||
println!("crsqlite_path: {}", crsqlite_path.display());
|
||||
Ok(crsqlite_path)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_hlc_timestamp(state: tauri::State<HlcService>) -> String {
|
||||
let hlc = state.0.lock().unwrap();
|
||||
hlc.new_timestamp().to_string()
|
||||
@ -604,16 +280,6 @@ pub fn update_hlc_from_remote(
|
||||
.map_err(|e| format!("HLC update failed: {:?}", e))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct SqlTableInfo {
|
||||
cid: u32,
|
||||
name: String,
|
||||
r#type: String,
|
||||
notnull: bool,
|
||||
dflt_value: Option<String>,
|
||||
pk: u8,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn create_crdt_trigger_for_table(
|
||||
state: &State<'_, DbConnection>,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
//mod browser;
|
||||
mod android_storage;
|
||||
mod crdt;
|
||||
mod database;
|
||||
mod extension;
|
||||
mod models;
|
||||
@ -44,6 +45,7 @@ pub fn run() {
|
||||
}
|
||||
})
|
||||
.manage(DbConnection(Arc::new(Mutex::new(None))))
|
||||
.manage(database::HlcService(Mutex::new(uhlc::HLC::default())))
|
||||
.manage(ExtensionState::default())
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_fs::init())
|
||||
@ -61,7 +63,6 @@ pub fn run() {
|
||||
database::sql_execute,
|
||||
database::sql_select,
|
||||
database::test,
|
||||
database::get_hlc_timestamp,
|
||||
database::update_hlc_from_remote,
|
||||
extension::copy_directory,
|
||||
extension::database::extension_sql_execute,
|
||||
|
||||
@ -21,9 +21,6 @@
|
||||
v-model:open="passwordPromptOpen"
|
||||
:path="vaultPath"
|
||||
/>
|
||||
|
||||
<UiButton @click="requesty()">Storage Request</UiButton>
|
||||
res: {{ res }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
@ -94,18 +91,6 @@ const { syncLastVaultsAsync, removeVaultAsync } = useLastVaultStore()
|
||||
const { lastVaults } = storeToRefs(useLastVaultStore())
|
||||
|
||||
await syncLastVaultsAsync()
|
||||
|
||||
const res = ref()
|
||||
|
||||
const storage = useAndroidStorage()
|
||||
const requesty = async () => {
|
||||
try {
|
||||
res.value = await storage.requestStoragePermission()
|
||||
res.value += ' wat the fuk'
|
||||
} catch (error) {
|
||||
res.value = error
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<i18n lang="json">
|
||||
|
||||
Reference in New Issue
Block a user