init commit

This commit is contained in:
Martin Drechsel
2025-04-02 18:54:55 +02:00
commit 2c5ec6b281
126 changed files with 21323 additions and 0 deletions

2
src-tauri/.env Normal file
View File

@ -0,0 +1,2 @@
DATABASE_URL=sqlite:database/vault.db
SQLX_OFFLINE=true

7
src-tauri/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
# Generated by Cargo
# will have compiled files and executables
/target/
# Generated by Tauri
# will have schema files for capabilities auto-completion
/gen/schemas

View File

@ -0,0 +1,44 @@
{
"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"
}

5862
src-tauri/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

37
src-tauri/Cargo.toml Normal file
View File

@ -0,0 +1,37 @@
[package]
name = "haex-hub"
version = "0.1.0"
description = "A Tauri App"
authors = ["you"]
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
# The `_lib` suffix may seem redundant but it is necessary
# to make the lib name unique and wouldn't conflict with the bin name.
# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
name = "haex_hub_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies]
tauri-build = { version = "2", features = [] }
[dependencies]
rusqlite = { version = "0.34.0", features = [
"load_extension",
"bundled-sqlcipher",
] }
#libsqlite3-sys = { version = "0.32", features = ["bundled-sqlcipher"] }
tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread"] }
#libsqlite3-sys = { version = "0.28.0", features = ["bundled-sqlcipher"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sqlparser = { version = "0.55.0", features = [] }
tauri = { version = "2", features = [] }
tauri-plugin-dialog = "2"
tauri-plugin-fs = "2"
tauri-plugin-opener = "2"
tauri-plugin-os = "2"
tauri-plugin-store = "2"
tauri-plugin-http = "2"

3
src-tauri/build.rs Normal file
View File

@ -0,0 +1,3 @@
fn main() {
tauri_build::build()
}

View File

@ -0,0 +1,28 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"core:default",
"dialog:default",
"fs:allow-read-file",
"fs:allow-resource-read-recursive",
"fs:default",
"http:allow-fetch-send",
"http:allow-fetch",
"http:default",
"opener:allow-open-url",
"opener:default",
"os:default",
"store:default",
"core:window:allow-create",
"core:window:default",
"core:window:allow-get-all-windows",
"core:window:allow-show",
"core:webview:allow-create-webview",
"core:webview:allow-create-webview-window",
"core:webview:default",
"core:webview:allow-webview-show"
]
}

View File

