mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-16 14:10:52 +01:00
add more typesafty
This commit is contained in:
@ -16,9 +16,9 @@ crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
|
||||
[build-dependencies]
|
||||
serde_json = "1.0.145"
|
||||
|
||||
tauri-build = { version = "2.2", features = [] }
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
|
||||
[dependencies]
|
||||
rusqlite = { version = "0.37.0", features = [
|
||||
"load_extension",
|
||||
|
||||
@ -1,106 +1,7 @@
|
||||
use serde::Deserialize;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, Write};
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Schema {
|
||||
haex: Haex,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Haex {
|
||||
settings: String,
|
||||
extensions: String,
|
||||
extension_permissions: String,
|
||||
notifications: String,
|
||||
passwords: Passwords,
|
||||
crdt: Crdt,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Passwords {
|
||||
groups: String,
|
||||
group_items: String,
|
||||
item_details: String,
|
||||
item_key_values: String,
|
||||
item_histories: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Crdt {
|
||||
logs: String,
|
||||
snapshots: String,
|
||||
configs: String,
|
||||
}
|
||||
mod generator;
|
||||
|
||||
fn main() {
|
||||
// Pfad zur Eingabe-JSON und zur Ausgabe-Rust-Datei festlegen.
|
||||
// `OUT_DIR` ist ein spezielles Verzeichnis, das Cargo für generierte Dateien bereitstellt.
|
||||
let schema_path = Path::new("database/tableNames.json");
|
||||
let out_dir =
|
||||
env::var("OUT_DIR").expect("OUT_DIR ist nicht gesetzt. Führen Sie dies mit Cargo aus.");
|
||||
let dest_path = Path::new(&out_dir).join("tableNames.rs");
|
||||
|
||||
// --- 2. JSON-Datei lesen und mit serde parsen ---
|
||||
let file = File::open(&schema_path).expect("Konnte tableNames.json nicht öffnen");
|
||||
let reader = BufReader::new(file);
|
||||
let schema: Schema =
|
||||
serde_json::from_reader(reader).expect("Konnte tableNames.json nicht parsen");
|
||||
let haex = schema.haex;
|
||||
|
||||
// --- 3. Den zu generierenden Rust-Code als String erstellen ---
|
||||
// Wir verwenden das `format!`-Makro, um die Werte aus den geparsten Structs
|
||||
// in einen vordefinierten Code-Template-String einzufügen.
|
||||
// Das `r#""#`-Format erlaubt uns, mehrzeilige Strings mit Anführungszeichen zu verwenden.
|
||||
let code = format!(
|
||||
r#"
|
||||
// HINWEIS: Diese Datei wurde automatisch von build.rs generiert.
|
||||
// Manuelle Änderungen werden bei der nächsten Kompilierung überschrieben!
|
||||
|
||||
pub const TABLE_SETTINGS: &str = "{settings}";
|
||||
pub const TABLE_EXTENSIONS: &str = "{extensions}";
|
||||
pub const TABLE_EXTENSION_PERMISSIONS: &str = "{extension_permissions}";
|
||||
pub const TABLE_NOTIFICATIONS: &str = "{notifications}";
|
||||
|
||||
// Passwords
|
||||
pub const TABLE_PASSWORDS_GROUPS: &str = "{pw_groups}";
|
||||
pub const TABLE_PASSWORDS_GROUP_ITEMS: &str = "{pw_group_items}";
|
||||
pub const TABLE_PASSWORDS_ITEM_DETAILS: &str = "{pw_item_details}";
|
||||
pub const TABLE_PASSWORDS_ITEM_KEY_VALUES: &str = "{pw_item_key_values}";
|
||||
pub const TABLE_PASSWORDS_ITEM_HISTORIES: &str = "{pw_item_histories}";
|
||||
|
||||
// CRDT
|
||||
pub const TABLE_CRDT_LOGS: &str = "{crdt_logs}";
|
||||
pub const TABLE_CRDT_SNAPSHOTS: &str = "{crdt_snapshots}";
|
||||
pub const TABLE_CRDT_CONFIGS: &str = "{crdt_configs}";
|
||||
|
||||
"#,
|
||||
// Hier werden die Werte aus dem `haex`-Struct in die Platzhalter oben eingesetzt.
|
||||
settings = haex.settings,
|
||||
extensions = haex.extensions,
|
||||
extension_permissions = haex.extension_permissions,
|
||||
notifications = haex.notifications,
|
||||
pw_groups = haex.passwords.groups,
|
||||
pw_group_items = haex.passwords.group_items,
|
||||
pw_item_details = haex.passwords.item_details,
|
||||
pw_item_key_values = haex.passwords.item_key_values,
|
||||
pw_item_histories = haex.passwords.item_histories,
|
||||
crdt_logs = haex.crdt.logs,
|
||||
crdt_snapshots = haex.crdt.snapshots,
|
||||
crdt_configs = haex.crdt.configs
|
||||
);
|
||||
|
||||
// --- 4. Den generierten Code in die Zieldatei schreiben ---
|
||||
let mut f = File::create(&dest_path).expect("Konnte die Zieldatei nicht erstellen");
|
||||
f.write_all(code.as_bytes())
|
||||
.expect("Konnte nicht in die Zieldatei schreiben");
|
||||
|
||||
// --- 5. Cargo anweisen, das Skript erneut auszuführen, wenn sich die JSON-Datei ändert ---
|
||||
// Diese Zeile ist extrem wichtig für eine reibungslose Entwicklung! Ohne sie
|
||||
// würde Cargo Änderungen an der JSON-Datei nicht bemerken.
|
||||
println!("cargo:rerun-if-changed=database/tableNames.json");
|
||||
|
||||
tauri_build::build()
|
||||
generator::table_names::generate_table_names();
|
||||
generator::rust_types::generate_rust_types();
|
||||
tauri_build::build();
|
||||
}
|
||||
|
||||
@ -23,23 +23,16 @@ function drizzleToRustType(colDef: AnySQLiteColumn): {
|
||||
let isOptional = !colDef.notNull
|
||||
|
||||
if (colDef.columnType === 'SQLiteText') {
|
||||
// Das 'mode' Property ist ebenfalls direkt auf dem Objekt verfügbar
|
||||
if ('mode' in colDef && colDef.mode === 'json') {
|
||||
baseType = 'serde_json::Value'
|
||||
} else {
|
||||
baseType = 'String'
|
||||
}
|
||||
} else if (colDef.columnType === 'SQLiteInteger') {
|
||||
if ('mode' in colDef && colDef.mode === 'boolean') {
|
||||
baseType = 'bool'
|
||||
} else if ('mode' in colDef && colDef.mode === 'timestamp') {
|
||||
baseType = 'i64'
|
||||
} else {
|
||||
baseType = 'i64'
|
||||
}
|
||||
}
|
||||
// ... weitere SQLite-Typen hier ...
|
||||
else if (colDef.columnType === 'SQLiteReal') {
|
||||
baseType = 'i64'
|
||||
} else if (colDef.columnType === 'SQLiteBoolean') {
|
||||
baseType = 'bool'
|
||||
} else if (colDef.columnType === 'SQLiteReal') {
|
||||
baseType = 'f64'
|
||||
} else if (colDef.columnType === 'SQLiteBlob') {
|
||||
baseType = 'Vec<u8>'
|
||||
@ -81,6 +74,7 @@ function toSnakeCase(str: string): string {
|
||||
}
|
||||
|
||||
function toPascalCase(str: string): string {
|
||||
console.log('toPascalCase:', str)
|
||||
return str
|
||||
.split('_')
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
||||
@ -164,15 +158,18 @@ use serde::{Deserialize, Serialize};
|
||||
`
|
||||
|
||||
const schemas = [
|
||||
{ name: tablesNames.haex.settings, table: schema.haexSettings },
|
||||
{ name: tablesNames.haex.extensions, table: schema.haexExtensions },
|
||||
{ name: tablesNames.haex.settings.name, table: schema.haexSettings },
|
||||
{ name: tablesNames.haex.extensions.name, table: schema.haexExtensions },
|
||||
{
|
||||
name: tablesNames.haex.extension_permissions,
|
||||
name: tablesNames.haex.extension_permissions.name,
|
||||
table: schema.haexExtensionPermissions,
|
||||
},
|
||||
{ name: tablesNames.haex.crdt.logs, table: schema.haexCrdtLogs },
|
||||
{ name: tablesNames.haex.crdt.snapshots, table: schema.haexCrdtSnapshots },
|
||||
{ name: tablesNames.haex.crdt.configs, table: schema.haexCrdtConfigs },
|
||||
{ name: tablesNames.haex.crdt.logs.name, table: schema.haexCrdtLogs },
|
||||
{
|
||||
name: tablesNames.haex.crdt.snapshots.name,
|
||||
table: schema.haexCrdtSnapshots,
|
||||
},
|
||||
{ name: tablesNames.haex.crdt.configs.name, table: schema.haexCrdtConfigs },
|
||||
]
|
||||
|
||||
for (const { name, table } of schemas) {
|
||||
|
||||
@ -0,0 +1 @@
|
||||
ALTER TABLE `haex_notifications` ADD `haex_timestamp` text;
|
||||
926
src-tauri/database/migrations/meta/0001_snapshot.json
Normal file
926
src-tauri/database/migrations/meta/0001_snapshot.json
Normal file
@ -0,0 +1,926 @@
|
||||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "862ac1d5-3065-4244-8652-2b6782254862",
|
||||
"prevId": "3bbe52b8-5933-4b21-8b24-de3927a2f9b0",
|
||||
"tables": {
|
||||
"haex_crdt_configs": {
|
||||
"name": "haex_crdt_configs",
|
||||
"columns": {
|
||||
"key": {
|
||||
"name": "key",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"value": {
|
||||
"name": "value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_crdt_logs": {
|
||||
"name": "haex_crdt_logs",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"haex_timestamp": {
|
||||
"name": "haex_timestamp",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"table_name": {
|
||||
"name": "table_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"row_pks": {
|
||||
"name": "row_pks",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"op_type": {
|
||||
"name": "op_type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"column_name": {
|
||||
"name": "column_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"new_value": {
|
||||
"name": "new_value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"old_value": {
|
||||
"name": "old_value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"idx_haex_timestamp": {
|
||||
"name": "idx_haex_timestamp",
|
||||
"columns": [
|
||||
"haex_timestamp"
|
||||
],
|
||||
"isUnique": false
|
||||
},
|
||||
"idx_table_row": {
|
||||
"name": "idx_table_row",
|
||||
"columns": [
|
||||
"table_name",
|
||||
"row_pks"
|
||||
],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_crdt_snapshots": {
|
||||
"name": "haex_crdt_snapshots",
|
||||
"columns": {
|
||||
"snapshot_id": {
|
||||
"name": "snapshot_id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created": {
|
||||
"name": "created",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"epoch_hlc": {
|
||||
"name": "epoch_hlc",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"location_url": {
|
||||
"name": "location_url",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"file_size_bytes": {
|
||||
"name": "file_size_bytes",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_extension_permissions": {
|
||||
"name": "haex_extension_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_type": {
|
||||
"name": "resource_type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"action": {
|
||||
"name": "action",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"target": {
|
||||
"name": "target",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"constraints": {
|
||||
"name": "constraints",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"status": {
|
||||
"name": "status",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'denied'"
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": "(CURRENT_TIMESTAMP)"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"haex_tombstone": {
|
||||
"name": "haex_tombstone",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"haex_timestamp": {
|
||||
"name": "haex_timestamp",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"haex_extension_permissions_extension_id_resource_type_action_target_unique": {
|
||||
"name": "haex_extension_permissions_extension_id_resource_type_action_target_unique",
|
||||
"columns": [
|
||||
"extension_id",
|
||||
"resource_type",
|
||||
"action",
|
||||
"target"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"haex_extension_permissions_extension_id_haex_extensions_id_fk": {
|
||||
"name": "haex_extension_permissions_extension_id_haex_extensions_id_fk",
|
||||
"tableFrom": "haex_extension_permissions",
|
||||
"tableTo": "haex_extensions",
|
||||
"columnsFrom": [
|
||||
"extension_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"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
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"entry": {
|
||||
"name": "entry",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"homepage": {
|
||||
"name": "homepage",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"enabled": {
|
||||
"name": "enabled",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"icon": {
|
||||
"name": "icon",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"public_key": {
|
||||
"name": "public_key",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"signature": {
|
||||
"name": "signature",
|
||||
"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
|
||||
},
|
||||
"haex_tombstone": {
|
||||
"name": "haex_tombstone",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"haex_timestamp": {
|
||||
"name": "haex_timestamp",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_notifications": {
|
||||
"name": "haex_notifications",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"alt": {
|
||||
"name": "alt",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"date": {
|
||||
"name": "date",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"icon": {
|
||||
"name": "icon",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"image": {
|
||||
"name": "image",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"read": {
|
||||
"name": "read",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"source": {
|
||||
"name": "source",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"text": {
|
||||
"name": "text",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"title": {
|
||||
"name": "title",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"type": {
|
||||
"name": "type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"haex_tombstone": {
|
||||
"name": "haex_tombstone",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"haex_timestamp": {
|
||||
"name": "haex_timestamp",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"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
|
||||
},
|
||||
"type": {
|
||||
"name": "type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"value": {
|
||||
"name": "value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"haex_tombstone": {
|
||||
"name": "haex_tombstone",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"haex_timestamp": {
|
||||
"name": "haex_timestamp",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_passwords_group_items": {
|
||||
"name": "haex_passwords_group_items",
|
||||
"columns": {
|
||||
"group_id": {
|
||||
"name": "group_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"item_id": {
|
||||
"name": "item_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"haex_tombstone": {
|
||||
"name": "haex_tombstone",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"haex_passwords_group_items_group_id_haex_passwords_groups_id_fk": {
|
||||
"name": "haex_passwords_group_items_group_id_haex_passwords_groups_id_fk",
|
||||
"tableFrom": "haex_passwords_group_items",
|
||||
"tableTo": "haex_passwords_groups",
|
||||
"columnsFrom": [
|
||||
"group_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"haex_passwords_group_items_item_id_haex_passwords_item_details_id_fk": {
|
||||
"name": "haex_passwords_group_items_item_id_haex_passwords_item_details_id_fk",
|
||||
"tableFrom": "haex_passwords_group_items",
|
||||
"tableTo": "haex_passwords_item_details",
|
||||
"columnsFrom": [
|
||||
"item_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"haex_passwords_group_items_item_id_group_id_pk": {
|
||||
"columns": [
|
||||
"item_id",
|
||||
"group_id"
|
||||
],
|
||||
"name": "haex_passwords_group_items_item_id_group_id_pk"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_passwords_groups": {
|
||||
"name": "haex_passwords_groups",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"icon": {
|
||||
"name": "icon",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"order": {
|
||||
"name": "order",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"color": {
|
||||
"name": "color",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"parent_id": {
|
||||
"name": "parent_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": "(CURRENT_TIMESTAMP)"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"haex_tombstone": {
|
||||
"name": "haex_tombstone",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"haex_passwords_groups_parent_id_haex_passwords_groups_id_fk": {
|
||||
"name": "haex_passwords_groups_parent_id_haex_passwords_groups_id_fk",
|
||||
"tableFrom": "haex_passwords_groups",
|
||||
"tableTo": "haex_passwords_groups",
|
||||
"columnsFrom": [
|
||||
"parent_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_passwords_item_details": {
|
||||
"name": "haex_passwords_item_details",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"title": {
|
||||
"name": "title",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"username": {
|
||||
"name": "username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"password": {
|
||||
"name": "password",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"note": {
|
||||
"name": "note",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"icon": {
|
||||
"name": "icon",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"tags": {
|
||||
"name": "tags",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"url": {
|
||||
"name": "url",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": "(CURRENT_TIMESTAMP)"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"haex_tombstone": {
|
||||
"name": "haex_tombstone",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_passwords_item_history": {
|
||||
"name": "haex_passwords_item_history",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"item_id": {
|
||||
"name": "item_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"changed_property": {
|
||||
"name": "changed_property",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"old_value": {
|
||||
"name": "old_value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"new_value": {
|
||||
"name": "new_value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": "(CURRENT_TIMESTAMP)"
|
||||
},
|
||||
"haex_tombstone": {
|
||||
"name": "haex_tombstone",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"haex_passwords_item_history_item_id_haex_passwords_item_details_id_fk": {
|
||||
"name": "haex_passwords_item_history_item_id_haex_passwords_item_details_id_fk",
|
||||
"tableFrom": "haex_passwords_item_history",
|
||||
"tableTo": "haex_passwords_item_details",
|
||||
"columnsFrom": [
|
||||
"item_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_passwords_item_key_values": {
|
||||
"name": "haex_passwords_item_key_values",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"item_id": {
|
||||
"name": "item_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"key": {
|
||||
"name": "key",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"value": {
|
||||
"name": "value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"haex_tombstone": {
|
||||
"name": "haex_tombstone",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"haex_passwords_item_key_values_item_id_haex_passwords_item_details_id_fk": {
|
||||
"name": "haex_passwords_item_key_values_item_id_haex_passwords_item_details_id_fk",
|
||||
"tableFrom": "haex_passwords_item_key_values",
|
||||
"tableTo": "haex_passwords_item_details",
|
||||
"columnsFrom": [
|
||||
"item_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
}
|
||||
},
|
||||
"views": {},
|
||||
"enums": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
},
|
||||
"internal": {
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,13 @@
|
||||
"when": 1759402321133,
|
||||
"tag": "0000_glamorous_hulk",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "6",
|
||||
"when": 1759418087677,
|
||||
"tag": "0001_green_stark_industries",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -2,18 +2,24 @@ import { integer, sqliteTable, text, index } from 'drizzle-orm/sqlite-core'
|
||||
import tableNames from '../tableNames.json'
|
||||
|
||||
export const haexCrdtLogs = sqliteTable(
|
||||
tableNames.haex.crdt.logs,
|
||||
tableNames.haex.crdt.logs.name,
|
||||
{
|
||||
id: text()
|
||||
.primaryKey()
|
||||
.$defaultFn(() => crypto.randomUUID()),
|
||||
haexTimestamp: text('haex_timestamp'),
|
||||
tableName: text('table_name'),
|
||||
rowPks: text('row_pks', { mode: 'json' }),
|
||||
opType: text('op_type', { enum: ['INSERT', 'UPDATE', 'DELETE'] }),
|
||||
columnName: text('column_name'),
|
||||
newValue: text('new_value', { mode: 'json' }),
|
||||
oldValue: text('old_value', { mode: 'json' }),
|
||||
haexTimestamp: text(tableNames.haex.crdt.logs.columns.haexTimestamp),
|
||||
tableName: text(tableNames.haex.crdt.logs.columns.tableName),
|
||||
rowPks: text(tableNames.haex.crdt.logs.columns.rowPks, { mode: 'json' }),
|
||||
opType: text(tableNames.haex.crdt.logs.columns.opType, {
|
||||
enum: ['INSERT', 'UPDATE', 'DELETE'],
|
||||
}),
|
||||
columnName: text(tableNames.haex.crdt.logs.columns.columnName),
|
||||
newValue: text(tableNames.haex.crdt.logs.columns.newValue, {
|
||||
mode: 'json',
|
||||
}),
|
||||
oldValue: text(tableNames.haex.crdt.logs.columns.oldValue, {
|
||||
mode: 'json',
|
||||
}),
|
||||
},
|
||||
(table) => [
|
||||
index('idx_haex_timestamp').on(table.haexTimestamp),
|
||||
@ -23,17 +29,22 @@ export const haexCrdtLogs = sqliteTable(
|
||||
export type InsertHaexCrdtLogs = typeof haexCrdtLogs.$inferInsert
|
||||
export type SelectHaexCrdtLogs = typeof haexCrdtLogs.$inferSelect
|
||||
|
||||
export const haexCrdtSnapshots = sqliteTable(tableNames.haex.crdt.snapshots, {
|
||||
snapshot_id: text()
|
||||
.primaryKey()
|
||||
.$defaultFn(() => crypto.randomUUID()),
|
||||
created: text(),
|
||||
epoch_hlc: text(),
|
||||
location_url: text(),
|
||||
file_size_bytes: integer(),
|
||||
})
|
||||
export const haexCrdtSnapshots = sqliteTable(
|
||||
tableNames.haex.crdt.snapshots.name,
|
||||
{
|
||||
snapshotId: text(tableNames.haex.crdt.snapshots.columns.snapshotId)
|
||||
.primaryKey()
|
||||
.$defaultFn(() => crypto.randomUUID()),
|
||||
created: text(),
|
||||
epochHlc: text(tableNames.haex.crdt.snapshots.columns.epochHlc),
|
||||
locationUrl: text(tableNames.haex.crdt.snapshots.columns.locationUrl),
|
||||
fileSizeBytes: integer(
|
||||
tableNames.haex.crdt.snapshots.columns.fileSizeBytes,
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
export const haexCrdtConfigs = sqliteTable(tableNames.haex.crdt.configs, {
|
||||
export const haexCrdtConfigs = sqliteTable(tableNames.haex.crdt.configs.name, {
|
||||
key: text()
|
||||
.primaryKey()
|
||||
.$defaultFn(() => crypto.randomUUID()),
|
||||
|
||||
@ -8,20 +8,22 @@ import {
|
||||
} from 'drizzle-orm/sqlite-core'
|
||||
import tableNames from '../tableNames.json'
|
||||
|
||||
export const haexSettings = sqliteTable(tableNames.haex.settings, {
|
||||
export const haexSettings = sqliteTable(tableNames.haex.settings.name, {
|
||||
id: text()
|
||||
.primaryKey()
|
||||
.$defaultFn(() => crypto.randomUUID()),
|
||||
key: text(),
|
||||
type: text(),
|
||||
value: text(),
|
||||
haexTombstone: integer('haex_tombstone', { mode: 'boolean' }),
|
||||
haexTimestamp: text('haex_timestamp'),
|
||||
haexTombstone: integer(tableNames.haex.settings.columns.haexTombstone, {
|
||||
mode: 'boolean',
|
||||
}),
|
||||
haexTimestamp: text(tableNames.haex.settings.columns.haexTimestamp),
|
||||
})
|
||||
export type InsertHaexSettings = typeof haexSettings.$inferInsert
|
||||
export type SelectHaexSettings = typeof haexSettings.$inferSelect
|
||||
|
||||
export const haexExtensions = sqliteTable(tableNames.haex.extensions, {
|
||||
export const haexExtensions = sqliteTable(tableNames.haex.extensions.name, {
|
||||
id: text()
|
||||
.primaryKey()
|
||||
.$defaultFn(() => crypto.randomUUID()),
|
||||
@ -36,21 +38,23 @@ export const haexExtensions = sqliteTable(tableNames.haex.extensions, {
|
||||
signature: text(),
|
||||
url: text(),
|
||||
version: text(),
|
||||
haexTombstone: integer('haex_tombstone', { mode: 'boolean' }),
|
||||
haexTimestamp: text('haex_timestamp'),
|
||||
haexTombstone: integer(tableNames.haex.extensions.columns.haexTombstone, {
|
||||
mode: 'boolean',
|
||||
}),
|
||||
haexTimestamp: text(tableNames.haex.extensions.columns.haexTimestamp),
|
||||
})
|
||||
export type InsertHaexExtensions = typeof haexExtensions.$inferInsert
|
||||
export type SelectHaexExtensions = typeof haexExtensions.$inferSelect
|
||||
|
||||
export const haexExtensionPermissions = sqliteTable(
|
||||
tableNames.haex.extension_permissions,
|
||||
tableNames.haex.extension_permissions.name,
|
||||
{
|
||||
id: text()
|
||||
.primaryKey()
|
||||
.$defaultFn(() => crypto.randomUUID()),
|
||||
extensionId: text('extension_id').references(
|
||||
(): AnySQLiteColumn => haexExtensions.id,
|
||||
),
|
||||
extensionId: text(
|
||||
tableNames.haex.extension_permissions.columns.extensionId,
|
||||
).references((): AnySQLiteColumn => haexExtensions.id),
|
||||
resourceType: text('resource_type', {
|
||||
enum: ['fs', 'http', 'db', 'shell'],
|
||||
}),
|
||||
@ -64,8 +68,13 @@ export const haexExtensionPermissions = sqliteTable(
|
||||
updateAt: integer('updated_at', { mode: 'timestamp' }).$onUpdate(
|
||||
() => new Date(),
|
||||
),
|
||||
haexTombstone: integer('haex_tombstone', { mode: 'boolean' }),
|
||||
haexTimestamp: text('haex_timestamp'),
|
||||
haexTombstone: integer(
|
||||
tableNames.haex.extension_permissions.columns.haexTombstone,
|
||||
{ mode: 'boolean' },
|
||||
),
|
||||
haexTimestamp: text(
|
||||
tableNames.haex.extension_permissions.columns.haexTimestamp,
|
||||
),
|
||||
},
|
||||
(table) => [
|
||||
unique().on(
|
||||
@ -80,3 +89,28 @@ export type InserthaexExtensionPermissions =
|
||||
typeof haexExtensionPermissions.$inferInsert
|
||||
export type SelecthaexExtensionPermissions =
|
||||
typeof haexExtensionPermissions.$inferSelect
|
||||
|
||||
export const haexNotifications = sqliteTable(
|
||||
tableNames.haex.notifications.name,
|
||||
{
|
||||
id: text().primaryKey(),
|
||||
alt: text(),
|
||||
date: text(),
|
||||
icon: text(),
|
||||
image: text(),
|
||||
read: integer({ mode: 'boolean' }),
|
||||
source: text(),
|
||||
text: text(),
|
||||
title: text(),
|
||||
type: text({
|
||||
enum: ['error', 'success', 'warning', 'info', 'log'],
|
||||
}).notNull(),
|
||||
haexTombstone: integer(
|
||||
tableNames.haex.notifications.columns.haexTombstone,
|
||||
{ mode: 'boolean' },
|
||||
),
|
||||
haexTimestamp: text(tableNames.haex.notifications.columns.haexTimestamp),
|
||||
},
|
||||
)
|
||||
export type InsertHaexNotifications = typeof haexNotifications.$inferInsert
|
||||
export type SelectHaexNotifications = typeof haexNotifications.$inferSelect
|
||||
|
||||
@ -8,24 +8,6 @@ import {
|
||||
} from 'drizzle-orm/sqlite-core'
|
||||
import tableNames from '../tableNames.json'
|
||||
|
||||
export const haexNotifications = sqliteTable(tableNames.haex.notifications, {
|
||||
id: text().primaryKey(),
|
||||
alt: text(),
|
||||
date: text(),
|
||||
icon: text(),
|
||||
image: text(),
|
||||
read: integer({ mode: 'boolean' }),
|
||||
source: text(),
|
||||
text: text(),
|
||||
title: text(),
|
||||
type: text({
|
||||
enum: ['error', 'success', 'warning', 'info', 'log'],
|
||||
}).notNull(),
|
||||
haex_tombstone: integer({ mode: 'boolean' }),
|
||||
})
|
||||
export type InsertHaexNotifications = typeof haexNotifications.$inferInsert
|
||||
export type SelectHaexNotifications = typeof haexNotifications.$inferSelect
|
||||
|
||||
export const haexPasswordsItemDetails = sqliteTable(
|
||||
tableNames.haex.passwords.item_details,
|
||||
{
|
||||
|
||||
@ -1,9 +1,68 @@
|
||||
{
|
||||
"haex": {
|
||||
"settings": "haex_settings",
|
||||
"extensions": "haex_extensions",
|
||||
"extension_permissions": "haex_extension_permissions",
|
||||
"notifications": "haex_notifications",
|
||||
"settings": {
|
||||
"name": "haex_settings",
|
||||
"columns": {
|
||||
"id": "id",
|
||||
"key": "key",
|
||||
"type": "type",
|
||||
"value": "value",
|
||||
"haexTombstone": "haex_tombstone",
|
||||
"haexTimestamp": "haex_timestamp"
|
||||
}
|
||||
},
|
||||
"extensions": {
|
||||
"name": "haex_extensions",
|
||||
"columns": {
|
||||
"id": "id",
|
||||
"author": "author",
|
||||
"description": "description",
|
||||
"entry": "entry",
|
||||
"homepage": "homepage",
|
||||
"enabled": "enabled",
|
||||
"icon": "icon",
|
||||
"name": "name",
|
||||
"public_key": "public_key",
|
||||
"signature": "signature",
|
||||
"url": "url",
|
||||
"version": "version",
|
||||
"haexTombstone": "haex_tombstone",
|
||||
"haexTimestamp": "haex_timestamp"
|
||||
}
|
||||
},
|
||||
"extension_permissions": {
|
||||
"name": "haex_extension_permissions",
|
||||
"columns": {
|
||||
"id": "id",
|
||||
"extensionId": "extension_id",
|
||||
"resourceType": "resource_type",
|
||||
"action": "action",
|
||||
"target": "target",
|
||||
"constraints": "constraints",
|
||||
"status": "status",
|
||||
"createdAt": "created_at",
|
||||
"updateAt": "updated_at",
|
||||
"haexTombstone": "haex_tombstone",
|
||||
"haexTimestamp": "haex_timestamp"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
"name": "haex_notifications",
|
||||
"columns": {
|
||||
"id": "id",
|
||||
"alt": "alt",
|
||||
"date": "date",
|
||||
"icon": "icon",
|
||||
"image": "image",
|
||||
"read": "read",
|
||||
"source": "source",
|
||||
"text": "text",
|
||||
"title": "title",
|
||||
"type": "type",
|
||||
"haexTombstone": "haex_tombstone",
|
||||
"haexTimestamp": "haex_timestamp"
|
||||
}
|
||||
},
|
||||
"passwords": {
|
||||
"groups": "haex_passwords_groups",
|
||||
"group_items": "haex_passwords_group_items",
|
||||
@ -12,9 +71,36 @@
|
||||
"item_histories": "haex_passwords_item_history"
|
||||
},
|
||||
"crdt": {
|
||||
"logs": "haex_crdt_logs",
|
||||
"snapshots": "haex_crdt_snapshots",
|
||||
"configs": "haex_crdt_configs"
|
||||
"logs": {
|
||||
"name": "haex_crdt_logs",
|
||||
"columns": {
|
||||
"id": "id",
|
||||
"haexTimestamp": "haex_timestamp",
|
||||
"tableName": "table_name",
|
||||
"rowPks": "row_pks",
|
||||
"opType": "op_type",
|
||||
"columnName": "column_name",
|
||||
"newValue": "new_value",
|
||||
"oldValue": "old_value"
|
||||
}
|
||||
},
|
||||
"snapshots": {
|
||||
"name": "haex_crdt_snapshots",
|
||||
"columns": {
|
||||
"snapshotId": "snapshot_id",
|
||||
"created": "created",
|
||||
"epochHlc": "epoch_hlc",
|
||||
"locationUrl": "location_url",
|
||||
"fileSizeBytes": "file_size_bytes"
|
||||
}
|
||||
},
|
||||
"configs": {
|
||||
"name": "haex_crdt_configs",
|
||||
"columns": {
|
||||
"key": "key",
|
||||
"value": "value"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
// src-tauri/src/build/mod.rs
|
||||
// build/mod.rs
|
||||
pub mod rust_types;
|
||||
pub mod table_names;
|
||||
213
src-tauri/generator/table_names.rs
Normal file
213
src-tauri/generator/table_names.rs
Normal file
@ -0,0 +1,213 @@
|
||||
// src-tarui/src/build/table_names.rs
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, Write};
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Schema {
|
||||
haex: Haex,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[allow(non_snake_case)]
|
||||
struct Haex {
|
||||
settings: TableDefinition,
|
||||
extensions: TableDefinition,
|
||||
extension_permissions: TableDefinition,
|
||||
notifications: TableDefinition,
|
||||
crdt: Crdt,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Crdt {
|
||||
logs: TableDefinition,
|
||||
snapshots: TableDefinition,
|
||||
configs: TableDefinition,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct TableDefinition {
|
||||
name: String,
|
||||
columns: HashMap<String, String>,
|
||||
}
|
||||
|
||||
pub fn generate_table_names() {
|
||||
let out_dir = env::var("OUT_DIR").expect("OUT_DIR ist nicht gesetzt.");
|
||||
println!("Generiere Tabellennamen nach {}", out_dir);
|
||||
let schema_path = Path::new("database/tableNames.json");
|
||||
let dest_path = Path::new(&out_dir).join("tableNames.rs");
|
||||
|
||||
let file = File::open(&schema_path).expect("Konnte tableNames.json nicht öffnen");
|
||||
let reader = BufReader::new(file);
|
||||
let schema: Schema =
|
||||
serde_json::from_reader(reader).expect("Konnte tableNames.json nicht parsen");
|
||||
let haex = schema.haex;
|
||||
|
||||
let code = format!(
|
||||
r#"
|
||||
// ==================================================================
|
||||
// HINWEIS: Diese Datei wurde automatisch von build.rs generiert.
|
||||
// Manuelle Änderungen werden bei der nächsten Kompilierung überschrieben!
|
||||
// ==================================================================
|
||||
|
||||
// --- Table: haex_settings ---
|
||||
pub const TABLE_SETTINGS: &str = "{t_settings}";
|
||||
pub const COL_SETTINGS_ID: &str = "{c_settings_id}";
|
||||
pub const COL_SETTINGS_KEY: &str = "{c_settings_key}";
|
||||
pub const COL_SETTINGS_TYPE: &str = "{c_settings_type}";
|
||||
pub const COL_SETTINGS_VALUE: &str = "{c_settings_value}";
|
||||
pub const COL_SETTINGS_HAEX_TOMBSTONE: &str = "{c_settings_tombstone}";
|
||||
pub const COL_SETTINGS_HAEX_TIMESTAMP: &str = "{c_settings_timestamp}";
|
||||
|
||||
// --- Table: haex_extensions ---
|
||||
pub const TABLE_EXTENSIONS: &str = "{t_extensions}";
|
||||
pub const COL_EXTENSIONS_ID: &str = "{c_ext_id}";
|
||||
pub const COL_EXTENSIONS_AUTHOR: &str = "{c_ext_author}";
|
||||
pub const COL_EXTENSIONS_DESCRIPTION: &str = "{c_ext_description}";
|
||||
pub const COL_EXTENSIONS_ENTRY: &str = "{c_ext_entry}";
|
||||
pub const COL_EXTENSIONS_HOMEPAGE: &str = "{c_ext_homepage}";
|
||||
pub const COL_EXTENSIONS_ENABLED: &str = "{c_ext_enabled}";
|
||||
pub const COL_EXTENSIONS_ICON: &str = "{c_ext_icon}";
|
||||
pub const COL_EXTENSIONS_NAME: &str = "{c_ext_name}";
|
||||
pub const COL_EXTENSIONS_PUBLIC_KEY: &str = "{c_ext_public_key}";
|
||||
pub const COL_EXTENSIONS_SIGNATURE: &str = "{c_ext_signature}";
|
||||
pub const COL_EXTENSIONS_URL: &str = "{c_ext_url}";
|
||||
pub const COL_EXTENSIONS_VERSION: &str = "{c_ext_version}";
|
||||
pub const COL_EXTENSIONS_HAEX_TOMBSTONE: &str = "{c_ext_tombstone}";
|
||||
pub const COL_EXTENSIONS_HAEX_TIMESTAMP: &str = "{c_ext_timestamp}";
|
||||
|
||||
// --- Table: haex_extension_permissions ---
|
||||
pub const TABLE_EXTENSION_PERMISSIONS: &str = "{t_ext_perms}";
|
||||
pub const COL_EXT_PERMS_ID: &str = "{c_extp_id}";
|
||||
pub const COL_EXT_PERMS_EXTENSION_ID: &str = "{c_extp_extensionId}";
|
||||
pub const COL_EXT_PERMS_RESOURCE_TYPE: &str = "{c_extp_resourceType}";
|
||||
pub const COL_EXT_PERMS_ACTION: &str = "{c_extp_action}";
|
||||
pub const COL_EXT_PERMS_TARGET: &str = "{c_extp_target}";
|
||||
pub const COL_EXT_PERMS_CONSTRAINTS: &str = "{c_extp_constraints}";
|
||||
pub const COL_EXT_PERMS_STATUS: &str = "{c_extp_status}";
|
||||
pub const COL_EXT_PERMS_CREATED_AT: &str = "{c_extp_createdAt}";
|
||||
pub const COL_EXT_PERMS_UPDATE_AT: &str = "{c_extp_updateAt}";
|
||||
pub const COL_EXT_PERMS_HAEX_TOMBSTONE: &str = "{c_extp_tombstone}";
|
||||
pub const COL_EXT_PERMS_HAEX_TIMESTAMP: &str = "{c_extp_timestamp}";
|
||||
|
||||
// --- Table: haex_notifications ---
|
||||
pub const TABLE_NOTIFICATIONS: &str = "{t_notifications}";
|
||||
pub const COL_NOTIFICATIONS_ID: &str = "{c_notif_id}";
|
||||
pub const COL_NOTIFICATIONS_ALT: &str = "{c_notif_alt}";
|
||||
pub const COL_NOTIFICATIONS_DATE: &str = "{c_notif_date}";
|
||||
pub const COL_NOTIFICATIONS_ICON: &str = "{c_notif_icon}";
|
||||
pub const COL_NOTIFICATIONS_IMAGE: &str = "{c_notif_image}";
|
||||
pub const COL_NOTIFICATIONS_READ: &str = "{c_notif_read}";
|
||||
pub const COL_NOTIFICATIONS_SOURCE: &str = "{c_notif_source}";
|
||||
pub const COL_NOTIFICATIONS_TEXT: &str = "{c_notif_text}";
|
||||
pub const COL_NOTIFICATIONS_TITLE: &str = "{c_notif_title}";
|
||||
pub const COL_NOTIFICATIONS_TYPE: &str = "{c_notif_type}";
|
||||
pub const COL_NOTIFICATIONS_HAEX_TOMBSTONE: &str = "{c_notif_tombstone}";
|
||||
|
||||
// --- Table: haex_crdt_logs ---
|
||||
pub const TABLE_CRDT_LOGS: &str = "{t_crdt_logs}";
|
||||
pub const COL_CRDT_LOGS_ID: &str = "{c_crdt_logs_id}";
|
||||
pub const COL_CRDT_LOGS_HAEX_TIMESTAMP: &str = "{c_crdt_logs_timestamp}";
|
||||
pub const COL_CRDT_LOGS_TABLE_NAME: &str = "{c_crdt_logs_tableName}";
|
||||
pub const COL_CRDT_LOGS_ROW_PKS: &str = "{c_crdt_logs_rowPks}";
|
||||
pub const COL_CRDT_LOGS_OP_TYPE: &str = "{c_crdt_logs_opType}";
|
||||
pub const COL_CRDT_LOGS_COLUMN_NAME: &str = "{c_crdt_logs_columnName}";
|
||||
pub const COL_CRDT_LOGS_NEW_VALUE: &str = "{c_crdt_logs_newValue}";
|
||||
pub const COL_CRDT_LOGS_OLD_VALUE: &str = "{c_crdt_logs_oldValue}";
|
||||
|
||||
// --- Table: haex_crdt_snapshots ---
|
||||
pub const TABLE_CRDT_SNAPSHOTS: &str = "{t_crdt_snapshots}";
|
||||
pub const COL_CRDT_SNAPSHOTS_ID: &str = "{c_crdt_snap_id}";
|
||||
pub const COL_CRDT_SNAPSHOTS_CREATED: &str = "{c_crdt_snap_created}";
|
||||
pub const COL_CRDT_SNAPSHOTS_EPOCH_HLC: &str = "{c_crdt_snap_epoch}";
|
||||
pub const COL_CRDT_SNAPSHOTS_LOCATION_URL: &str = "{c_crdt_snap_location}";
|
||||
pub const COL_CRDT_SNAPSHOTS_FILE_SIZE: &str = "{c_crdt_snap_size}";
|
||||
|
||||
// --- Table: haex_crdt_configs ---
|
||||
pub const TABLE_CRDT_CONFIGS: &str = "{t_crdt_configs}";
|
||||
pub const COL_CRDT_CONFIGS_KEY: &str = "{c_crdt_configs_key}";
|
||||
pub const COL_CRDT_CONFIGS_VALUE: &str = "{c_crdt_configs_value}";
|
||||
"#,
|
||||
// Settings
|
||||
t_settings = haex.settings.name,
|
||||
c_settings_id = haex.settings.columns["id"],
|
||||
c_settings_key = haex.settings.columns["key"],
|
||||
c_settings_type = haex.settings.columns["type"],
|
||||
c_settings_value = haex.settings.columns["value"],
|
||||
c_settings_tombstone = haex.settings.columns["haexTombstone"],
|
||||
c_settings_timestamp = haex.settings.columns["haexTimestamp"],
|
||||
// Extensions
|
||||
t_extensions = haex.extensions.name,
|
||||
c_ext_id = haex.extensions.columns["id"],
|
||||
c_ext_author = haex.extensions.columns["author"],
|
||||
c_ext_description = haex.extensions.columns["description"],
|
||||
c_ext_entry = haex.extensions.columns["entry"],
|
||||
c_ext_homepage = haex.extensions.columns["homepage"],
|
||||
c_ext_enabled = haex.extensions.columns["enabled"],
|
||||
c_ext_icon = haex.extensions.columns["icon"],
|
||||
c_ext_name = haex.extensions.columns["name"],
|
||||
c_ext_public_key = haex.extensions.columns["public_key"],
|
||||
c_ext_signature = haex.extensions.columns["signature"],
|
||||
c_ext_url = haex.extensions.columns["url"],
|
||||
c_ext_version = haex.extensions.columns["version"],
|
||||
c_ext_tombstone = haex.extensions.columns["haexTombstone"],
|
||||
c_ext_timestamp = haex.extensions.columns["haexTimestamp"],
|
||||
// Extension Permissions
|
||||
t_ext_perms = haex.extension_permissions.name,
|
||||
c_extp_id = haex.extension_permissions.columns["id"],
|
||||
c_extp_extensionId = haex.extension_permissions.columns["extensionId"],
|
||||
c_extp_resourceType = haex.extension_permissions.columns["resourceType"],
|
||||
c_extp_action = haex.extension_permissions.columns["action"],
|
||||
c_extp_target = haex.extension_permissions.columns["target"],
|
||||
c_extp_constraints = haex.extension_permissions.columns["constraints"],
|
||||
c_extp_status = haex.extension_permissions.columns["status"],
|
||||
c_extp_createdAt = haex.extension_permissions.columns["createdAt"],
|
||||
c_extp_updateAt = haex.extension_permissions.columns["updateAt"],
|
||||
c_extp_tombstone = haex.extension_permissions.columns["haexTombstone"],
|
||||
c_extp_timestamp = haex.extension_permissions.columns["haexTimestamp"],
|
||||
// Notifications
|
||||
t_notifications = haex.notifications.name,
|
||||
c_notif_id = haex.notifications.columns["id"],
|
||||
c_notif_alt = haex.notifications.columns["alt"],
|
||||
c_notif_date = haex.notifications.columns["date"],
|
||||
c_notif_icon = haex.notifications.columns["icon"],
|
||||
c_notif_image = haex.notifications.columns["image"],
|
||||
c_notif_read = haex.notifications.columns["read"],
|
||||
c_notif_source = haex.notifications.columns["source"],
|
||||
c_notif_text = haex.notifications.columns["text"],
|
||||
c_notif_title = haex.notifications.columns["title"],
|
||||
c_notif_type = haex.notifications.columns["type"],
|
||||
c_notif_tombstone = haex.notifications.columns["haexTombstone"],
|
||||
// CRDT Logs
|
||||
t_crdt_logs = haex.crdt.logs.name,
|
||||
c_crdt_logs_id = haex.crdt.logs.columns["id"],
|
||||
c_crdt_logs_timestamp = haex.crdt.logs.columns["haexTimestamp"],
|
||||
c_crdt_logs_tableName = haex.crdt.logs.columns["tableName"],
|
||||
c_crdt_logs_rowPks = haex.crdt.logs.columns["rowPks"],
|
||||
c_crdt_logs_opType = haex.crdt.logs.columns["opType"],
|
||||
c_crdt_logs_columnName = haex.crdt.logs.columns["columnName"],
|
||||
c_crdt_logs_newValue = haex.crdt.logs.columns["newValue"],
|
||||
c_crdt_logs_oldValue = haex.crdt.logs.columns["oldValue"],
|
||||
// CRDT Snapshots
|
||||
t_crdt_snapshots = haex.crdt.snapshots.name,
|
||||
c_crdt_snap_id = haex.crdt.snapshots.columns["snapshotId"],
|
||||
c_crdt_snap_created = haex.crdt.snapshots.columns["created"],
|
||||
c_crdt_snap_epoch = haex.crdt.snapshots.columns["epochHlc"],
|
||||
c_crdt_snap_location = haex.crdt.snapshots.columns["locationUrl"],
|
||||
c_crdt_snap_size = haex.crdt.snapshots.columns["fileSizeBytes"],
|
||||
// CRDT Configs
|
||||
t_crdt_configs = haex.crdt.configs.name,
|
||||
c_crdt_configs_key = haex.crdt.configs.columns["key"],
|
||||
c_crdt_configs_value = haex.crdt.configs.columns["value"]
|
||||
);
|
||||
|
||||
// --- Datei schreiben ---
|
||||
let mut f = File::create(&dest_path).expect("Konnte Zieldatei nicht erstellen");
|
||||
f.write_all(code.as_bytes())
|
||||
.expect("Konnte nicht in Zieldatei schreiben");
|
||||
|
||||
println!("cargo:rerun-if-changed=database/tableNames.json");
|
||||
}
|
||||
@ -1,64 +0,0 @@
|
||||
// build/table_names.rs
|
||||
use serde::Deserialize;
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, Write};
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Schema {
|
||||
pub haex: Haex,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Haex {
|
||||
pub settings: String,
|
||||
pub extensions: String,
|
||||
pub extension_permissions: String,
|
||||
pub crdt: Crdt,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Crdt {
|
||||
pub logs: String,
|
||||
pub snapshots: String,
|
||||
pub configs: String,
|
||||
}
|
||||
|
||||
pub fn generate_table_names(out_dir: &str) {
|
||||
let schema_path = Path::new("database/tableNames.json");
|
||||
let dest_path = Path::new(out_dir).join("tableNames.rs");
|
||||
|
||||
let file = File::open(&schema_path).expect("Konnte tableNames.json nicht öffnen");
|
||||
let reader = BufReader::new(file);
|
||||
let schema: Schema =
|
||||
serde_json::from_reader(reader).expect("Konnte tableNames.json nicht parsen");
|
||||
let haex = schema.haex;
|
||||
|
||||
let code = format!(
|
||||
r#"
|
||||
// Auto-generated - DO NOT EDIT
|
||||
|
||||
// Core Tables
|
||||
pub const TABLE_SETTINGS: &str = "{settings}";
|
||||
pub const TABLE_EXTENSIONS: &str = "{extensions}";
|
||||
pub const TABLE_EXTENSION_PERMISSIONS: &str = "{extension_permissions}";
|
||||
|
||||
// CRDT Tables
|
||||
pub const TABLE_CRDT_LOGS: &str = "{crdt_logs}";
|
||||
pub const TABLE_CRDT_SNAPSHOTS: &str = "{crdt_snapshots}";
|
||||
pub const TABLE_CRDT_CONFIGS: &str = "{crdt_configs}";
|
||||
"#,
|
||||
settings = haex.settings,
|
||||
extensions = haex.extensions,
|
||||
extension_permissions = haex.extension_permissions,
|
||||
crdt_logs = haex.crdt.logs,
|
||||
crdt_snapshots = haex.crdt.snapshots,
|
||||
crdt_configs = haex.crdt.configs
|
||||
);
|
||||
|
||||
let mut f = File::create(&dest_path).expect("Konnte Zieldatei nicht erstellen");
|
||||
f.write_all(code.as_bytes())
|
||||
.expect("Konnte nicht in Zieldatei schreiben");
|
||||
|
||||
println!("cargo:rerun-if-changed=database/tableNames.json");
|
||||
}
|
||||
@ -259,9 +259,10 @@ fn generate_insert_trigger_sql(table_name: &str, pks: &[String], cols: &[String]
|
||||
let column_inserts = if cols.is_empty() {
|
||||
// Nur PKs -> einfacher Insert ins Log
|
||||
format!(
|
||||
"INSERT INTO {log_table} (hlc_timestamp, op_type, table_name, row_pks)
|
||||
VALUES (hlc_new_timestamp(), 'INSERT', '{table}', json_object({pk_payload}));",
|
||||
"INSERT INTO {log_table} (haex_timestamp, op_type, table_name, row_pks)
|
||||
VALUES (NEW.\"{hlc_col}\", 'INSERT', '{table}', json_object({pk_payload}));",
|
||||
log_table = TABLE_CRDT_LOGS,
|
||||
hlc_col = HLC_TIMESTAMP_COLUMN,
|
||||
table = table_name,
|
||||
pk_payload = pk_json_payload
|
||||
)
|
||||
@ -269,9 +270,10 @@ fn generate_insert_trigger_sql(table_name: &str, pks: &[String], cols: &[String]
|
||||
cols.iter().fold(String::new(), |mut acc, col| {
|
||||
writeln!(
|
||||
&mut acc,
|
||||
"INSERT INTO {log_table} (hlc_timestamp, op_type, table_name, row_pks, column_name, new_value)
|
||||
VALUES (hlc_new_timestamp(), 'INSERT', '{table}', json_object({pk_payload}), '{column}', json_object('value', NEW.\"{column}\"));",
|
||||
"INSERT INTO {log_table} (haex_timestamp, op_type, table_name, row_pks, column_name, new_value)
|
||||
VALUES (NEW.\"{hlc_col}\", 'INSERT', '{table}', json_object({pk_payload}), '{column}', json_object('value', NEW.\"{column}\"));",
|
||||
log_table = TABLE_CRDT_LOGS,
|
||||
hlc_col = HLC_TIMESTAMP_COLUMN,
|
||||
table = table_name,
|
||||
pk_payload = pk_json_payload,
|
||||
column = col
|
||||
@ -312,11 +314,12 @@ fn generate_update_trigger_sql(table_name: &str, pks: &[String], cols: &[String]
|
||||
for col in cols {
|
||||
writeln!(
|
||||
&mut body,
|
||||
"INSERT INTO {log_table} (hlc_timestamp, op_type, table_name, row_pks, column_name, new_value, old_value)
|
||||
SELECT hlc_new_timestamp(), 'UPDATE', '{table}', json_object({pk_payload}), '{column}',
|
||||
"INSERT INTO {log_table} (haex_timestamp, op_type, table_name, row_pks, column_name, new_value, old_value)
|
||||
SELECT NEW.\"{hlc_col}\", 'UPDATE', '{table}', json_object({pk_payload}), '{column}',
|
||||
json_object('value', NEW.\"{column}\"), json_object('value', OLD.\"{column}\")
|
||||
WHERE NEW.\"{column}\" IS NOT OLD.\"{column}\";",
|
||||
log_table = TABLE_CRDT_LOGS,
|
||||
hlc_col = HLC_TIMESTAMP_COLUMN,
|
||||
table = table_name,
|
||||
pk_payload = pk_json_payload,
|
||||
column = col
|
||||
@ -327,10 +330,11 @@ fn generate_update_trigger_sql(table_name: &str, pks: &[String], cols: &[String]
|
||||
// Soft-delete loggen
|
||||
writeln!(
|
||||
&mut body,
|
||||
"INSERT INTO {log_table} (hlc_timestamp, op_type, table_name, row_pks)
|
||||
SELECT hlc_new_timestamp(), 'DELETE', '{table}', json_object({pk_payload})
|
||||
"INSERT INTO {log_table} (haex_timestamp, op_type, table_name, row_pks)
|
||||
SELECT NEW.\"{hlc_col}\", 'DELETE', '{table}', json_object({pk_payload})
|
||||
WHERE NEW.\"{tombstone_col}\" = 1 AND OLD.\"{tombstone_col}\" = 0;",
|
||||
log_table = TABLE_CRDT_LOGS,
|
||||
hlc_col = HLC_TIMESTAMP_COLUMN,
|
||||
table = table_name,
|
||||
pk_payload = pk_json_payload,
|
||||
tombstone_col = TOMBSTONE_COLUMN
|
||||
|
||||
@ -16,7 +16,7 @@ pub struct HaexSettings {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub value: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub haex_tombstone: Option<String>,
|
||||
pub haex_tombstone: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub haex_timestamp: Option<String>,
|
||||
}
|
||||
@ -47,7 +47,7 @@ pub struct HaexExtensions {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub homepage: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub enabled: Option<String>,
|
||||
pub enabled: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub icon: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@ -61,7 +61,7 @@ pub struct HaexExtensions {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub version: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub haex_tombstone: Option<String>,
|
||||
pub haex_tombstone: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub haex_timestamp: Option<String>,
|
||||
}
|
||||
@ -107,7 +107,7 @@ pub struct HaexExtensionPermissions {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub updated_at: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub haex_tombstone: Option<String>,
|
||||
pub haex_tombstone: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub haex_timestamp: Option<String>,
|
||||
}
|
||||
|
||||
@ -2,7 +2,12 @@
|
||||
|
||||
pub mod core;
|
||||
pub mod error;
|
||||
pub mod generated;
|
||||
|
||||
use crate::crdt::hlc::HlcService;
|
||||
use crate::database::error::DatabaseError;
|
||||
use crate::table_names::TABLE_CRDT_CONFIGS;
|
||||
use crate::AppState;
|
||||
use rusqlite::Connection;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value as JsonValue;
|
||||
@ -13,13 +18,8 @@ use std::time::UNIX_EPOCH;
|
||||
use std::{fs, sync::Arc};
|
||||
use tauri::{path::BaseDirectory, AppHandle, Manager, State};
|
||||
use tauri_plugin_fs::FsExt;
|
||||
use thiserror::Error;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::crdt::hlc::HlcService;
|
||||
use crate::database::error::DatabaseError;
|
||||
use crate::table_names::TABLE_CRDT_CONFIGS;
|
||||
use crate::AppState;
|
||||
pub struct DbConnection(pub Arc<Mutex<Option<Connection>>>);
|
||||
|
||||
const VAULT_EXTENSION: &str = ".db";
|
||||
|
||||
@ -5,7 +5,7 @@ use crate::crdt::transformer::CrdtTransformer;
|
||||
use crate::crdt::trigger;
|
||||
use crate::database::core::{parse_sql_statements, ValueConverter};
|
||||
use crate::database::error::DatabaseError;
|
||||
use rusqlite::{params_from_iter, Transaction};
|
||||
use rusqlite::{params_from_iter, Params, Transaction};
|
||||
use serde_json::Value as JsonValue;
|
||||
use sqlparser::ast::Statement;
|
||||
use std::collections::HashSet;
|
||||
@ -14,6 +14,62 @@ use std::collections::HashSet;
|
||||
pub struct SqlExecutor;
|
||||
|
||||
impl SqlExecutor {
|
||||
pub fn execute_internal_typed<P>(
|
||||
tx: &Transaction,
|
||||
hlc_service: &HlcService,
|
||||
sql: &str,
|
||||
params: P, // Akzeptiert jetzt alles, was rusqlite als Parameter versteht
|
||||
) -> Result<HashSet<String>, DatabaseError>
|
||||
where
|
||||
P: Params,
|
||||
{
|
||||
let mut ast_vec = parse_sql_statements(sql)?;
|
||||
|
||||
// Wir stellen sicher, dass wir nur EIN Statement verarbeiten. Das ist sicherer.
|
||||
if ast_vec.len() != 1 {
|
||||
return Err(DatabaseError::ExecutionError {
|
||||
sql: sql.to_string(),
|
||||
reason: "execute_internal_typed sollte nur ein einzelnes SQL-Statement erhalten"
|
||||
.to_string(),
|
||||
table: None,
|
||||
});
|
||||
}
|
||||
// Wir nehmen das einzige Statement aus dem Vektor.
|
||||
let mut statement = ast_vec.pop().unwrap();
|
||||
|
||||
let transformer = CrdtTransformer::new();
|
||||
let hlc_timestamp =
|
||||
hlc_service
|
||||
.new_timestamp_and_persist(tx)
|
||||
.map_err(|e| DatabaseError::HlcError {
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
|
||||
let mut modified_schema_tables = HashSet::new();
|
||||
if let Some(table_name) =
|
||||
transformer.transform_execute_statement(&mut statement, &hlc_timestamp)?
|
||||
{
|
||||
modified_schema_tables.insert(table_name);
|
||||
}
|
||||
|
||||
// Führe das transformierte Statement aus.
|
||||
// `params` wird jetzt nur noch einmal hierher bewegt, was korrekt ist.
|
||||
let sql_str = statement.to_string();
|
||||
tx.execute(&sql_str, params)
|
||||
.map_err(|e| DatabaseError::ExecutionError {
|
||||
sql: sql_str.clone(),
|
||||
table: None,
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
|
||||
// Die Trigger-Logik für CREATE TABLE bleibt erhalten.
|
||||
if let Statement::CreateTable(create_table_details) = statement {
|
||||
let table_name_str = create_table_details.name.to_string();
|
||||
trigger::setup_triggers_for_table(tx, &table_name_str, false)?;
|
||||
}
|
||||
|
||||
Ok(modified_schema_tables)
|
||||
}
|
||||
/// Führt SQL aus (mit CRDT-Transformation) - OHNE Permission-Check
|
||||
pub fn execute_internal(
|
||||
tx: &Transaction,
|
||||
|
||||
@ -1,14 +1,17 @@
|
||||
use crate::table_names::TABLE_EXTENSION_PERMISSIONS;
|
||||
use crate::AppState;
|
||||
use crate::database::core::with_connection;
|
||||
use crate::database::error::DatabaseError;
|
||||
use crate::extension::database::executor::SqlExecutor;
|
||||
use crate::extension::error::ExtensionError;
|
||||
use crate::extension::permissions::types::{Action, DbConstraints, ExtensionPermission, FsConstraints, HttpConstraints, PermissionConstraints, PermissionStatus, ResourceType, ShellConstraints};
|
||||
use crate::extension::permissions::types::{parse_constraints, Action, DbConstraints, ExtensionPermission, FsConstraints, HttpConstraints, PermissionConstraints, PermissionStatus, ResourceType, ShellConstraints};
|
||||
use serde_json;
|
||||
use serde_json::json;
|
||||
use std::path::Path;
|
||||
use tauri::State;
|
||||
use url::Url;
|
||||
use crate::database::generated::HaexExtensionPermissions;
|
||||
use rusqlite::{params, ToSql};
|
||||
|
||||
pub struct PermissionManager;
|
||||
|
||||
@ -29,31 +32,29 @@ impl PermissionManager {
|
||||
reason: "Failed to lock HLC service".to_string(),
|
||||
})?;
|
||||
|
||||
let sql = format!(
|
||||
"INSERT INTO {} (id, extension_id, resource_type, action, target, constraints, status) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||
TABLE_EXTENSION_PERMISSIONS
|
||||
);
|
||||
|
||||
for perm in permissions {
|
||||
let resource_type_str = format!("{:?}", perm.resource_type).to_lowercase();
|
||||
let action_str = format!("{:?}", perm.action).to_lowercase();
|
||||
// 1. Konvertiere App-Struct zu DB-Struct
|
||||
let db_perm: HaexExtensionPermissions = perm.into();
|
||||
|
||||
let constraints_json = perm
|
||||
.constraints
|
||||
.as_ref()
|
||||
.map(|c| serde_json::to_string(c).ok())
|
||||
.flatten();
|
||||
|
||||
let sql = "INSERT INTO haex_extension_permissions
|
||||
(id, extension_id, resource_type, action, target, constraints, status)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
let params = vec![
|
||||
json!(perm.id),
|
||||
json!(extension_id),
|
||||
json!(resource_type_str),
|
||||
json!(action_str),
|
||||
json!(perm.target),
|
||||
json!(constraints_json),
|
||||
json!(perm.status.as_str()),
|
||||
// 2. Erstelle typsichere Parameter
|
||||
let params = params![
|
||||
db_perm.id,
|
||||
db_perm.extension_id,
|
||||
db_perm.resource_type,
|
||||
db_perm.action,
|
||||
db_perm.target,
|
||||
db_perm.constraints,
|
||||
db_perm.status,
|
||||
];
|
||||
|
||||
SqlExecutor::execute_internal(&tx, &hlc_service, sql, ¶ms)?;
|
||||
// 3. Führe mit dem typsicheren Executor aus
|
||||
// HINWEIS: Du musst eine `execute_internal_typed` Funktion erstellen!
|
||||
SqlExecutor::execute_internal_typed(&tx, &hlc_service, &sql, params)?;
|
||||
}
|
||||
|
||||
tx.commit().map_err(DatabaseError::from)?;
|
||||
@ -77,32 +78,24 @@ impl PermissionManager {
|
||||
reason: "Failed to lock HLC service".to_string(),
|
||||
})?;
|
||||
|
||||
let resource_type_str = format!("{:?}", permission.resource_type).to_lowercase();
|
||||
let action_str = format!("{:?}", permission.action).to_lowercase();
|
||||
let db_perm: HaexExtensionPermissions = permission.into();
|
||||
|
||||
let sql = format!(
|
||||
"UPDATE {} SET resource_type = ?, action = ?, target = ?, constraints = ?, status = ? WHERE id = ?",
|
||||
TABLE_EXTENSION_PERMISSIONS
|
||||
);
|
||||
|
||||
let constraints_json = permission
|
||||
.constraints
|
||||
.as_ref()
|
||||
.map(|c| serde_json::to_string(c).ok())
|
||||
.flatten();
|
||||
|
||||
let sql = "UPDATE haex_extension_permissions
|
||||
SET resource_type = ?, action = ?, target = ?, constraints = ?, status = ?
|
||||
WHERE id = ?";
|
||||
|
||||
let params = vec![
|
||||
json!(resource_type_str),
|
||||
json!(action_str),
|
||||
json!(permission.target),
|
||||
json!(constraints_json),
|
||||
json!(permission.status.as_str()),
|
||||
json!(permission.id),
|
||||
let params = params![
|
||||
db_perm.resource_type,
|
||||
db_perm.action,
|
||||
db_perm.target,
|
||||
db_perm.constraints,
|
||||
db_perm.status,
|
||||
db_perm.id,
|
||||
];
|
||||
|
||||
SqlExecutor::execute_internal(&tx, &hlc_service, sql, ¶ms)?;
|
||||
|
||||
tx.commit().map_err(DatabaseError::from)?;
|
||||
Ok(())
|
||||
SqlExecutor::execute_internal_typed(&tx, &hlc_service, &sql, params)?;
|
||||
tx.commit().map_err(DatabaseError::from)
|
||||
})
|
||||
.map_err(ExtensionError::from)
|
||||
}
|
||||
@ -123,16 +116,10 @@ impl PermissionManager {
|
||||
reason: "Failed to lock HLC service".to_string(),
|
||||
})?;
|
||||
|
||||
let sql = "UPDATE haex_extension_permissions
|
||||
SET status = ?
|
||||
WHERE id = ?";
|
||||
|
||||
let params = vec![json!(new_status.as_str()), json!(permission_id)];
|
||||
|
||||
SqlExecutor::execute_internal(&tx, &hlc_service, sql, ¶ms)?;
|
||||
|
||||
tx.commit().map_err(DatabaseError::from)?;
|
||||
Ok(())
|
||||
let sql = format!("UPDATE {} SET status = ? WHERE id = ?", TABLE_EXTENSION_PERMISSIONS);
|
||||
let params = params![new_status.as_str(), permission_id];
|
||||
SqlExecutor::execute_internal_typed(&tx, &hlc_service, &sql, params)?;
|
||||
tx.commit().map_err(DatabaseError::from)
|
||||
})
|
||||
.map_err(ExtensionError::from)
|
||||
}
|
||||
@ -151,14 +138,9 @@ impl PermissionManager {
|
||||
})?;
|
||||
|
||||
// Echtes DELETE - wird vom CrdtTransformer zu UPDATE umgewandelt
|
||||
let sql = "DELETE FROM haex_extension_permissions WHERE id = ?";
|
||||
|
||||
let params = vec![json!(permission_id)];
|
||||
|
||||
SqlExecutor::execute_internal(&tx, &hlc_service, sql, ¶ms)?;
|
||||
|
||||
tx.commit().map_err(DatabaseError::from)?;
|
||||
Ok(())
|
||||
let sql = format!("DELETE FROM {} WHERE id = ?", TABLE_EXTENSION_PERMISSIONS);
|
||||
SqlExecutor::execute_internal_typed(&tx, &hlc_service, &sql, params![permission_id])?;
|
||||
tx.commit().map_err(DatabaseError::from)
|
||||
}).map_err(ExtensionError::from)
|
||||
}
|
||||
|
||||
@ -175,15 +157,9 @@ impl PermissionManager {
|
||||
reason: "Failed to lock HLC service".to_string(),
|
||||
})?;
|
||||
|
||||
// Echtes DELETE - wird vom CrdtTransformer zu UPDATE umgewandelt
|
||||
let sql = "DELETE FROM haex_extension_permissions WHERE extension_id = ?";
|
||||
|
||||
let params = vec![json!(extension_id)];
|
||||
|
||||
SqlExecutor::execute_internal(&tx, &hlc_service, sql, ¶ms)?;
|
||||
|
||||
tx.commit().map_err(DatabaseError::from)?;
|
||||
Ok(())
|
||||
let sql = format!("DELETE FROM {} WHERE extension_id = ?", TABLE_EXTENSION_PERMISSIONS);
|
||||
SqlExecutor::execute_internal_typed(&tx, &hlc_service, &sql, params![extension_id])?;
|
||||
tx.commit().map_err(DatabaseError::from)
|
||||
}).map_err(ExtensionError::from)
|
||||
}
|
||||
/// Lädt alle Permissions einer Extension
|
||||
@ -192,92 +168,23 @@ impl PermissionManager {
|
||||
extension_id: &str,
|
||||
) -> Result<Vec<ExtensionPermission>, ExtensionError> {
|
||||
with_connection(&app_state.db, |conn| {
|
||||
let sql = "SELECT id, extension_id, resource_type, action, target, constraints, status, haex_timestamp, haex_tombstone
|
||||
FROM haex_extension_permissions
|
||||
WHERE extension_id = ?";
|
||||
let sql = format!("SELECT * FROM {} WHERE extension_id = ?", TABLE_EXTENSION_PERMISSIONS);
|
||||
let mut stmt = conn.prepare(&sql).map_err(DatabaseError::from)?;
|
||||
|
||||
let params = vec![json!(extension_id)];
|
||||
let perms_iter = stmt.query_map(params![extension_id], |row| {
|
||||
HaexExtensionPermissions::from_row(row)
|
||||
})?;
|
||||
|
||||
// SELECT nutzt select_internal
|
||||
let results = SqlExecutor::select_internal(conn, sql, ¶ms)?;
|
||||
|
||||
// Parse JSON results zu ExtensionPermission
|
||||
let permissions = results
|
||||
.into_iter()
|
||||
.map(|row| Self::parse_permission_from_json(row))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let permissions = perms_iter
|
||||
.filter_map(Result::ok)
|
||||
.map(Into::into)
|
||||
.collect();
|
||||
|
||||
Ok(permissions)
|
||||
}).map_err(ExtensionError::from)
|
||||
}
|
||||
|
||||
// Helper für JSON -> ExtensionPermission Konvertierung
|
||||
fn parse_permission_from_json(json: serde_json::Value) -> Result<ExtensionPermission, DatabaseError> {
|
||||
|
||||
let obj = json.as_object().ok_or_else(|| DatabaseError::SerializationError {
|
||||
reason: "Expected JSON object".to_string(),
|
||||
})?;
|
||||
|
||||
let resource_type = Self::parse_resource_type(
|
||||
obj.get("resource_type")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| DatabaseError::SerializationError {
|
||||
reason: "Missing resource_type".to_string(),
|
||||
})?
|
||||
)?;
|
||||
|
||||
let action = Self::parse_action(
|
||||
obj.get("action")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| DatabaseError::SerializationError {
|
||||
reason: "Missing action".to_string(),
|
||||
})?
|
||||
)?;
|
||||
|
||||
let status = PermissionStatus::from_str(
|
||||
obj.get("status")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| DatabaseError::SerializationError {
|
||||
reason: "Missing status".to_string(),
|
||||
})?
|
||||
)?; // Jetzt funktioniert das ?
|
||||
|
||||
let constraints = obj.get("constraints")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|json_str| Self::parse_constraints(&resource_type, json_str))
|
||||
.transpose()?;
|
||||
|
||||
Ok(ExtensionPermission {
|
||||
id: obj.get("id")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| DatabaseError::SerializationError {
|
||||
reason: "Missing id".to_string(),
|
||||
})?
|
||||
.to_string(),
|
||||
extension_id: obj.get("extension_id")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| DatabaseError::SerializationError {
|
||||
reason: "Missing extension_id".to_string(),
|
||||
})?
|
||||
.to_string(),
|
||||
resource_type,
|
||||
action,
|
||||
target: obj.get("target")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| DatabaseError::SerializationError {
|
||||
reason: "Missing target".to_string(),
|
||||
})?
|
||||
.to_string(),
|
||||
constraints,
|
||||
status,
|
||||
haex_timestamp: obj.get("haex_timestamp")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string()),
|
||||
haex_tombstone: obj.get("haex_tombstone")
|
||||
.and_then(|v| v.as_i64())
|
||||
.map(|i| i == 1),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/// Prüft Datenbankberechtigungen
|
||||
pub async fn check_database_permission(
|
||||
@ -311,7 +218,7 @@ impl PermissionManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Prüft Dateisystem-Berechtigungen
|
||||
/* /// Prüft Dateisystem-Berechtigungen
|
||||
pub async fn check_filesystem_permission(
|
||||
app_state: &State<'_, AppState>,
|
||||
extension_id: &str,
|
||||
@ -471,9 +378,9 @@ impl PermissionManager {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
*/
|
||||
// Helper-Methoden - müssen DatabaseError statt ExtensionError zurückgeben
|
||||
fn parse_resource_type(s: &str) -> Result<ResourceType, DatabaseError> {
|
||||
pub fn parse_resource_type(s: &str) -> Result<ResourceType, DatabaseError> {
|
||||
match s {
|
||||
"fs" => Ok(ResourceType::Fs),
|
||||
"http" => Ok(ResourceType::Http),
|
||||
@ -485,52 +392,8 @@ impl PermissionManager {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_action(s: &str) -> Result<Action, DatabaseError> {
|
||||
match s {
|
||||
"read" => Ok(Action::Read),
|
||||
"write" => Ok(Action::Write),
|
||||
_ => Err(DatabaseError::SerializationError {
|
||||
reason: format!("Unknown action: {}", s),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_constraints(
|
||||
resource_type: &ResourceType,
|
||||
json: &str,
|
||||
) -> Result<PermissionConstraints, DatabaseError> {
|
||||
match resource_type {
|
||||
ResourceType::Db => {
|
||||
let constraints: DbConstraints = serde_json::from_str(json)
|
||||
.map_err(|e| DatabaseError::SerializationError {
|
||||
reason: format!("Failed to parse DB constraints: {}", e),
|
||||
})?;
|
||||
Ok(PermissionConstraints::Database(constraints))
|
||||
}
|
||||
ResourceType::Fs => {
|
||||
let constraints: FsConstraints = serde_json::from_str(json)
|
||||
.map_err(|e| DatabaseError::SerializationError {
|
||||
reason: format!("Failed to parse FS constraints: {}", e),
|
||||
})?;
|
||||
Ok(PermissionConstraints::Filesystem(constraints))
|
||||
}
|
||||
ResourceType::Http => {
|
||||
let constraints: HttpConstraints = serde_json::from_str(json)
|
||||
.map_err(|e| DatabaseError::SerializationError {
|
||||
reason: format!("Failed to parse HTTP constraints: {}", e),
|
||||
})?;
|
||||
Ok(PermissionConstraints::Http(constraints))
|
||||
}
|
||||
ResourceType::Shell => {
|
||||
let constraints: ShellConstraints = serde_json::from_str(json)
|
||||
.map_err(|e| DatabaseError::SerializationError {
|
||||
reason: format!("Failed to parse Shell constraints: {}", e),
|
||||
})?;
|
||||
Ok(PermissionConstraints::Shell(constraints))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn matches_path_pattern(pattern: &str, path: &str) -> bool {
|
||||
if pattern.ends_with("/*") {
|
||||
let prefix = &pattern[..pattern.len() - 2];
|
||||
@ -557,7 +420,7 @@ impl PermissionManager {
|
||||
}
|
||||
|
||||
// Convenience-Funktionen für die verschiedenen Subsysteme
|
||||
impl PermissionManager {
|
||||
/* impl PermissionManager {
|
||||
// Convenience-Methoden
|
||||
pub async fn can_read_file(
|
||||
app_state: &State<'_, AppState>,
|
||||
@ -647,4 +510,4 @@ impl PermissionManager {
|
||||
.filter(|perm| perm.status == PermissionStatus::Ask)
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
} */
|
||||
|
||||
@ -1,6 +1,12 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
// src-tauri/src/extension/permissions/types.rs
|
||||
|
||||
use crate::database::error::DatabaseError;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::{
|
||||
database::{error::DatabaseError, generated::HaexExtensionPermissions},
|
||||
extension::permissions::manager::PermissionManager,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct ExtensionPermission {
|
||||
@ -20,6 +26,52 @@ pub struct ExtensionPermission {
|
||||
pub haex_timestamp: Option<String>,
|
||||
}
|
||||
|
||||
impl From<HaexExtensionPermissions> for ExtensionPermission {
|
||||
fn from(db_perm: HaexExtensionPermissions) -> Self {
|
||||
let resource_type = ResourceType::from_str(&db_perm.resource_type.unwrap_or_default())
|
||||
.unwrap_or(ResourceType::Db); // Fallback
|
||||
|
||||
let constraints = db_perm
|
||||
.constraints
|
||||
.and_then(|json_str| parse_constraints(&resource_type, &json_str).ok());
|
||||
|
||||
ExtensionPermission {
|
||||
id: db_perm.id,
|
||||
extension_id: db_perm.extension_id.unwrap_or_default(),
|
||||
resource_type,
|
||||
action: Action::from_str(&db_perm.action.unwrap_or_default()).unwrap_or(Action::Read),
|
||||
target: db_perm.target.unwrap_or_default(),
|
||||
status: PermissionStatus::from_str(&db_perm.status).unwrap_or(PermissionStatus::Ask),
|
||||
constraints,
|
||||
haex_timestamp: db_perm.haex_timestamp,
|
||||
haex_tombstone: db_perm.haex_tombstone,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ExtensionPermission> for HaexExtensionPermissions {
|
||||
fn from(perm: &ExtensionPermission) -> Self {
|
||||
let constraints_json = perm
|
||||
.constraints
|
||||
.as_ref()
|
||||
.and_then(|c| serde_json::to_string(c).ok());
|
||||
|
||||
HaexExtensionPermissions {
|
||||
id: perm.id.clone(),
|
||||
extension_id: Some(perm.extension_id.clone()),
|
||||
resource_type: Some(format!("{:?}", perm.resource_type).to_lowercase()),
|
||||
action: Some(format!("{:?}", perm.action).to_lowercase()),
|
||||
target: Some(perm.target.clone()),
|
||||
constraints: constraints_json,
|
||||
status: perm.status.as_str().to_string(),
|
||||
created_at: None, // Wird von der DB gesetzt
|
||||
updated_at: None, // Wird von der DB gesetzt
|
||||
haex_timestamp: perm.haex_timestamp.clone(),
|
||||
haex_tombstone: perm.haex_tombstone,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ResourceType {
|
||||
@ -29,13 +81,43 @@ pub enum ResourceType {
|
||||
Shell,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
impl FromStr for ResourceType {
|
||||
type Err = DatabaseError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"fs" => Ok(ResourceType::Fs),
|
||||
"http" => Ok(ResourceType::Http),
|
||||
"db" => Ok(ResourceType::Db),
|
||||
"shell" => Ok(ResourceType::Shell),
|
||||
_ => Err(DatabaseError::SerializationError {
|
||||
reason: format!("Unbekannter Ressourcentyp: {}", s),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Action {
|
||||
Read,
|
||||
Write,
|
||||
}
|
||||
|
||||
impl FromStr for Action {
|
||||
type Err = DatabaseError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"read" => Ok(Action::Read),
|
||||
"write" => Ok(Action::Write),
|
||||
_ => Err(DatabaseError::SerializationError {
|
||||
reason: format!("Unbekannte Aktion: {}", s),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum PermissionStatus {
|
||||
@ -125,7 +207,7 @@ pub struct EditablePermissions {
|
||||
}
|
||||
|
||||
// Oder gruppiert nach Typ:
|
||||
impl EditablePermissions {
|
||||
/* impl EditablePermissions {
|
||||
pub fn database_permissions(&self) -> Vec<&ExtensionPermission> {
|
||||
self.permissions
|
||||
.iter()
|
||||
@ -153,4 +235,40 @@ impl EditablePermissions {
|
||||
.filter(|p| p.resource_type == ResourceType::Shell)
|
||||
.collect()
|
||||
}
|
||||
} */
|
||||
|
||||
pub fn parse_constraints(
|
||||
resource_type: &ResourceType,
|
||||
json: &str,
|
||||
) -> Result<PermissionConstraints, DatabaseError> {
|
||||
match resource_type {
|
||||
ResourceType::Db => {
|
||||
let constraints: DbConstraints =
|
||||
serde_json::from_str(json).map_err(|e| DatabaseError::SerializationError {
|
||||
reason: format!("Failed to parse DB constraints: {}", e),
|
||||
})?;
|
||||
Ok(PermissionConstraints::Database(constraints))
|
||||
}
|
||||
ResourceType::Fs => {
|
||||
let constraints: FsConstraints =
|
||||
serde_json::from_str(json).map_err(|e| DatabaseError::SerializationError {
|
||||
reason: format!("Failed to parse FS constraints: {}", e),
|
||||
})?;
|
||||
Ok(PermissionConstraints::Filesystem(constraints))
|
||||
}
|
||||
ResourceType::Http => {
|
||||
let constraints: HttpConstraints =
|
||||
serde_json::from_str(json).map_err(|e| DatabaseError::SerializationError {
|
||||
reason: format!("Failed to parse HTTP constraints: {}", e),
|
||||
})?;
|
||||
Ok(PermissionConstraints::Http(constraints))
|
||||
}
|
||||
ResourceType::Shell => {
|
||||
let constraints: ShellConstraints =
|
||||
serde_json::from_str(json).map_err(|e| DatabaseError::SerializationError {
|
||||
reason: format!("Failed to parse Shell constraints: {}", e),
|
||||
})?;
|
||||
Ok(PermissionConstraints::Shell(constraints))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user