mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-16 22:20:51 +01:00
laden von erweiterungen implementiert
This commit is contained in:
@ -1,44 +0,0 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT id, extension_id, resource, operation, path \n FROM haex_extensions_permissions \n WHERE extension_id = ? AND resource = ? AND operation = ?\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "extension_id",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "resource",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "operation",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "path",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "a73e92ff12dca9b046a6440b9a68b002662b594f7f569ee71de11e00c23ca625"
|
||||
}
|
||||
5
src-tauri/Cargo.lock
generated
5
src-tauri/Cargo.lock
generated
@ -1536,6 +1536,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"fs_extra",
|
||||
"hex",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"rusqlite",
|
||||
@ -4516,9 +4517,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.44.1"
|
||||
version = "1.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a"
|
||||
checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
|
||||
@ -24,15 +24,16 @@ rusqlite = { version = "0.35.0", features = [
|
||||
] }
|
||||
#libsqlite3-sys = { version = "0.28", features = ["bundled-sqlcipher"] }
|
||||
#sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "sqlite"] }
|
||||
tokio = { version = "1.44", features = ["macros", "rt-multi-thread"] }
|
||||
tokio = { version = "1.45", features = ["macros", "rt-multi-thread"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
hex = "0.4"
|
||||
serde_json = "1"
|
||||
base64 = "0.22"
|
||||
mime_guess = "2.0"
|
||||
mime = "0.3"
|
||||
fs_extra = "1.3.0"
|
||||
sqlparser = { version = "0.56.0", features = [] }
|
||||
tauri = { version = "2.5", features = ["protocol-asset", "custom-protocol"] }
|
||||
tauri = { version = "2.5", features = ["protocol-asset"] }
|
||||
tauri-plugin-dialog = "2.2"
|
||||
tauri-plugin-fs = "2.2.0"
|
||||
tauri-plugin-opener = "2.2"
|
||||
|
||||
@ -227,5 +227,5 @@ pub fn open_encrypted_database(
|
||||
let mut db = state.0.lock().map_err(|e| e.to_string())?;
|
||||
*db = Some(conn);
|
||||
|
||||
Ok(format!("Verschlüsselte CRDT-Datenbank geöffnet: {}", path))
|
||||
Ok(format!("success"))
|
||||
}
|
||||
|
||||
@ -1,12 +1,70 @@
|
||||
use mime;
|
||||
use serde::Deserialize;
|
||||
use std::fmt;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use tauri::{
|
||||
http::{Request, Response, Uri},
|
||||
AppHandle, Error as TauriError, Manager, Runtime,
|
||||
http::{Request, Response},
|
||||
AppHandle, Error as TauriError, Manager, Runtime, UriSchemeContext,
|
||||
};
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct ExtensionInfo {
|
||||
id: String,
|
||||
version: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum DataProcessingError {
|
||||
HexDecoding(hex::FromHexError),
|
||||
Utf8Conversion(std::string::FromUtf8Error),
|
||||
JsonParsing(serde_json::Error),
|
||||
}
|
||||
|
||||
// Implementierung von Display für benutzerfreundliche Fehlermeldungen
|
||||
impl fmt::Display for DataProcessingError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
DataProcessingError::HexDecoding(e) => write!(f, "Hex-Dekodierungsfehler: {}", e),
|
||||
DataProcessingError::Utf8Conversion(e) => {
|
||||
write!(f, "UTF-8-Konvertierungsfehler: {}", e)
|
||||
}
|
||||
DataProcessingError::JsonParsing(e) => write!(f, "JSON-Parsing-Fehler: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implementierung von std::error::Error (optional, aber gute Praxis für bibliotheksähnlichen Code)
|
||||
impl std::error::Error for DataProcessingError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
DataProcessingError::HexDecoding(e) => Some(e),
|
||||
DataProcessingError::Utf8Conversion(e) => Some(e),
|
||||
DataProcessingError::JsonParsing(e) => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implementierung von From-Traits für einfache Verwendung des '?'-Operators
|
||||
impl From<hex::FromHexError> for DataProcessingError {
|
||||
fn from(err: hex::FromHexError) -> Self {
|
||||
DataProcessingError::HexDecoding(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::string::FromUtf8Error> for DataProcessingError {
|
||||
fn from(err: std::string::FromUtf8Error) -> Self {
|
||||
DataProcessingError::Utf8Conversion(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for DataProcessingError {
|
||||
fn from(err: serde_json::Error) -> Self {
|
||||
DataProcessingError::JsonParsing(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_directory(source: String, destination: String) -> Result<(), String> {
|
||||
println!(
|
||||
"Kopiere Verzeichnis von '{}' nach '{}'",
|
||||
@ -46,6 +104,7 @@ pub fn copy_directory(source: String, destination: String) -> Result<(), String>
|
||||
pub fn resolve_secure_extension_asset_path<R: Runtime>(
|
||||
app_handle: &AppHandle<R>,
|
||||
extension_id: &str,
|
||||
extension_version: &str,
|
||||
requested_asset_path: &str,
|
||||
) -> Result<PathBuf, String> {
|
||||
// 1. Validiere die Extension ID
|
||||
@ -57,6 +116,17 @@ pub fn resolve_secure_extension_asset_path<R: Runtime>(
|
||||
return Err(format!("Ungültige Extension ID: {}", extension_id));
|
||||
}
|
||||
|
||||
if extension_version.is_empty()
|
||||
|| !extension_version
|
||||
.chars()
|
||||
.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '.')
|
||||
{
|
||||
return Err(format!(
|
||||
"Ungültige Extension Version: {}",
|
||||
extension_version
|
||||
));
|
||||
}
|
||||
|
||||
// 2. Bestimme das Basisverzeichnis für alle Erweiterungen (Resource Directory)
|
||||
let base_extensions_dir = app_handle
|
||||
.path()
|
||||
@ -66,7 +136,8 @@ pub fn resolve_secure_extension_asset_path<R: Runtime>(
|
||||
.join("extensions");
|
||||
|
||||
// 3. Verzeichnis für die spezifische Erweiterung
|
||||
let specific_extension_dir = base_extensions_dir.join(extension_id);
|
||||
let specific_extension_dir =
|
||||
base_extensions_dir.join(format!("{}/{}", extension_id, extension_version));
|
||||
|
||||
// 4. Bereinige den angeforderten Asset-Pfad
|
||||
let clean_relative_path = requested_asset_path
|
||||
@ -112,70 +183,106 @@ pub fn resolve_secure_extension_asset_path<R: Runtime>(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_extension_protocol<R: Runtime>(
|
||||
app_handle: &AppHandle<R>,
|
||||
pub fn extension_protocol_handler<R: Runtime>(
|
||||
context: &UriSchemeContext<'_, R>,
|
||||
request: &Request<Vec<u8>>,
|
||||
) -> Result<Response<Vec<u8>>, Box<dyn std::error::Error>> {
|
||||
let uri_ref = request.uri(); // uri_ref ist &Uri
|
||||
let uri_ref = request.uri();
|
||||
println!("Protokoll Handler für: {}", uri_ref);
|
||||
|
||||
let uri_string = uri_ref.to_string(); // Konvertiere zu String
|
||||
let parsed_uri = Uri::from_str(&uri_string)?; // Parse aus &str
|
||||
|
||||
let extension_id = parsed_uri
|
||||
let host = uri_ref
|
||||
.host()
|
||||
.ok_or("Kein Host (Extension ID) in URI gefunden")?
|
||||
.to_string();
|
||||
|
||||
let requested_asset_path = parsed_uri.path();
|
||||
let path_str = uri_ref.path();
|
||||
let segments_iter = path_str.split('/').filter(|s| !s.is_empty());
|
||||
let resource_segments: Vec<&str> = segments_iter.collect();
|
||||
let raw_asset_path = resource_segments.join("/");
|
||||
|
||||
let asset_path_to_load = if requested_asset_path == "/" || requested_asset_path.is_empty() {
|
||||
"index.html"
|
||||
} else {
|
||||
requested_asset_path
|
||||
};
|
||||
match process_hex_encoded_json(&host) {
|
||||
Ok(info) => {
|
||||
println!("Daten erfolgreich verarbeitet:");
|
||||
println!(" ID: {}", info.id);
|
||||
println!(" Version: {}", info.version);
|
||||
let absolute_secure_path = resolve_secure_extension_asset_path(
|
||||
context.app_handle(),
|
||||
&info.id,
|
||||
&info.version,
|
||||
&raw_asset_path,
|
||||
)?;
|
||||
|
||||
// Sicheren Dateisystempfad auflösen (nutzt jetzt AppHandle)
|
||||
let absolute_secure_path =
|
||||
resolve_secure_extension_asset_path(app_handle, &extension_id, asset_path_to_load)?;
|
||||
println!("absolute_secure_path: {}", absolute_secure_path.display());
|
||||
|
||||
// Datei lesen und Response erstellen (Code wie vorher)
|
||||
match fs::read(&absolute_secure_path) {
|
||||
Ok(content) => {
|
||||
let mime_type = mime_guess::from_path(&absolute_secure_path)
|
||||
.first_or(mime::APPLICATION_OCTET_STREAM)
|
||||
.to_string();
|
||||
println!(
|
||||
"Liefere {} ({}) für Extension '{}'",
|
||||
absolute_secure_path.display(),
|
||||
mime_type,
|
||||
extension_id
|
||||
);
|
||||
// *** KORREKTUR: Verwende Response::builder() ***
|
||||
Response::builder()
|
||||
.status(200)
|
||||
.header("Content-Type", mime_type) // Setze Header über .header()
|
||||
.body(content) // body() gibt Result<Response<Vec<u8>>, Error> zurück
|
||||
.map_err(|e| e.into()) // Wandle http::Error in Box<dyn Error> um
|
||||
if absolute_secure_path.exists() && absolute_secure_path.is_file() {
|
||||
match fs::read(&absolute_secure_path) {
|
||||
Ok(content) => {
|
||||
let mime_type = mime_guess::from_path(&absolute_secure_path)
|
||||
.first_or(mime::APPLICATION_OCTET_STREAM)
|
||||
.to_string();
|
||||
println!(
|
||||
"Liefere {} ({}) ",
|
||||
absolute_secure_path.display(),
|
||||
mime_type
|
||||
);
|
||||
Response::builder()
|
||||
.status(200)
|
||||
.header("Content-Type", mime_type)
|
||||
.body(content)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"Fehler beim Lesen der Datei {}: {}",
|
||||
absolute_secure_path.display(),
|
||||
e
|
||||
);
|
||||
let status_code = if e.kind() == std::io::ErrorKind::NotFound {
|
||||
404
|
||||
} else if e.kind() == std::io::ErrorKind::PermissionDenied {
|
||||
403
|
||||
} else {
|
||||
500
|
||||
};
|
||||
|
||||
Response::builder()
|
||||
.status(status_code)
|
||||
.body(Vec::new()) // Leerer Body für Fehler
|
||||
.map_err(|e| e.into()) // Wandle http::Error in Box<dyn Error> um
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Datei nicht gefunden oder es ist keine Datei
|
||||
eprintln!(
|
||||
"Asset nicht gefunden oder ist kein File: {}",
|
||||
absolute_secure_path.display()
|
||||
);
|
||||
Response::builder()
|
||||
.status(404) // HTTP 404 Not Found
|
||||
.body(Vec::new())
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"Fehler beim Lesen der Datei {}: {}",
|
||||
absolute_secure_path.display(),
|
||||
e
|
||||
);
|
||||
let status_code = if e.kind() == std::io::ErrorKind::NotFound {
|
||||
404
|
||||
} else if e.kind() == std::io::ErrorKind::PermissionDenied {
|
||||
403
|
||||
} else {
|
||||
500
|
||||
};
|
||||
// *** KORREKTUR: Verwende Response::builder() auch für Fehler ***
|
||||
eprintln!("Fehler bei der Datenverarbeitung: {}", e);
|
||||
|
||||
Response::builder()
|
||||
.status(status_code)
|
||||
.status("500")
|
||||
.body(Vec::new()) // Leerer Body für Fehler
|
||||
.map_err(|e| e.into()) // Wandle http::Error in Box<dyn Error> um
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn process_hex_encoded_json(hex_input: &str) -> Result<ExtensionInfo, DataProcessingError> {
|
||||
// Schritt 1: Hex-String zu Bytes dekodieren
|
||||
let bytes = hex::decode(hex_input)?; // Konvertiert hex::FromHexError automatisch
|
||||
|
||||
// Schritt 2: Bytes zu UTF-8-String konvertieren
|
||||
let json_string = String::from_utf8(bytes)?; // Konvertiert FromUtf8Error automatisch
|
||||
|
||||
// Schritt 3: JSON-String zu Struktur parsen
|
||||
let extension_info: ExtensionInfo = serde_json::from_str(&json_string)?; // Konvertiert serde_json::Error automatisch
|
||||
|
||||
Ok(extension_info)
|
||||
}
|
||||
|
||||
@ -12,30 +12,37 @@ pub fn run() {
|
||||
let protocol_name = "haex-extension";
|
||||
|
||||
tauri::Builder::default()
|
||||
/* .register_uri_scheme_protocol(protocol_name, move |app_handle, request| {
|
||||
// Extrahiere den Request aus dem Kontext
|
||||
//let request = context.request();
|
||||
// Rufe die Handler-Logik auf
|
||||
match extension::core::handle_extension_protocol(0, &request) {
|
||||
Ok(response) => response, // Gib die erfolgreiche Response zurück
|
||||
.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
|
||||
Err(e) => {
|
||||
// Logge den Fehler
|
||||
eprintln!("Fehler im Protokoll-Handler für '{}': {}", request.uri(), e);
|
||||
// Gib eine generische 500er Fehler-Response zurück
|
||||
Response::builder()
|
||||
.status(500)
|
||||
.mimetype("text/plain") // Einfacher Text für die Fehlermeldung
|
||||
.body(format!("Internal Server Error: {}", e).into_bytes()) // Body als Vec<u8>
|
||||
.unwrap() // .body() kann hier nicht fehlschlagen
|
||||
// Wenn der Handler einen Fehler zurückgibt, logge ihn und erstelle eine Fehler-Response
|
||||
eprintln!(
|
||||
"Fehler im Custom Protocol Handler für URI '{}': {}",
|
||||
request.uri(),
|
||||
e
|
||||
);
|
||||
// Erstelle eine HTTP 500 Fehler-Response
|
||||
// Du kannst hier auch spezifischere Fehler-Responses bauen, falls gewünscht.
|
||||
tauri::http::Response::builder()
|
||||
.status(500)
|
||||
.header("Content-Type", "text/plain") // Optional, aber gut für Klarheit
|
||||
.body(Vec::from(format!(
|
||||
"Interner Serverfehler im Protokollhandler: {}",
|
||||
e
|
||||
)))
|
||||
.unwrap_or_else(|build_err| {
|
||||
// Fallback, falls selbst das Erstellen der Fehler-Response fehlschlägt
|
||||
eprintln!("Konnte Fehler-Response nicht erstellen: {}", build_err);
|
||||
tauri::http::Response::builder()
|
||||
.status(500)
|
||||
.body(Vec::new())
|
||||
.expect("Konnte minimale Fallback-Response nicht erstellen")
|
||||
})
|
||||
}
|
||||
}
|
||||
}) */
|
||||
/* .setup(move |app| {
|
||||
// Der .setup Hook ist jetzt nur noch für andere Initialisierungen da
|
||||
// Der AppHandle ist hier nicht mehr nötig für die Protokoll-Registrierung
|
||||
println!("App Setup abgeschlossen.");
|
||||
Ok(())
|
||||
}) */
|
||||
//extension::core::extension_protocol_handler(&context, &request)
|
||||
})
|
||||
.plugin(tauri_plugin_http::init())
|
||||
.manage(DbConnection(Mutex::new(None)))
|
||||
.manage(ExtensionState::default())
|
||||
@ -57,3 +64,29 @@ pub fn run() {
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
||||
/* fn extension_protocol_handler(
|
||||
app_handle: &tauri::AppHandle, // Beachten Sie die Signaturänderung in neueren Tauri-Versionen
|
||||
request: &tauri::http::Request<Vec<u8>>,
|
||||
) -> Result<tauri::http::Response<Vec<u8>>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let uri_str = request.uri().to_string();
|
||||
let parsed_url = match Url::parse(&uri_str) {
|
||||
Ok(url) => url,
|
||||
Err(e) => {
|
||||
eprintln!("Fehler beim Parsen der URL '{}': {}", uri_str, e);
|
||||
return Ok(tauri::http::ResponseBuilder::new().status(400).body(Vec::from("Ungültige URL"))?);
|
||||
}
|
||||
};
|
||||
|
||||
let plugin_id = parsed_url.host_str().ok_or_else(|| "Fehlende Plugin-ID in der URL".to_string())?;
|
||||
let path_segments: Vec<&str> = parsed_url.path_segments().ok_or_else(|| "URL hat keinen Pfad".to_string())?.collect();
|
||||
|
||||
if path_segments.len() < 2 {
|
||||
eprintln!("Unvollständiger Pfad in URL: {}", uri_str);
|
||||
return Ok(tauri::http::Response::new().status(400).body(Vec::from("Unvollständiger Pfad"))?);
|
||||
}
|
||||
|
||||
let version = path_segments;
|
||||
let file_path = path_segments[1..].join("/");
|
||||
Ok(tauri::http::Response::builder()::new().status(404).body(Vec::new())?)
|
||||
} */
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
"beforeDevCommand": "pnpm dev",
|
||||
"devUrl": "http://localhost:3003",
|
||||
"beforeBuildCommand": "pnpm generate",
|
||||
"frontendDist": "../dist"
|
||||
"frontendDist": "../.output/public"
|
||||
},
|
||||
"app": {
|
||||
"windows": [
|
||||
@ -18,7 +18,14 @@
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": "default-src 'self' ipc: http://ipc.localhost; img-src 'self' asset: http://asset.localhost",
|
||||
"csp": {
|
||||
"default-src": ["'self'", "haex-extensions"],
|
||||
"script-src": ["'self'", "haex-extensions"],
|
||||
"style-src": ["'self'", "haex-extensions"],
|
||||
"connect-src": ["'self'", "haex-extensions"],
|
||||
"img-src": ["'self'", "haex-extensions", "data:"],
|
||||
"font-src": ["'self'", "haex-extensions", "data:"]
|
||||
},
|
||||
"assetProtocol": {
|
||||
"enable": true,
|
||||
"scope": ["$RESOURCE/extensions/**"]
|
||||
|
||||
Reference in New Issue
Block a user