@ -0,0 +1,26 @@
CREATE TABLE `haex_extensions` (
`id` text PRIMARY KEY NOT NULL,
`author` text,
`enabled` integer,
`name` text,
`url` text,
`version` text
);
--> statement-breakpoint
CREATE TABLE `haex_extensions_permissions` (
`id` text PRIMARY KEY NOT NULL,
`extension_id` text,
`resource` text,
`operation` text,
`path` text,
FOREIGN KEY (`extension_id`) REFERENCES `haex_extensions`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
CREATE UNIQUE INDEX `haex_extensions_permissions_extension_id_resource_operation_path_unique` ON `haex_extensions_permissions` (`extension_id`,`resource`,`operation`,`path`);--> statement-breakpoint
CREATE TABLE `haex_settings` (
`id` text PRIMARY KEY NOT NULL,
`key` text,
`value_text` text,
`value_json` text,
`value_number` numeric
);

View File

@ -0,0 +1,185 @@
{
"version": "6",
"dialect": "sqlite",
"id": "fc5a7c9d-4846-4120-a762-cc2ea00504b9",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"haex_extensions": {
"name": "haex_extensions",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"author": {
"name": "author",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"enabled": {
"name": "enabled",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"url": {
"name": "url",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"version": {
"name": "version",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"haex_extensions_permissions": {
"name": "haex_extensions_permissions",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"extension_id": {
"name": "extension_id",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"resource": {
"name": "resource",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"operation": {
"name": "operation",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"path": {
"name": "path",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"haex_extensions_permissions_extension_id_resource_operation_path_unique": {
"name": "haex_extensions_permissions_extension_id_resource_operation_path_unique",
"columns": [
"extension_id",
"resource",
"operation",
"path"
],
"isUnique": true
}
},
"foreignKeys": {
"haex_extensions_permissions_extension_id_haex_extensions_id_fk": {
"name": "haex_extensions_permissions_extension_id_haex_extensions_id_fk",
"tableFrom": "haex_extensions_permissions",
"tableTo": "haex_extensions",
"columnsFrom": [
"extension_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"haex_settings": {
"name": "haex_settings",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"key": {
"name": "key",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"value_text": {
"name": "value_text",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"value_json": {
"name": "value_json",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"value_number": {
"name": "value_number",
"type": "numeric",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"indexes": {}
}
}

View File

@ -0,0 +1,13 @@
{
"version": "7",
"dialect": "sqlite",
"entries": [
{
"idx": 0,
"version": "6",
"when": 1742903332283,
"tag": "0000_zippy_scourge",
"breakpoints": true
}
]
}

View File

@ -0,0 +1,52 @@
import {
integer,
sqliteTable,
text,
type AnySQLiteColumn,
unique,
numeric,
} from 'drizzle-orm/sqlite-core';
export const haexSettings = sqliteTable('haex_settings', {
id: text().primaryKey(),
key: text(),
value_text: text(),
value_json: text({ mode: 'json' }),
value_number: numeric(),
});
export const haexExtensions = sqliteTable('haex_extensions', {
id: text().primaryKey(),
author: text(),
enabled: integer(),
name: text(),
url: text(),
version: text(),
});
export const haexExtensionsPermissions = sqliteTable(
'haex_extensions_permissions',
{
id: text().primaryKey(),
extensionId: text('extension_id').references(
(): AnySQLiteColumn => haexExtensions.id
),
resource: text({ enum: ['fs', 'http', 'database'] }),
operation: text({ enum: ['read', 'write', 'create'] }),
path: text(),
},
(table) => [
unique().on(table.extensionId, table.resource, table.operation, table.path),
]
);
export type InsertHaexSettings = typeof haexSettings.$inferInsert;
export type SelectHaexSettings = typeof haexSettings.$inferSelect;
export type InsertHaexExtensions = typeof haexExtensions.$inferInsert;
export type SelectHaexExtensions = typeof haexExtensions.$inferSelect;
export type InsertHaexExtensionsPermissions =
typeof haexExtensionsPermissions.$inferInsert;
export type SelectHaexExtensionsPermissions =
typeof haexExtensionsPermissions.$inferSelect;

BIN
src-tauri/database/vault.db Normal file

Binary file not shown.

BIN
src-tauri/icons/128x128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
src-tauri/icons/32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
src-tauri/icons/64x64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
src-tauri/icons/icon.icns Normal file

Binary file not shown.

BIN
src-tauri/icons/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
src-tauri/icons/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 834 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

@ -0,0 +1,285 @@
//mod middleware;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use tauri::{webview, AppHandle, LogicalPosition, LogicalSize, Manager, WebviewUrl, Window};
//use uuid::Uuid;
#[derive(Debug, Clone)]
pub struct Tab {
pub id: String,
pub webview_label: String,
pub title: String,
pub url: String,
pub is_loading: bool,
pub is_visible: bool,
}
pub struct BrowserManager {
tabs: Arc<Mutex<HashMap<String, Tab>>>,
active_tab_id: Arc<Mutex<Option<String>>>,
//middleware: Arc<RoutingMiddleware>,
}
impl BrowserManager {
pub fn new() -> Self {
Self {
tabs: Arc::new(Mutex::new(HashMap::new())),
active_tab_id: Arc::new(Mutex::new(None)),
//middleware: Arc::new(RoutingMiddleware::new()),
}
}
/* pub async fn create_window(app: tauri::AppHandle) -> Result<tauri::WebviewWindow, _> {
let webview_window = tauri::WebviewWindowBuilder::new(
&app,
"label",
tauri::WebviewUrl::App("index.html".into()),
)
.build()
.unwrap();
Ok(webview_window);
} */
pub fn create_tab(&self, app: AppHandle, url: &str) {
// Generiere eine eindeutige ID für den Tab
/* let tab_id = Uuid::new_v4().to_string();
let webview_label = format!("webview-{}", tab_id); */
// Überprüfe URL mit Middleware
//let processed_url = self.middleware.process_url(url);
// Hole das Hauptfenster
let main_window = app.get_webview_window("main").unwrap();
// Berechne die Position und Größe für den Webview
// Hier nehmen wir an, dass wir einen Header-Bereich von 100 Pixeln haben
/* let window_size = main_window.inner_size()?;
let header_height = 100.0;
let webview_position = LogicalPosition::new(0.0, header_height);
let webview_size = LogicalSize::new(window_size.width, window_size.height - header_height);
*/
/* let webview = tauri::WebviewWindowBuilder::new(
&app,
"label",
//WebviewUrl::External(processed_url.parse().unwrap()),
WebviewUrl::External(url),
)
.build()
.unwrap() */
/* .on_navigation(move |url| {
// Middleware für Navigation anwenden
self.middleware.process_navigation(url.as_str())
})
.on_web_resource_request(move |request, response| {
// Middleware für HTTP-Anfragen anwenden
self.middleware.process_request(request, response)
}); */
// Erstelle Tab-Objekt
/* let tab = Tab {
id: tab_id.clone(),
webview_label: webview_label.clone(),
title: "Neuer Tab".to_string(),
url: processed_url.to_string(),
is_loading: true,
is_visible: false,
}; */
// Speichere Tab
/* {
let mut tabs = self.tabs.lock().unwrap();
tabs.insert(tab_id.clone(), tab.clone());
} */
// Setze als aktiven Tab
//self.activate_tab(app, &tab_id)?;
// Injiziere die Webview-Bridge
/* let script = include_str!("../assets/webview-bridge.js");
webview.evaluate_script(script)?; */
// Registriere Event-Handler für Titeländerungen
let tab_manager = self.clone();
//let tab_id_clone = tab_id.clone();
/* webview.listen("tauri://title-changed", move |event| {
if let Some(title) = event.payload().and_then(|p| p.as_str()) {
tab_manager.update_tab_title(&tab_id_clone, title);
}
}); */
// Registriere Event-Handler für Ladestatus
let tab_manager = self.clone();
//let tab_id_clone = tab_id.clone();
/* webview.listen("tauri://load-changed", move |event| {
if let Some(status) = event.payload().and_then(|p| p.as_str()) {
let is_loading = status == "loading";
tab_manager.update_tab_loading_status(&tab_id_clone, is_loading);
}
}); */
//Ok()
}
pub fn close_tab(&self, app: &AppHandle, tab_id: &str) -> Result<(), tauri::Error> {
// Hole das Hauptfenster
let main_window = app.get_webview_window("main").unwrap();
// Entferne Tab aus der Verwaltung
let webview_label = {
let mut tabs = self.tabs.lock().unwrap();
if let Some(tab) = tabs.remove(tab_id) {
tab.webview_label
} else {
return Ok(());
}
};
// Entferne den Webview
//main_window.remove_child(&webview_label)?;
// Aktualisiere aktiven Tab, falls nötig
{
let mut active_tab_id = self.active_tab_id.lock().unwrap();
if active_tab_id.as_ref().map_or(false, |id| id == tab_id) {
// Wähle einen anderen Tab als aktiv
let tabs = self.tabs.lock().unwrap();
*active_tab_id = tabs.keys().next().cloned();
// Aktiviere den neuen Tab, falls vorhanden
if let Some(new_active_id) = active_tab_id.clone() {
drop(active_tab_id); // Mutex freigeben vor dem rekursiven Aufruf
self.activate_tab(app, &new_active_id)?;
}
}
}
Ok(())
}
pub fn activate_tab(&self, app: &AppHandle, tab_id: &str) -> Result<(), tauri::Error> {
// Hole das Hauptfenster
let main_window = app.get_webview_window("main").unwrap();
// Setze Tab als aktiv
{
let mut active_tab_id = self.active_tab_id.lock().unwrap();
*active_tab_id = Some(tab_id.to_string());
}
// Verstecke alle anderen Tabs und zeige den aktiven
let mut tabs = self.tabs.lock().unwrap();
for (id, tab) in tabs.iter_mut() {
if id == tab_id {
// Zeige den aktiven Tab
/* main_window
.get_webview_window(&tab.webview_label)?
.set_visible(true)?; */
tab.is_visible = true;
} else {
// Verstecke alle anderen Tabs
/* main_window
.get_webview_window(&tab.webview_label)?
.set_visible(false)?; */
tab.is_visible = false;
}
}
Ok(())
}
pub fn navigate_to_url(
&self,
app: &AppHandle,
tab_id: &str,
url: &str,
) -> Result<(), tauri::Error> {
// Überprüfe URL mit Middleware
//let processed_url = self.middleware.process_url(url);
// Aktualisiere URL im Tab
{
let mut tabs = self.tabs.lock().unwrap();
if let Some(tab) = tabs.get_mut(tab_id) {
tab.url = url.to_string() //processed_url.to_string();
}
}
// Navigiere zum URL im Webview
let tabs = self.tabs.lock().unwrap();
if let Some(tab) = tabs.get(tab_id) {
let main_window = app.get_webview_window("main").unwrap();
/* let webview = main_window.get_webview_window(&tab.webview_label)?;
webview.navigate(&processed_url)?; */
}
Ok(())
}
pub fn get_all_tabs(&self) -> Vec<Tab> {
let tabs = self.tabs.lock().unwrap();
tabs.values().cloned().collect()
}
pub fn get_active_tab_id(&self) -> Option<String> {
let active_tab_id = self.active_tab_id.lock().unwrap();
active_tab_id.clone()
}
pub fn update_tab_title(&self, tab_id: &str, title: &str) {
let mut tabs = self.tabs.lock().unwrap();
if let Some(tab) = tabs.get_mut(tab_id) {
tab.title = title.to_string();
}
}
pub fn update_tab_loading_status(&self, tab_id: &str, is_loading: bool) {
let mut tabs = self.tabs.lock().unwrap();
if let Some(tab) = tabs.get_mut(tab_id) {
tab.is_loading = is_loading;
}
}
// Weitere Methoden für Browser-Navigation
pub fn go_back(&self, app: &AppHandle, tab_id: &str) -> Result<(), tauri::Error> {
let tabs = self.tabs.lock().unwrap();
if let Some(tab) = tabs.get(tab_id) {
let main_window = app.get_webview_window("main").unwrap();
/* let webview = main_window.get_webview(&tab.webview_label)?;
webview.evaluate_script("window.history.back()")?; */
}
Ok(())
}
pub fn go_forward(&self, app: &AppHandle, tab_id: &str) -> Result<(), tauri::Error> {
let tabs = self.tabs.lock().unwrap();
if let Some(tab) = tabs.get(tab_id) {
let main_window = app.get_webview_window("main").unwrap();
/* let webview = main_window.get_webview(&tab.webview_label)?;
webview.evaluate_script("window.history.forward()")?; */
}
Ok(())
}
pub fn inject_content_script(
&self,
app: &AppHandle,
tab_id: &str,
script: &str,
) -> Result<(), tauri::Error> {
let tabs = self.tabs.lock().unwrap();
if let Some(tab) = tabs.get(tab_id) {
let main_window = app.get_webview_window("main").unwrap();
/* let webview = main_window.get_webview(&tab.webview_label)?;
webview.evaluate_script(script)?; */
}
Ok(())
}
pub fn clone(&self) -> Self {
Self {
tabs: Arc::clone(&self.tabs),
active_tab_id: Arc::clone(&self.active_tab_id),
//middleware: Arc::clone(&self.middleware),
}
}
}

View File

@ -0,0 +1,125 @@
use std::sync::{Arc, Mutex};
use tauri::http::{Request, Response, ResponseBuilder};
pub struct RoutingMiddleware {
extensions: Arc<Mutex<Vec<Box<dyn MiddlewareExtension + Send + Sync>>>>,
}
pub trait MiddlewareExtension: Send + Sync {
fn name(&self) -> &str;
fn process_url(&self, url: &str) -> String;
fn process_navigation(&self, url: &str) -> bool;
fn process_request(&self, request: &Request, response: &mut Response) -> bool;
}
impl RoutingMiddleware {
pub fn new() -> Self {
let mut middleware = Self {
extensions: Arc::new(Mutex::new(Vec::new())),
};
// Registriere Standard-Erweiterungen
//middleware.register_extension(Box::new(AdBlockerExtension::new()));
middleware
}
pub fn register_extension(&mut self, extension: Box<dyn MiddlewareExtension + Send + Sync>) {
let mut extensions = self.extensions.lock().unwrap();
extensions.push(extension);
}
pub fn process_url(&self, url: &str) -> String {
let extensions = self.extensions.lock().unwrap();
let mut processed_url = url.to_string();
for extension in extensions.iter() {
processed_url = extension.process_url(&processed_url);
}
processed_url
}
pub fn process_navigation(&self, url: &str) -> bool {
let extensions = self.extensions.lock().unwrap();
for extension in extensions.iter() {
if !extension.process_navigation(url) {
return false;
}
}
true
}
pub fn process_request(&self, request: &Request, response: &mut Response) -> bool {
let extensions = self.extensions.lock().unwrap();
for extension in extensions.iter() {
if extension.process_request(request, response) {
return true;
}
}
false
}
}
// Beispiel für eine Ad-Blocker-Erweiterung
struct AdBlockerExtension {
block_patterns: Vec<String>,
}
impl AdBlockerExtension {
fn new() -> Self {
Self {
block_patterns: vec![
"ads".to_string(),
"analytics".to_string(),
"tracker".to_string(),
"banner".to_string(),
],
}
}
fn is_blocked_url(&self, url: &str) -> bool {
for pattern in &self.block_patterns {
if url.contains(pattern) {
return true;
}
}
false
}
}
impl MiddlewareExtension for AdBlockerExtension {
fn name(&self) -> &str {
"AdBlocker"
}
fn process_url(&self, url: &str) -> String {
// Für vollständige Navigationen blockieren wir normalerweise nicht die ganze Seite
url.to_string()
}
fn process_navigation(&self, url: &str) -> bool {
// Blockiere nur vollständige Navigationen zu Werbeseiten
let is_ad_site = url.contains("doubleclick.net")
|| url.contains("googleadservices.com")
|| url.contains("ads.example.com");
!is_ad_site
}
fn process_request(&self, request: &Request, response: &mut Response) -> bool {
let url = request.uri().to_string();
if self.is_blocked_url(&url) {
println!("AdBlocker: Blockiere Anfrage: {}", url);
*response = ResponseBuilder::new()
.status(403)
.body("Zugriff verweigert durch AdBlocker".as_bytes().to_vec())
.unwrap();
return true;
}
false
}
}

View File

@ -0,0 +1,188 @@
use serde::{Deserialize, Serialize};
use tauri::{AppHandle, Manager, State};
mod manager;
#[derive(Serialize, Deserialize)]
pub struct TabInfo {
id: String,
title: String,
url: String,
is_loading: bool,
is_active: bool,
}
// Einfache Kommandos für die Tab-Verwaltung
#[tauri::command]
pub fn create_tab(app_handle: tauri::AppHandle, tab_id: String, url: String) -> Result<(), String> {
let main_window = app_handle
.get_webview_window("main")
.ok_or("Hauptfenster nicht gefunden")?;
let window_size = main_window.inner_size().map_err(|e| e.to_string())?;
// Erstelle eine neue Webview als eigenständiges Fenster
let webview = tauri::WebviewWindowBuilder::new(
&app_handle,
tab_id.clone(),
tauri::WebviewUrl::External(url.parse::<tauri::Url>().map_err(|e| e.to_string())?),
//tauri::WebviewUrl::External("http://google.de"),
)
.title(format!("Tab: {}", tab_id))
.inner_size(window_size.width as f64, window_size.height as f64 - 50.0)
.position(0.0, 50.0)
.build()
.map_err(|e| e.to_string())?;
// Sende die Tab-ID zurück an das Hauptfenster
/* main_window
.emit("tab-created", tab_id)
.map_err(|e| e.to_string())?; */
Ok(())
}
#[tauri::command]
pub fn show_tab(app_handle: tauri::AppHandle, tab_id: String) -> Result<(), String> {
// Hole alle Webview-Fenster
let windows = app_handle.webview_windows();
// Zeige das ausgewählte Tab und verstecke die anderen
for (id, window) in windows {
if id != "main" {
// Hauptfenster nicht verstecken
if id == tab_id {
window.show().map_err(|e| e.to_string())?;
window.set_focus().map_err(|e| e.to_string())?;
} else {
window.hide().map_err(|e| e.to_string())?;
}
}
}
Ok(())
}
#[tauri::command]
pub fn close_tab(app_handle: tauri::AppHandle, tab_id: String) -> Result<(), String> {
if let Some(window) = app_handle.get_webview_window(&tab_id) {
window.close().map_err(|e| e.to_string())?;
}
Ok(())
}
/* #[tauri::command]
pub fn create_tab(app: AppHandle, url: String) -> Result<TabInfo, String> {
let browser_manager = app.state::<manager::BrowserManager>();
match browser_manager.create_tab(&app, &url) {
Ok(tab) => {
let active_tab_id = browser_manager.get_active_tab_id();
let is_active = active_tab_id.as_ref().map_or(false, |id| id == &tab.id);
let main = app.get_webview_window("main");
//main.unwrap().
// Sende Event an Frontend
/* app.emit_all(
"tab-created",
TabInfo {
id: tab.id.clone(),
title: tab.title.clone(),
url: tab.url.clone(),
is_loading: tab.is_loading,
is_active,
},
)
.unwrap(); */
Ok(TabInfo {
id: tab.id,
title: tab.title,
url: tab.url,
is_loading: tab.is_loading,
is_active: true,
})
}
Err(e) => Err(format!("Fehler beim Erstellen des Tabs: {}", e)),
}
} */
/* #[tauri::command]
pub fn close_tab(app: AppHandle, tab_id: String) -> Result<(), String> {
let browser_manager = app.state::<manager::BrowserManager>();
match browser_manager.close_tab(&app, &tab_id) {
Ok(_) => {
// Sende Event an Frontend
//app.emit_all("tab-closed", tab_id).unwrap();
Ok(())
}
Err(e) => Err(format!("Fehler beim Schließen des Tabs: {}", e)),
}
} */
#[tauri::command]
pub fn navigate_to_url(app: AppHandle, tab_id: String, url: String) -> Result<(), String> {
let browser_manager = app.state::<manager::BrowserManager>();
match browser_manager.navigate_to_url(&app, &tab_id, &url) {
Ok(_) => Ok(()),
Err(e) => Err(format!("Fehler bei der Navigation: {}", e)),
}
}
#[tauri::command]
pub fn get_current_url(app: AppHandle, tab_id: String) -> Result<String, String> {
let browser_manager = app.state::<manager::BrowserManager>();
let tabs = browser_manager.get_all_tabs();
for tab in tabs {
if tab.id == tab_id {
return Ok(tab.url);
}
}
Err("Tab nicht gefunden".to_string())
}
#[tauri::command]
pub fn go_back(app: AppHandle, tab_id: String) -> Result<(), String> {
let browser_manager = app.state::<manager::BrowserManager>();
match browser_manager.go_back(&app, &tab_id) {
Ok(_) => Ok(()),
Err(e) => Err(format!("Fehler beim Zurückgehen: {}", e)),
}
}
#[tauri::command]
pub fn go_forward(app: AppHandle, tab_id: String) -> Result<(), String> {
let browser_manager = app.state::<manager::BrowserManager>();
match browser_manager.go_forward(&app, &tab_id) {
Ok(_) => Ok(()),
Err(e) => Err(format!("Fehler beim Vorwärtsgehen: {}", e)),
}
}
#[tauri::command]
pub fn block_resource_request(url: String, resource_type: String) -> bool {
// Diese Funktion wird vom Frontend aufgerufen, um zu prüfen, ob eine Ressource blockiert werden soll
// Die eigentliche Logik wird im JavaScript-Erweiterungssystem implementiert
// Hier könnten Sie zusätzliche Rust-seitige Prüfungen durchführen
println!("Prüfe Ressourcenanfrage: {} (Typ: {})", url, resource_type);
// Einfache Prüfung für Beispielzwecke
url.contains("ads") || url.contains("analytics") || url.contains("tracker")
}
#[tauri::command]
pub fn inject_content_script(app: AppHandle, tab_id: String, script: String) -> Result<(), String> {
let browser_manager = app.state::<manager::BrowserManager>();
match browser_manager.inject_content_script(&app, &tab_id, &script) {
Ok(_) => Ok(()),
Err(e) => Err(format!("Fehler beim Injizieren des Scripts: {}", e)),
}
}

View 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
}
}

View 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, &params, &state).await
}
#[tauri::command]
pub async fn sql_execute(
sql: String,
params: Vec<String>,
state: State<'_, DbConnection>,
) -> Result<String, String> {
core::execute(&sql, &params, &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))
}

View File

@ -0,0 +1,46 @@
mod permissions;
use crate::database;
use crate::database::DbConnection;
//use crate::models::ExtensionState;
use tauri::{AppHandle, State};
// Extension-bezogene Funktionen mit extension_-Präfix
/// Lädt eine Extension aus einer Manifest-Datei
/* #[tauri::command]
pub fn extension_load(
manifest_path: String,
app: AppHandle,
) -> Result<crate::models::ExtensionManifest, String> {
let manifest_content = std::fs::read_to_string(&manifest_path).map_err(|e| e.to_string())?;
let manifest: crate::models::ExtensionManifest =
serde_json::from_str(&manifest_content).map_err(|e| e.to_string())?;
app.state::<ExtensionState>()
.add_extension(manifest_path.clone(), manifest.clone());
Ok(manifest)
} */
/// Führt SQL-Leseoperationen mit Berechtigungsprüfung aus
#[tauri::command]
pub async fn extension_sql_select(
app: AppHandle,
extension_id: String,
sql: String,
params: Vec<String>,
state: State<'_, DbConnection>,
) -> Result<Vec<Vec<String>>, String> {
permissions::check_read_permission(&app, &extension_id, &sql).await?;
database::core::select(&sql, &params, &state).await
}
/// Führt SQL-Schreiboperationen mit Berechtigungsprüfung aus
#[tauri::command]
pub async fn extension_sql_execute(
app: AppHandle,
extension_id: String,
sql: String,
params: Vec<String>,
state: State<'_, DbConnection>,
) -> Result<String, String> {
permissions::check_write_permission(&app, &extension_id, &sql).await?;
database::core::execute(&sql, &params, &state).await
}

View File

@ -0,0 +1,203 @@
// database/permissions.rs
use crate::database::core::extract_tables_from_query;
use crate::database::DbConnection;
use crate::models::DbExtensionPermission;
use sqlparser::dialect::SQLiteDialect;
use sqlparser::parser::Parser;
use tauri::{AppHandle, Manager};
/// Prüft Leseberechtigungen für eine Extension basierend auf Datenbankeinträgen
pub async fn check_read_permission(
app: &AppHandle,
extension_id: &str,
sql: &str,
) -> Result<(), String> {
// SQL-Statement parsen
let dialect = SQLiteDialect {};
let statements = Parser::parse_sql(&dialect, sql).map_err(|e| e.to_string())?;
let statement = statements
.into_iter()
.next()
.ok_or("Keine SQL-Anweisung gefunden")?;
// Berechtigungsprüfung für SELECT-Statements
if let sqlparser::ast::Statement::Query(query) = statement {
let tables = extract_tables_from_query(&query);
// Berechtigungen aus der Datenbank abrufen
let db_state = app.state::<DbConnection>();
let permissions =
get_extension_permissions(db_state, extension_id, "database", "read").await?;
// Prüfen, ob alle benötigten Tabellen in den Berechtigungen enthalten sind
for table in tables {
let has_permission = permissions.iter().any(|perm| perm.path.contains(&table));
if !has_permission {
return Err(format!("Keine Leseberechtigung für Tabelle {}", table));
}
}
Ok(())
} else {
Err("Nur SELECT-Anweisungen erlaubt".into())
}
}
/// Prüft Schreibberechtigungen für eine Extension basierend auf Datenbankeinträgen
pub async fn check_write_permission(
app: &AppHandle,
extension_id: &str,
sql: &str,
) -> Result<(), String> {
// SQL-Statement parsen
let dialect = SQLiteDialect {};
let statements = Parser::parse_sql(&dialect, sql).map_err(|e| e.to_string())?;
let statement = statements
.into_iter()
.next()
.ok_or("Keine SQL-Anweisung gefunden")?;
// Berechtigungsprüfung basierend auf Statement-Typ
match statement {
sqlparser::ast::Statement::Insert(insert) => {
let table_name = match insert.table {
sqlparser::ast::TableObject::TableName(name) => name.to_string(),
_ => return Err("Ungültige Tabellenangabe in INSERT".into()),
};
// Berechtigungen aus der Datenbank abrufen
let db_state = app.state::<DbConnection>();
let permissions =
get_extension_permissions(db_state, extension_id, "database", "write").await?;
// Prüfen, ob die Tabelle in den Berechtigungen enthalten ist
let has_permission = permissions
.iter()
.any(|perm| perm.path.contains(&table_name));
if !has_permission {
return Err(format!(
"Keine Schreibberechtigung für Tabelle {}",
table_name
));
}
}
sqlparser::ast::Statement::Update { table, .. } => {
let table_name = table.relation.to_string();
// Berechtigungen aus der Datenbank abrufen
let db_state = app.state::<DbConnection>();
let permissions =
get_extension_permissions(db_state, extension_id, "database", "write").await?;
// Prüfen, ob die Tabelle in den Berechtigungen enthalten ist
let has_permission = permissions
.iter()
.any(|perm| perm.path.contains(&table_name));
if !has_permission {
return Err(format!(
"Keine Schreibberechtigung für Tabelle {}",
table_name
));
}
}
sqlparser::ast::Statement::Delete(delete) => {
let from_tables = match delete.from {
sqlparser::ast::FromTable::WithFromKeyword(tables) => tables,
sqlparser::ast::FromTable::WithoutKeyword(tables) => tables,
};
if from_tables.is_empty() && delete.tables.is_empty() {
return Err("Keine Tabelle in DELETE angegeben".into());
}
let table_name = if !from_tables.is_empty() {
from_tables[0].relation.to_string()
} else {
delete.tables[0].to_string()
};
// Berechtigungen aus der Datenbank abrufen
let db_state = app.state::<DbConnection>();
let permissions =
get_extension_permissions(db_state, extension_id, "database", "write").await?;
// Prüfen, ob die Tabelle in den Berechtigungen enthalten ist
let has_permission = permissions
.iter()
.any(|perm| perm.path.contains(&table_name));
if !has_permission {
return Err(format!(
"Keine Schreibberechtigung für Tabelle {}",
table_name
));
}
}
sqlparser::ast::Statement::CreateTable(create_table) => {
let table_name = create_table.name.to_string();
// Berechtigungen aus der Datenbank abrufen
let db_state = app.state::<DbConnection>();
let permissions =
get_extension_permissions(db_state, extension_id, "database", "create").await?;
// Prüfen, ob die Tabelle in den Berechtigungen enthalten ist
let has_permission = permissions
.iter()
.any(|perm| perm.path.contains(&table_name));
if !has_permission {
return Err(format!(
"Keine Erstellungsberechtigung für Tabelle {}",
table_name
));
}
}
_ => return Err("Nur Schreiboperationen erlaubt (nutze 'select' für Abfragen)".into()),
}
Ok(())
}
/// Ruft die Berechtigungen einer Extension aus der Datenbank ab
async fn get_extension_permissions(
db_state: tauri::State<'_, DbConnection>,
extension_id: &str,
resource: &str,
operation: &str,
) -> Result<Vec<DbExtensionPermission>, String> {
let db = 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(
"SELECT id, extension_id, resource, operation, path
FROM haex_vault_extension_permissions
WHERE extension_id = ? AND resource = ? AND operation = ?",
)
.map_err(|e| format!("SQL-Vorbereitungsfehler: {}", e))?;
let rows = stmt
.query_map(&[extension_id, resource, operation], |row| {
Ok(DbExtensionPermission {
id: row.get(0)?,
extension_id: row.get(1)?,
resource: row.get(2)?,
operation: row.get(3)?,
path: row.get(4)?,
})
})
.map_err(|e| format!("SQL-Abfragefehler: {}", e))?;
let mut permissions = Vec::new();
for row in rows {
permissions.push(row.map_err(|e| format!("Fehler beim Lesen der Berechtigungen: {}", e))?);
}
Ok(permissions)
}

View File

@ -0,0 +1 @@
pub mod database;

32
src-tauri/src/lib.rs Normal file
View File

@ -0,0 +1,32 @@
mod browser;
mod database;
mod extension;
mod models;
use database::DbConnection;
//use models::ExtensionState;
use std::sync::Mutex;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.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_opener::init())
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_store::Builder::new().build())
.invoke_handler(tauri::generate_handler![
database::create_encrypted_database,
database::open_encrypted_database,
database::sql_execute,
database::sql_select,
extension::database::extension_sql_execute,
extension::database::extension_sql_select,
browser::create_tab
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

6
src-tauri/src/main.rs Normal file
View File

@ -0,0 +1,6 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
haex_hub_lib::run()
}

50
src-tauri/src/models.rs Normal file
View File

@ -0,0 +1,50 @@
// models.rs
use serde::{Deserialize, Serialize};
//use std::sync::Mutex;
#[derive(Serialize, Deserialize, Clone)]
pub struct ExtensionManifest {
pub name: String,
pub entry: String,
pub permissions: ExtensionPermissions,
}
#[derive(Serialize, Deserialize, Clone)]
pub struct ExtensionPermissions {
pub database: Option<DatabasePermissions>,
pub http: Option<Vec<String>>,
pub filesystem: Option<String>,
}
#[derive(Serialize, Deserialize, Clone)]
pub struct DatabasePermissions {
pub read: Option<Vec<String>>,
pub write: Option<Vec<String>>,
pub create: Option<Vec<String>>,
}
/* #[derive(Default)]
pub struct ExtensionState {
pub extensions: Mutex<std::collections::HashMap<String, ExtensionManifest>>,
}
impl ExtensionState {
pub fn add_extension(&self, path: String, manifest: ExtensionManifest) {
let mut extensions = self.extensions.lock().unwrap();
extensions.insert(path, manifest);
}
pub fn get_extension(&self, addon_id: &str) -> Option<ExtensionManifest> {
let extensions = self.extensions.lock().unwrap();
extensions.values().find(|p| p.name == addon_id).cloned()
}
} */
#[derive(Debug, Serialize, Deserialize)]
pub struct DbExtensionPermission {
pub id: String,
pub extension_id: String,
pub resource: String,
pub operation: String,
pub path: String,
}

38
src-tauri/tauri.conf.json Normal file
View File

@ -0,0 +1,38 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "haex-hub",
"version": "0.1.0",
"identifier": "space.haex.hub",
"build": {
"beforeDevCommand": "pnpm dev",
"devUrl": "http://localhost:3001",
"beforeBuildCommand": "pnpm generate",
"frontendDist": "../dist"
},
"app": {
"windows": [
{
"title": "haex-hub",
"width": 800,
"height": 600
}
],
"security": {
"csp": null
}
},
"bundle": {
"active": true,
"targets": "all",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"resources": {
"database/vault.db": "resources/vault.db"
}
}
}