mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-16 22:20:51 +01:00
zwischenstand
This commit is contained in:
@ -6,8 +6,8 @@ use rusqlite::{
|
||||
Connection, OpenFlags, ToSql,
|
||||
};
|
||||
use serde_json::Value as JsonValue;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::{fs, path::PathBuf};
|
||||
use tauri::State;
|
||||
// --- Hilfsfunktion: Konvertiert JSON Value zu etwas, das rusqlite versteht ---
|
||||
// Diese Funktion ist etwas knifflig wegen Ownership und Lifetimes.
|
||||
@ -67,32 +67,10 @@ pub async fn execute(
|
||||
Ok(affected_rows)
|
||||
}
|
||||
|
||||
/// Führt SQL-Schreiboperationen (INSERT, UPDATE, DELETE, CREATE) ohne Berechtigungsprüfung aus
|
||||
/* pub async fn execute(
|
||||
sql: &str,
|
||||
params: &[String],
|
||||
state: &State<'_, DbConnection>,
|
||||
) -> Result<String, String> {
|
||||
let db = state.0.lock().map_err(|e| format!("Mutex-Fehler: {}", e))?;
|
||||
let conn = db.as_ref().ok_or("Keine Datenbankverbindung vorhanden")?;
|
||||
|
||||
let rows_affected = conn
|
||||
.execute(sql, rusqlite::params_from_iter(params.iter()))
|
||||
.map_err(|e| format!("SQL-Ausführungsfehler: {}", e))?;
|
||||
|
||||
let last_id = conn.last_insert_rowid();
|
||||
|
||||
Ok(serde_json::to_string(&json!({
|
||||
"rows_affected": rows_affected,
|
||||
"last_insert_id": last_id
|
||||
}))
|
||||
.map_err(|e| format!("JSON-Serialisierungsfehler: {}", e))?)
|
||||
} */
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn select(
|
||||
sql: String,
|
||||
params: Vec<JsonValue>, // Parameter als JSON Values empfangen
|
||||
params: Vec<JsonValue>,
|
||||
state: &State<'_, DbConnection>,
|
||||
) -> Result<Vec<Vec<JsonValue>>, String> {
|
||||
// Ergebnis als Vec<RowObject>
|
||||
@ -182,45 +160,6 @@ pub async fn select(
|
||||
Ok(result_vec)
|
||||
}
|
||||
|
||||
/// Führt SQL-Leseoperationen (SELECT) ohne Berechtigungsprüfung aus
|
||||
/* pub async fn select(
|
||||
sql: &str,
|
||||
params: &[String],
|
||||
state: &State<'_, DbConnection>,
|
||||
) -> Result<Vec<Vec<Option<String>>>, String> {
|
||||
let db = state.0.lock().map_err(|e| format!("Mutex-Fehler: {}", e))?;
|
||||
let conn = db.as_ref().ok_or("Keine Datenbankverbindung vorhanden")?;
|
||||
|
||||
let mut stmt = conn
|
||||
.prepare(sql)
|
||||
.map_err(|e| format!("SQL-Vorbereitungsfehler: {}", e))?;
|
||||
let columns = stmt.column_count();
|
||||
let mut rows = stmt
|
||||
.query(rusqlite::params_from_iter(params.iter()))
|
||||
.map_err(|e| format!("SQL-Abfragefehler: {}", e))?;
|
||||
|
||||
let mut result = Vec::new();
|
||||
while let Some(row) = rows
|
||||
.next()
|
||||
.map_err(|e| format!("Zeilenabruffehler: {}", e))?
|
||||
{
|
||||
let mut row_data = Vec::new();
|
||||
for i in 0..columns {
|
||||
let value = row
|
||||
.get(i)
|
||||
.map_err(|e| format!("Datentypfehler in Spalte {}: {}", i, e))?;
|
||||
row_data.push(value);
|
||||
}
|
||||
/* println!(
|
||||
"Select Row Data: {}",
|
||||
&row_data.clone().join("").to_string()
|
||||
); */
|
||||
result.push(row_data);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
} */
|
||||
|
||||
/// Öffnet und initialisiert eine Datenbank mit Verschlüsselung
|
||||
pub fn open_and_init_db(path: &str, key: &str, create: bool) -> Result<Connection, String> {
|
||||
let flags = if create {
|
||||
@ -236,6 +175,16 @@ pub fn open_and_init_db(path: &str, key: &str, create: bool) -> Result<Connectio
|
||||
conn.execute_batch("SELECT count(*) from haex_extensions")
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let journal_mode: String = conn
|
||||
.query_row("PRAGMA journal_mode=WAL;", [], |row| row.get(0))
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
if journal_mode.eq_ignore_ascii_case("wal") {
|
||||
println!("WAL mode successfully enabled.");
|
||||
} else {
|
||||
eprintln!("Failed to enable WAL mode.");
|
||||
}
|
||||
|
||||
Ok(conn)
|
||||
}
|
||||
|
||||
|
||||
@ -5,10 +5,13 @@ use rusqlite::Connection;
|
||||
use serde_json::Value as JsonValue;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Mutex;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tauri::{path::BaseDirectory, AppHandle, Manager, State, Wry};
|
||||
|
||||
pub struct DbConnection(pub Mutex<Option<Connection>>);
|
||||
use crate::database::core::open_and_init_db;
|
||||
pub struct HlcService(pub Mutex<uhlc::HLC>);
|
||||
pub struct DbConnection(pub Arc<Mutex<Option<Connection>>>);
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn sql_select(
|
||||
@ -145,50 +148,21 @@ pub fn create_encrypted_database(
|
||||
eprintln!("FEHLER: SQLCipher scheint NICHT aktiv zu sein!");
|
||||
eprintln!("Der Befehl 'PRAGMA cipher_version;' schlug fehl: {}", e);
|
||||
eprintln!("Die Datenbank wurde wahrscheinlich NICHT verschlüsselt.");
|
||||
// Optional: Hier die Verbindung schließen oder weitere Aktionen unterlassen
|
||||
// return Err(e); // Beende das Programm mit dem Fehler
|
||||
}
|
||||
}
|
||||
|
||||
/* // Kopieren der Ressourcen-Datenbank zum Zielpfad
|
||||
core::copy_file(&resource_path, &path)?;
|
||||
println!("resource_path: {}", resource_path.display());
|
||||
|
||||
// Öffnen der kopierten Datenbank ohne Verschlüsselung
|
||||
let conn = Connection::open(&path).map_err(|e| {
|
||||
format!(
|
||||
"Fehler beim Öffnen der kopierten Datenbank: {}",
|
||||
e.to_string()
|
||||
)
|
||||
})?;
|
||||
conn.close().unwrap();
|
||||
|
||||
// Verschlüsseln der Datenbank mit dem angegebenen Schlüssel
|
||||
conn.pragma_update(None, "key", &key)
|
||||
.map_err(|e| format!("Fehler beim Verschlüsseln der Datenbank: {}", e.to_string()))?;
|
||||
let new_conn = open_and_init_db(&path, &key, false)?;
|
||||
|
||||
// Schließen der Verbindung, um sicherzustellen, dass Änderungen gespeichert werden
|
||||
drop(conn);
|
||||
|
||||
// Öffnen der verschlüsselten Datenbank mit dem Schlüssel
|
||||
let encrypted_conn = core::open_and_init_db(&path, &key, false)
|
||||
.map_err(|e| format!("Fehler beim Öffnen der verschlüsselten Datenbank: {}", e))?;
|
||||
|
||||
// Überprüfen, ob die Datenbank korrekt verschlüsselt wurde, indem wir eine einfache Abfrage ausführen
|
||||
let validation_result: Result<i32, _> =
|
||||
encrypted_conn.query_row("SELECT 1", [], |row| row.get(0));
|
||||
|
||||
if let Err(e) = validation_result {
|
||||
return Err(format!(
|
||||
"Fehler beim Testen der verschlüsselten Datenbank: {}",
|
||||
e.to_string()
|
||||
));
|
||||
}
|
||||
*/
|
||||
// Aktualisieren der Datenbankverbindung im State
|
||||
let mut db = state
|
||||
.0
|
||||
.lock()
|
||||
.map_err(|e| format!("Mutex-Fehler: {}", e.to_string()))?;
|
||||
*db = Some(conn);
|
||||
*db = Some(new_conn);
|
||||
|
||||
Ok(format!("Verschlüsselte CRDT-Datenbank erstellt",))
|
||||
}
|
||||
@ -205,6 +179,7 @@ pub fn open_encrypted_database(
|
||||
|
||||
let conn =
|
||||
core::open_and_init_db(&path, &key, false).map_err(|e| format!("Error during open: {}", e));
|
||||
|
||||
let mut db = state.0.lock().map_err(|e| e.to_string())?;
|
||||
*db = Some(conn.unwrap());
|
||||
|
||||
@ -516,3 +491,120 @@ pub fn create_encrypted_database_new(
|
||||
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") {
|
||||
"x86_64-unknown-linux-gnu".to_string()
|
||||
} else if cfg!(target_arch = "aarch64") {
|
||||
"aarch64-unknown-linux-gnu".to_string()
|
||||
} else {
|
||||
return Err(format!(
|
||||
"Unbekannte Linux-Architektur: {}",
|
||||
std::env::consts::ARCH
|
||||
));
|
||||
}
|
||||
} else if cfg!(target_os = "macos") {
|
||||
if cfg!(target_arch = "x86_64") {
|
||||
"x86_64-apple-darwin".to_string()
|
||||
} else if cfg!(target_arch = "aarch64") {
|
||||
"aarch64-apple-darwin".to_string()
|
||||
} else {
|
||||
return Err(format!(
|
||||
"Unbekannte macOS-Architektur: {}",
|
||||
std::env::consts::ARCH
|
||||
));
|
||||
}
|
||||
} else if cfg!(target_os = "windows") {
|
||||
if cfg!(target_arch = "x86_64") {
|
||||
"x86_64-pc-windows-msvc".to_string()
|
||||
} else if cfg!(target_arch = "x86") {
|
||||
"i686-pc-windows-msvc".to_string()
|
||||
} else {
|
||||
return Err(format!(
|
||||
"Unbekannte Windows-Architektur: {}",
|
||||
std::env::consts::ARCH
|
||||
));
|
||||
}
|
||||
} else if cfg!(target_os = "android") {
|
||||
if cfg!(target_arch = "aarch64") {
|
||||
"aarch64-linux-android".to_string()
|
||||
} else {
|
||||
return Err(format!(
|
||||
"Unbekannte Android-Architektur: {}",
|
||||
std::env::consts::ARCH
|
||||
));
|
||||
}
|
||||
} else if cfg!(target_os = "ios") {
|
||||
if cfg!(target_arch = "aarch64") {
|
||||
"aarch64-apple-ios".to_string()
|
||||
} else {
|
||||
return Err(format!(
|
||||
"Unbekannte iOS-Architektur: {}",
|
||||
std::env::consts::ARCH
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err("Unbekanntes Zielsystem".to_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()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn update_hlc_from_remote(
|
||||
remote_timestamp_str: String,
|
||||
state: tauri::State<HlcService>,
|
||||
) -> Result<(), String> {
|
||||
let remote_ts =
|
||||
uhlc::Timestamp::from_str(&remote_timestamp_str).map_err(|e| e.cause.to_string())?;
|
||||
|
||||
let hlc = state.0.lock().unwrap();
|
||||
hlc.update_with_timestamp(&remote_ts)
|
||||
.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>,
|
||||
table_name: String,
|
||||
) -> Result<Vec<Vec<JsonValue>>, String> {
|
||||
let stmt = format!(
|
||||
"SELECT cid, name, type, notnull, dflt_value, pk from pragma_table_info('{}')",
|
||||
table_name
|
||||
);
|
||||
|
||||
let table_info = core::select(stmt, vec![], state).await;
|
||||
Ok(table_info.unwrap())
|
||||
}
|
||||
|
||||
63
src-tauri/src/database/sql_proxy.rs
Normal file
63
src-tauri/src/database/sql_proxy.rs
Normal file
@ -0,0 +1,63 @@
|
||||
// src-tauri/src/sql_proxy.rs
|
||||
|
||||
use rusqlite::Connection;
|
||||
use sqlparser::ast::Statement;
|
||||
use sqlparser::dialect::SQLiteDialect;
|
||||
use sqlparser::parser::Parser;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
// Der Schema-Cache wird später benötigt, um zu wissen, welche Tabellen eine 'tombstone'-Spalte haben.
|
||||
// Für den Anfang lassen wir ihn leer.
|
||||
pub struct SchemaCache {
|
||||
// TODO: z.B. HashMap<String, Vec<String>> für Tabellen und ihre Spalten
|
||||
}
|
||||
|
||||
impl SchemaCache {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
// TODO: Methoden zum Befüllen und Abfragen des Caches
|
||||
}
|
||||
|
||||
// Die Hauptstruktur unseres Proxys
|
||||
pub struct SqlProxy {
|
||||
// Wir benötigen eine threadsichere Referenz auf den Schema-Cache
|
||||
schema_cache: Arc<Mutex<SchemaCache>>,
|
||||
}
|
||||
|
||||
impl SqlProxy {
|
||||
pub fn new(schema_cache: Arc<Mutex<SchemaCache>>) -> Self {
|
||||
Self { schema_cache }
|
||||
}
|
||||
|
||||
// Die zentrale Ausführungsfunktion
|
||||
pub fn execute(&self, sql: &str, conn: &Connection) -> Result<(), String> {
|
||||
// 1. Parsen des SQL-Strings in einen AST
|
||||
let dialect = SQLiteDialect {};
|
||||
let mut ast =
|
||||
Parser::parse_sql(&dialect, sql).map_err(|e| format!("SQL-Parse-Fehler: {}", e))?;
|
||||
|
||||
// Sicherstellen, dass wir nur eine Anweisung haben
|
||||
if ast.len() != 1 {
|
||||
return Err("Nur einzelne SQL-Anweisungen werden unterstützt.".to_string());
|
||||
}
|
||||
let statement = &mut ast;
|
||||
|
||||
// 2. Umschreiben des AST (Logik folgt in Abschnitt 2)
|
||||
self.transform_statement(statement)?;
|
||||
|
||||
// 3. Ausführen der (möglicherweise modifizierten) Anweisung
|
||||
let final_sql = statement.to_string();
|
||||
conn.execute(&final_sql)
|
||||
.map_err(|e| format!("DB-Ausführungsfehler: {}", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Platzhalter für die Transformationslogik
|
||||
fn transform_statement(&self, statement: &mut Statement) -> Result<(), String> {
|
||||
// HIER KOMMT DIE MAGIE HIN
|
||||
// TODO: Implementierung der `CREATE TABLE`, `DELETE` und `SELECT` Transformationen
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,7 @@ mod models;
|
||||
|
||||
use database::DbConnection;
|
||||
use models::ExtensionState;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
@ -42,7 +42,7 @@ pub fn run() {
|
||||
}
|
||||
}
|
||||
})
|
||||
.manage(DbConnection(Mutex::new(None)))
|
||||
.manage(DbConnection(Arc::new(Mutex::new(None))))
|
||||
.manage(ExtensionState::default())
|
||||
.plugin(tauri_plugin_notification::init())
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
@ -58,6 +58,8 @@ 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,
|
||||
extension::database::extension_sql_select,
|
||||
|
||||
Reference in New Issue
Block a user