mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-18 23:10:51 +01:00
init commit
This commit is contained in:
160
src-tauri/src/database/core.rs
Normal file
160
src-tauri/src/database/core.rs
Normal file
@ -0,0 +1,160 @@
|
||||
// database/core.rs
|
||||
use crate::database::DbConnection;
|
||||
use rusqlite::{Connection, OpenFlags};
|
||||
use serde_json::json;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use tauri::State;
|
||||
|
||||
/// 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))?)
|
||||
}
|
||||
|
||||
/// Führt SQL-Leseoperationen (SELECT) ohne Berechtigungsprüfung aus
|
||||
pub async fn select(
|
||||
sql: &str,
|
||||
params: &[String],
|
||||
state: &State<'_, DbConnection>,
|
||||
) -> Result<Vec<Vec<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: String = row
|
||||
.get(i)
|
||||
.map_err(|e| format!("Datentypfehler in Spalte {}: {}", i, e))?;
|
||||
row_data.push(value);
|
||||
}
|
||||
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 {
|
||||
OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE
|
||||
} else {
|
||||
OpenFlags::SQLITE_OPEN_READ_WRITE
|
||||
};
|
||||
|
||||
let conn = Connection::open_with_flags(path, flags).map_err(|e| e.to_string())?;
|
||||
conn.pragma_update(None, "key", key)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
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();
|
||||
extract_tables_from_set_expr(&query.body, &mut tables);
|
||||
tables
|
||||
}
|
||||
|
||||
fn extract_tables_from_set_expr(set_expr: &sqlparser::ast::SetExpr, tables: &mut Vec<String>) {
|
||||
match set_expr {
|
||||
sqlparser::ast::SetExpr::Select(select) => {
|
||||
for from in &select.from {
|
||||
extract_tables_from_table_with_joins(from, tables);
|
||||
}
|
||||
}
|
||||
sqlparser::ast::SetExpr::Query(query) => {
|
||||
extract_tables_from_set_expr(&query.body, tables);
|
||||
}
|
||||
sqlparser::ast::SetExpr::SetOperation { left, right, .. } => {
|
||||
extract_tables_from_set_expr(left, tables);
|
||||
extract_tables_from_set_expr(right, tables);
|
||||
}
|
||||
_ => (), // Andere Fälle wie Values oder Insert ignorieren
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_tables_from_table_with_joins(
|
||||
table_with_joins: &sqlparser::ast::TableWithJoins,
|
||||
tables: &mut Vec<String>,
|
||||
) {
|
||||
extract_tables_from_table_factor(&table_with_joins.relation, tables);
|
||||
for join in &table_with_joins.joins {
|
||||
extract_tables_from_table_factor(&join.relation, tables);
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_tables_from_table_factor(
|
||||
table_factor: &sqlparser::ast::TableFactor,
|
||||
tables: &mut Vec<String>,
|
||||
) {
|
||||
match table_factor {
|
||||
sqlparser::ast::TableFactor::Table { name, .. } => {
|
||||
tables.push(name.to_string());
|
||||
}
|
||||
sqlparser::ast::TableFactor::Derived { subquery, .. } => {
|
||||
extract_tables_from_set_expr(&subquery.body, tables);
|
||||
}
|
||||
sqlparser::ast::TableFactor::NestedJoin {
|
||||
table_with_joins, ..
|
||||
} => {
|
||||
extract_tables_from_table_with_joins(table_with_joins, tables);
|
||||
}
|
||||
_ => (), // Andere Fälle wie TableFunction ignorieren
|
||||
}
|
||||
}
|
||||
122
src-tauri/src/database/mod.rs
Normal file
122
src-tauri/src/database/mod.rs
Normal file
@ -0,0 +1,122 @@
|
||||
// database/mod.rs
|
||||
pub mod core;
|
||||
|
||||
use rusqlite::Connection;
|
||||
use std::path::Path;
|
||||
use std::sync::Mutex;
|
||||
use tauri::{path::BaseDirectory, AppHandle, Manager, State};
|
||||
|
||||
pub struct DbConnection(pub Mutex<Option<Connection>>);
|
||||
|
||||
// Öffentliche Funktionen für direkten Datenbankzugriff
|
||||
#[tauri::command]
|
||||
pub async fn sql_select(
|
||||
sql: String,
|
||||
params: Vec<String>,
|
||||
state: State<'_, DbConnection>,
|
||||
) -> Result<Vec<Vec<String>>, String> {
|
||||
core::select(&sql, ¶ms, &state).await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn sql_execute(
|
||||
sql: String,
|
||||
params: Vec<String>,
|
||||
state: State<'_, DbConnection>,
|
||||
) -> Result<String, String> {
|
||||
core::execute(&sql, ¶ms, &state).await
|
||||
}
|
||||
|
||||
/// Erstellt eine verschlüsselte Datenbank
|
||||
#[tauri::command]
|
||||
pub fn create_encrypted_database(
|
||||
app_handle: AppHandle,
|
||||
path: String,
|
||||
key: String,
|
||||
state: State<'_, DbConnection>,
|
||||
) -> Result<String, String> {
|
||||
// Ressourcenpfad zur eingebundenen Datenbank auflösen
|
||||
|
||||
let resource_path = app_handle
|
||||
.path()
|
||||
.resolve("resources/vault.db", BaseDirectory::Resource)
|
||||
.map_err(|e| format!("Fehler beim Auflösen des Ressourcenpfads: {}", e))?;
|
||||
|
||||
// Prüfen, ob die Ressourcendatei existiert
|
||||
if !resource_path.exists() {
|
||||
return Err(format!(
|
||||
"Ressourcendatenbank wurde nicht gefunden: {}",
|
||||
resource_path.display()
|
||||
));
|
||||
}
|
||||
|
||||
// Sicherstellen, dass das Zielverzeichnis existiert
|
||||
if let Some(parent) = Path::new(&path).parent() {
|
||||
if !parent.exists() {
|
||||
std::fs::create_dir_all(parent)
|
||||
.map_err(|e| format!("Fehler beim Erstellen des Zielverzeichnisses: {}", e))?;
|
||||
}
|
||||
}
|
||||
|
||||
// Kopieren der Ressourcen-Datenbank zum Zielpfad
|
||||
core::copy_file(&resource_path, &path)?;
|
||||
|
||||
// Ö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()
|
||||
)
|
||||
})?;
|
||||
|
||||
// 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()))?;
|
||||
|
||||
// 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(encrypted_conn);
|
||||
|
||||
Ok(format!(
|
||||
"Verschlüsselte CRDT-Datenbank erstellt unter: {} (kopiert aus Ressource)",
|
||||
path
|
||||
))
|
||||
}
|
||||
|
||||
/// Öffnet eine verschlüsselte Datenbank
|
||||
#[tauri::command]
|
||||
pub fn open_encrypted_database(
|
||||
path: String,
|
||||
key: String,
|
||||
state: State<'_, DbConnection>,
|
||||
) -> Result<String, String> {
|
||||
if !std::path::Path::new(&path).exists() {
|
||||
return Err("Datenbankdatei nicht gefunden".into());
|
||||
}
|
||||
|
||||
let conn = core::open_and_init_db(&path, &key, false)?;
|
||||
let mut db = state.0.lock().map_err(|e| e.to_string())?;
|
||||
*db = Some(conn);
|
||||
|
||||
Ok(format!("Verschlüsselte CRDT-Datenbank geöffnet: {}", path))
|
||||
}
|
||||
Reference in New Issue
Block a user