mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-17 06:30:50 +01:00
add more typesafty
This commit is contained in:
@ -16,9 +16,9 @@ crate-type = ["staticlib", "cdylib", "rlib"]
|
|||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
serde_json = "1.0.145"
|
serde_json = "1.0.145"
|
||||||
|
|
||||||
tauri-build = { version = "2.2", features = [] }
|
tauri-build = { version = "2.2", features = [] }
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rusqlite = { version = "0.37.0", features = [
|
rusqlite = { version = "0.37.0", features = [
|
||||||
"load_extension",
|
"load_extension",
|
||||||
|
|||||||
@ -1,106 +1,7 @@
|
|||||||
use serde::Deserialize;
|
mod generator;
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Pfad zur Eingabe-JSON und zur Ausgabe-Rust-Datei festlegen.
|
generator::table_names::generate_table_names();
|
||||||
// `OUT_DIR` ist ein spezielles Verzeichnis, das Cargo für generierte Dateien bereitstellt.
|
generator::rust_types::generate_rust_types();
|
||||||
let schema_path = Path::new("database/tableNames.json");
|
tauri_build::build();
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,23 +23,16 @@ function drizzleToRustType(colDef: AnySQLiteColumn): {
|
|||||||
let isOptional = !colDef.notNull
|
let isOptional = !colDef.notNull
|
||||||
|
|
||||||
if (colDef.columnType === 'SQLiteText') {
|
if (colDef.columnType === 'SQLiteText') {
|
||||||
// Das 'mode' Property ist ebenfalls direkt auf dem Objekt verfügbar
|
|
||||||
if ('mode' in colDef && colDef.mode === 'json') {
|
if ('mode' in colDef && colDef.mode === 'json') {
|
||||||
baseType = 'serde_json::Value'
|
baseType = 'serde_json::Value'
|
||||||
} else {
|
} else {
|
||||||
baseType = 'String'
|
baseType = 'String'
|
||||||
}
|
}
|
||||||
} else if (colDef.columnType === 'SQLiteInteger') {
|
} else if (colDef.columnType === 'SQLiteInteger') {
|
||||||
if ('mode' in colDef && colDef.mode === 'boolean') {
|
baseType = 'i64'
|
||||||
baseType = 'bool'
|
} else if (colDef.columnType === 'SQLiteBoolean') {
|
||||||
} else if ('mode' in colDef && colDef.mode === 'timestamp') {
|
baseType = 'bool'
|
||||||
baseType = 'i64'
|
} else if (colDef.columnType === 'SQLiteReal') {
|
||||||
} else {
|
|
||||||
baseType = 'i64'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ... weitere SQLite-Typen hier ...
|
|
||||||
else if (colDef.columnType === 'SQLiteReal') {
|
|
||||||
baseType = 'f64'
|
baseType = 'f64'
|
||||||
} else if (colDef.columnType === 'SQLiteBlob') {
|
} else if (colDef.columnType === 'SQLiteBlob') {
|
||||||
baseType = 'Vec<u8>'
|
baseType = 'Vec<u8>'
|
||||||
@ -81,6 +74,7 @@ function toSnakeCase(str: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function toPascalCase(str: string): string {
|
function toPascalCase(str: string): string {
|
||||||
|
console.log('toPascalCase:', str)
|
||||||
return str
|
return str
|
||||||
.split('_')
|
.split('_')
|
||||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
||||||
@ -164,15 +158,18 @@ use serde::{Deserialize, Serialize};
|
|||||||
`
|
`
|
||||||
|
|
||||||
const schemas = [
|
const schemas = [
|
||||||
{ name: tablesNames.haex.settings, table: schema.haexSettings },
|
{ name: tablesNames.haex.settings.name, table: schema.haexSettings },
|
||||||
{ name: tablesNames.haex.extensions, table: schema.haexExtensions },
|
{ name: tablesNames.haex.extensions.name, table: schema.haexExtensions },
|
||||||
{
|
{
|
||||||
name: tablesNames.haex.extension_permissions,
|
name: tablesNames.haex.extension_permissions.name,
|
||||||
table: schema.haexExtensionPermissions,
|
table: schema.haexExtensionPermissions,
|
||||||
},
|
},
|
||||||
{ name: tablesNames.haex.crdt.logs, table: schema.haexCrdtLogs },
|
{ name: tablesNames.haex.crdt.logs.name, table: schema.haexCrdtLogs },
|
||||||
{ name: tablesNames.haex.crdt.snapshots, table: schema.haexCrdtSnapshots },
|
{
|
||||||
{ name: tablesNames.haex.crdt.configs, table: schema.haexCrdtConfigs },
|
name: tablesNames.haex.crdt.snapshots.name,
|
||||||
|
table: schema.haexCrdtSnapshots,
|
||||||
|
},
|
||||||
|
{ name: tablesNames.haex.crdt.configs.name, table: schema.haexCrdtConfigs },
|
||||||
]
|
]
|
||||||
|
|
||||||
for (const { name, table } of schemas) {
|
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,
|
"when": 1759402321133,
|
||||||
"tag": "0000_glamorous_hulk",
|
"tag": "0000_glamorous_hulk",
|
||||||
"breakpoints": true
|
"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'
|
import tableNames from '../tableNames.json'
|
||||||
|
|
||||||
export const haexCrdtLogs = sqliteTable(
|
export const haexCrdtLogs = sqliteTable(
|
||||||
tableNames.haex.crdt.logs,
|
tableNames.haex.crdt.logs.name,
|
||||||
{
|
{
|
||||||
id: text()
|
id: text()
|
||||||
.primaryKey()
|
.primaryKey()
|
||||||
.$defaultFn(() => crypto.randomUUID()),
|
.$defaultFn(() => crypto.randomUUID()),
|
||||||
haexTimestamp: text('haex_timestamp'),
|
haexTimestamp: text(tableNames.haex.crdt.logs.columns.haexTimestamp),
|
||||||
tableName: text('table_name'),
|
tableName: text(tableNames.haex.crdt.logs.columns.tableName),
|
||||||
rowPks: text('row_pks', { mode: 'json' }),
|
rowPks: text(tableNames.haex.crdt.logs.columns.rowPks, { mode: 'json' }),
|
||||||
opType: text('op_type', { enum: ['INSERT', 'UPDATE', 'DELETE'] }),
|
opType: text(tableNames.haex.crdt.logs.columns.opType, {
|
||||||
columnName: text('column_name'),
|
enum: ['INSERT', 'UPDATE', 'DELETE'],
|
||||||
newValue: text('new_value', { mode: 'json' }),
|
}),
|
||||||
oldValue: text('old_value', { mode: 'json' }),
|
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) => [
|
(table) => [
|
||||||
index('idx_haex_timestamp').on(table.haexTimestamp),
|
index('idx_haex_timestamp').on(table.haexTimestamp),
|
||||||
@ -23,17 +29,22 @@ export const haexCrdtLogs = sqliteTable(
|
|||||||
export type InsertHaexCrdtLogs = typeof haexCrdtLogs.$inferInsert
|
export type InsertHaexCrdtLogs = typeof haexCrdtLogs.$inferInsert
|
||||||
export type SelectHaexCrdtLogs = typeof haexCrdtLogs.$inferSelect
|
export type SelectHaexCrdtLogs = typeof haexCrdtLogs.$inferSelect
|
||||||
|
|
||||||
export const haexCrdtSnapshots = sqliteTable(tableNames.haex.crdt.snapshots, {
|
export const haexCrdtSnapshots = sqliteTable(
|
||||||
snapshot_id: text()
|
tableNames.haex.crdt.snapshots.name,
|
||||||
.primaryKey()
|
{
|
||||||
.$defaultFn(() => crypto.randomUUID()),
|
snapshotId: text(tableNames.haex.crdt.snapshots.columns.snapshotId)
|
||||||
created: text(),
|
.primaryKey()
|
||||||
epoch_hlc: text(),
|
.$defaultFn(() => crypto.randomUUID()),
|
||||||
location_url: text(),
|
created: text(),
|
||||||
file_size_bytes: integer(),
|
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()
|
key: text()
|
||||||
.primaryKey()
|
.primaryKey()
|
||||||
.$defaultFn(() => crypto.randomUUID()),
|
.$defaultFn(() => crypto.randomUUID()),
|
||||||
|
|||||||
@ -8,20 +8,22 @@ import {
|
|||||||
} from 'drizzle-orm/sqlite-core'
|
} from 'drizzle-orm/sqlite-core'
|
||||||
import tableNames from '../tableNames.json'
|
import tableNames from '../tableNames.json'
|
||||||
|
|
||||||
export const haexSettings = sqliteTable(tableNames.haex.settings, {
|
export const haexSettings = sqliteTable(tableNames.haex.settings.name, {
|
||||||
id: text()
|
id: text()
|
||||||
.primaryKey()
|
.primaryKey()
|
||||||
.$defaultFn(() => crypto.randomUUID()),
|
.$defaultFn(() => crypto.randomUUID()),
|
||||||
key: text(),
|
key: text(),
|
||||||
type: text(),
|
type: text(),
|
||||||
value: text(),
|
value: text(),
|
||||||
haexTombstone: integer('haex_tombstone', { mode: 'boolean' }),
|
haexTombstone: integer(tableNames.haex.settings.columns.haexTombstone, {
|
||||||
haexTimestamp: text('haex_timestamp'),
|
mode: 'boolean',
|
||||||
|
}),
|
||||||
|
haexTimestamp: text(tableNames.haex.settings.columns.haexTimestamp),
|
||||||
})
|
})
|
||||||
export type InsertHaexSettings = typeof haexSettings.$inferInsert
|
export type InsertHaexSettings = typeof haexSettings.$inferInsert
|
||||||
export type SelectHaexSettings = typeof haexSettings.$inferSelect
|
export type SelectHaexSettings = typeof haexSettings.$inferSelect
|
||||||
|
|
||||||
export const haexExtensions = sqliteTable(tableNames.haex.extensions, {
|
export const haexExtensions = sqliteTable(tableNames.haex.extensions.name, {
|
||||||
id: text()
|
id: text()
|
||||||
.primaryKey()
|
.primaryKey()
|
||||||
.$defaultFn(() => crypto.randomUUID()),
|
.$defaultFn(() => crypto.randomUUID()),
|
||||||
@ -36,21 +38,23 @@ export const haexExtensions = sqliteTable(tableNames.haex.extensions, {
|
|||||||
signature: text(),
|
signature: text(),
|
||||||
url: text(),
|
url: text(),
|
||||||
version: text(),
|
version: text(),
|
||||||
haexTombstone: integer('haex_tombstone', { mode: 'boolean' }),
|
haexTombstone: integer(tableNames.haex.extensions.columns.haexTombstone, {
|
||||||
haexTimestamp: text('haex_timestamp'),
|
mode: 'boolean',
|
||||||
|
}),
|
||||||
|
haexTimestamp: text(tableNames.haex.extensions.columns.haexTimestamp),
|
||||||
})
|
})
|
||||||
export type InsertHaexExtensions = typeof haexExtensions.$inferInsert
|
export type InsertHaexExtensions = typeof haexExtensions.$inferInsert
|
||||||
export type SelectHaexExtensions = typeof haexExtensions.$inferSelect
|
export type SelectHaexExtensions = typeof haexExtensions.$inferSelect
|
||||||
|
|
||||||
export const haexExtensionPermissions = sqliteTable(
|
export const haexExtensionPermissions = sqliteTable(
|
||||||
tableNames.haex.extension_permissions,
|
tableNames.haex.extension_permissions.name,
|
||||||
{
|
{
|
||||||
id: text()
|
id: text()
|
||||||
.primaryKey()
|
.primaryKey()
|
||||||
.$defaultFn(() => crypto.randomUUID()),
|
.$defaultFn(() => crypto.randomUUID()),
|
||||||
extensionId: text('extension_id').references(
|
extensionId: text(
|
||||||
(): AnySQLiteColumn => haexExtensions.id,
|
tableNames.haex.extension_permissions.columns.extensionId,
|
||||||
),
|
).references((): AnySQLiteColumn => haexExtensions.id),
|
||||||
resourceType: text('resource_type', {
|
resourceType: text('resource_type', {
|
||||||
enum: ['fs', 'http', 'db', 'shell'],
|
enum: ['fs', 'http', 'db', 'shell'],
|
||||||
}),
|
}),
|
||||||
@ -64,8 +68,13 @@ export const haexExtensionPermissions = sqliteTable(
|
|||||||
updateAt: integer('updated_at', { mode: 'timestamp' }).$onUpdate(
|
updateAt: integer('updated_at', { mode: 'timestamp' }).$onUpdate(
|
||||||
() => new Date(),
|
() => new Date(),
|
||||||
),
|
),
|
||||||
haexTombstone: integer('haex_tombstone', { mode: 'boolean' }),
|
haexTombstone: integer(
|
||||||
haexTimestamp: text('haex_timestamp'),
|
tableNames.haex.extension_permissions.columns.haexTombstone,
|
||||||
|
{ mode: 'boolean' },
|
||||||
|
),
|
||||||
|
haexTimestamp: text(
|
||||||
|
tableNames.haex.extension_permissions.columns.haexTimestamp,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
(table) => [
|
(table) => [
|
||||||
unique().on(
|
unique().on(
|
||||||
@ -80,3 +89,28 @@ export type InserthaexExtensionPermissions =
|
|||||||
typeof haexExtensionPermissions.$inferInsert
|
typeof haexExtensionPermissions.$inferInsert
|
||||||
export type SelecthaexExtensionPermissions =
|
export type SelecthaexExtensionPermissions =
|
||||||
typeof haexExtensionPermissions.$inferSelect
|
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'
|
} from 'drizzle-orm/sqlite-core'
|
||||||
import tableNames from '../tableNames.json'
|
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(
|
export const haexPasswordsItemDetails = sqliteTable(
|
||||||
tableNames.haex.passwords.item_details,
|
tableNames.haex.passwords.item_details,
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,9 +1,68 @@
|
|||||||
{
|
{
|
||||||
"haex": {
|
"haex": {
|
||||||
"settings": "haex_settings",
|
"settings": {
|
||||||
"extensions": "haex_extensions",
|
"name": "haex_settings",
|
||||||
"extension_permissions": "haex_extension_permissions",
|
"columns": {
|
||||||
"notifications": "haex_notifications",
|
"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": {
|
"passwords": {
|
||||||
"groups": "haex_passwords_groups",
|
"groups": "haex_passwords_groups",
|
||||||
"group_items": "haex_passwords_group_items",
|
"group_items": "haex_passwords_group_items",
|
||||||
@ -12,9 +71,36 @@
|
|||||||
"item_histories": "haex_passwords_item_history"
|
"item_histories": "haex_passwords_item_history"
|
||||||
},
|
},
|
||||||
"crdt": {
|
"crdt": {
|
||||||
"logs": "haex_crdt_logs",
|
"logs": {
|
||||||
"snapshots": "haex_crdt_snapshots",
|
"name": "haex_crdt_logs",
|
||||||
"configs": "haex_crdt_configs"
|
"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 rust_types;
|
||||||
pub mod table_names;
|
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() {
|
let column_inserts = if cols.is_empty() {
|
||||||
// Nur PKs -> einfacher Insert ins Log
|
// Nur PKs -> einfacher Insert ins Log
|
||||||
format!(
|
format!(
|
||||||
"INSERT INTO {log_table} (hlc_timestamp, op_type, table_name, row_pks)
|
"INSERT INTO {log_table} (haex_timestamp, op_type, table_name, row_pks)
|
||||||
VALUES (hlc_new_timestamp(), 'INSERT', '{table}', json_object({pk_payload}));",
|
VALUES (NEW.\"{hlc_col}\", 'INSERT', '{table}', json_object({pk_payload}));",
|
||||||
log_table = TABLE_CRDT_LOGS,
|
log_table = TABLE_CRDT_LOGS,
|
||||||
|
hlc_col = HLC_TIMESTAMP_COLUMN,
|
||||||
table = table_name,
|
table = table_name,
|
||||||
pk_payload = pk_json_payload
|
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| {
|
cols.iter().fold(String::new(), |mut acc, col| {
|
||||||
writeln!(
|
writeln!(
|
||||||
&mut acc,
|
&mut acc,
|
||||||
"INSERT INTO {log_table} (hlc_timestamp, op_type, table_name, row_pks, column_name, new_value)
|
"INSERT INTO {log_table} (haex_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}\"));",
|
VALUES (NEW.\"{hlc_col}\", 'INSERT', '{table}', json_object({pk_payload}), '{column}', json_object('value', NEW.\"{column}\"));",
|
||||||
log_table = TABLE_CRDT_LOGS,
|
log_table = TABLE_CRDT_LOGS,
|
||||||
|
hlc_col = HLC_TIMESTAMP_COLUMN,
|
||||||
table = table_name,
|
table = table_name,
|
||||||
pk_payload = pk_json_payload,
|
pk_payload = pk_json_payload,
|
||||||
column = col
|
column = col
|
||||||
@ -312,11 +314,12 @@ fn generate_update_trigger_sql(table_name: &str, pks: &[String], cols: &[String]
|
|||||||
for col in cols {
|
for col in cols {
|
||||||
writeln!(
|
writeln!(
|
||||||
&mut body,
|
&mut body,
|
||||||
"INSERT INTO {log_table} (hlc_timestamp, op_type, table_name, row_pks, column_name, new_value, old_value)
|
"INSERT INTO {log_table} (haex_timestamp, op_type, table_name, row_pks, column_name, new_value, old_value)
|
||||||
SELECT hlc_new_timestamp(), 'UPDATE', '{table}', json_object({pk_payload}), '{column}',
|
SELECT NEW.\"{hlc_col}\", 'UPDATE', '{table}', json_object({pk_payload}), '{column}',
|
||||||
json_object('value', NEW.\"{column}\"), json_object('value', OLD.\"{column}\")
|
json_object('value', NEW.\"{column}\"), json_object('value', OLD.\"{column}\")
|
||||||
WHERE NEW.\"{column}\" IS NOT OLD.\"{column}\";",
|
WHERE NEW.\"{column}\" IS NOT OLD.\"{column}\";",
|
||||||
log_table = TABLE_CRDT_LOGS,
|
log_table = TABLE_CRDT_LOGS,
|
||||||
|
hlc_col = HLC_TIMESTAMP_COLUMN,
|
||||||
table = table_name,
|
table = table_name,
|
||||||
pk_payload = pk_json_payload,
|
pk_payload = pk_json_payload,
|
||||||
column = col
|
column = col
|
||||||
@ -327,10 +330,11 @@ fn generate_update_trigger_sql(table_name: &str, pks: &[String], cols: &[String]
|
|||||||
// Soft-delete loggen
|
// Soft-delete loggen
|
||||||
writeln!(
|
writeln!(
|
||||||
&mut body,
|
&mut body,
|
||||||
"INSERT INTO {log_table} (hlc_timestamp, op_type, table_name, row_pks)
|
"INSERT INTO {log_table} (haex_timestamp, op_type, table_name, row_pks)
|
||||||
SELECT hlc_new_timestamp(), 'DELETE', '{table}', json_object({pk_payload})
|
SELECT NEW.\"{hlc_col}\", 'DELETE', '{table}', json_object({pk_payload})
|
||||||
WHERE NEW.\"{tombstone_col}\" = 1 AND OLD.\"{tombstone_col}\" = 0;",
|
WHERE NEW.\"{tombstone_col}\" = 1 AND OLD.\"{tombstone_col}\" = 0;",
|
||||||
log_table = TABLE_CRDT_LOGS,
|
log_table = TABLE_CRDT_LOGS,
|
||||||
|
hlc_col = HLC_TIMESTAMP_COLUMN,
|
||||||
table = table_name,
|
table = table_name,
|
||||||
pk_payload = pk_json_payload,
|
pk_payload = pk_json_payload,
|
||||||
tombstone_col = TOMBSTONE_COLUMN
|
tombstone_col = TOMBSTONE_COLUMN
|
||||||
|
|||||||
@ -16,7 +16,7 @@ pub struct HaexSettings {
|
|||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub value: Option<String>,
|
pub value: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub haex_tombstone: Option<String>,
|
pub haex_tombstone: Option<bool>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub haex_timestamp: Option<String>,
|
pub haex_timestamp: Option<String>,
|
||||||
}
|
}
|
||||||
@ -47,7 +47,7 @@ pub struct HaexExtensions {
|
|||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub homepage: Option<String>,
|
pub homepage: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub enabled: Option<String>,
|
pub enabled: Option<bool>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub icon: Option<String>,
|
pub icon: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
@ -61,7 +61,7 @@ pub struct HaexExtensions {
|
|||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub version: Option<String>,
|
pub version: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub haex_tombstone: Option<String>,
|
pub haex_tombstone: Option<bool>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub haex_timestamp: Option<String>,
|
pub haex_timestamp: Option<String>,
|
||||||
}
|
}
|
||||||
@ -107,7 +107,7 @@ pub struct HaexExtensionPermissions {
|
|||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub updated_at: Option<String>,
|
pub updated_at: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub haex_tombstone: Option<String>,
|
pub haex_tombstone: Option<bool>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub haex_timestamp: Option<String>,
|
pub haex_timestamp: Option<String>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,12 @@
|
|||||||
|
|
||||||
pub mod core;
|
pub mod core;
|
||||||
pub mod error;
|
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 rusqlite::Connection;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value as JsonValue;
|
use serde_json::Value as JsonValue;
|
||||||
@ -13,13 +18,8 @@ use std::time::UNIX_EPOCH;
|
|||||||
use std::{fs, sync::Arc};
|
use std::{fs, sync::Arc};
|
||||||
use tauri::{path::BaseDirectory, AppHandle, Manager, State};
|
use tauri::{path::BaseDirectory, AppHandle, Manager, State};
|
||||||
use tauri_plugin_fs::FsExt;
|
use tauri_plugin_fs::FsExt;
|
||||||
use thiserror::Error;
|
|
||||||
use ts_rs::TS;
|
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>>>);
|
pub struct DbConnection(pub Arc<Mutex<Option<Connection>>>);
|
||||||
|
|
||||||
const VAULT_EXTENSION: &str = ".db";
|
const VAULT_EXTENSION: &str = ".db";
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use crate::crdt::transformer::CrdtTransformer;
|
|||||||
use crate::crdt::trigger;
|
use crate::crdt::trigger;
|
||||||
use crate::database::core::{parse_sql_statements, ValueConverter};
|
use crate::database::core::{parse_sql_statements, ValueConverter};
|
||||||
use crate::database::error::DatabaseError;
|
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 serde_json::Value as JsonValue;
|
||||||
use sqlparser::ast::Statement;
|
use sqlparser::ast::Statement;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
@ -14,6 +14,62 @@ use std::collections::HashSet;
|
|||||||
pub struct SqlExecutor;
|
pub struct SqlExecutor;
|
||||||
|
|
||||||
impl 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
|
/// Führt SQL aus (mit CRDT-Transformation) - OHNE Permission-Check
|
||||||
pub fn execute_internal(
|
pub fn execute_internal(
|
||||||
tx: &Transaction,
|
tx: &Transaction,
|
||||||
|
|||||||
@ -1,14 +1,17 @@
|
|||||||
|
use crate::table_names::TABLE_EXTENSION_PERMISSIONS;
|
||||||
use crate::AppState;
|
use crate::AppState;
|
||||||
use crate::database::core::with_connection;
|
use crate::database::core::with_connection;
|
||||||
use crate::database::error::DatabaseError;
|
use crate::database::error::DatabaseError;
|
||||||
use crate::extension::database::executor::SqlExecutor;
|
use crate::extension::database::executor::SqlExecutor;
|
||||||
use crate::extension::error::ExtensionError;
|
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;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use tauri::State;
|
use tauri::State;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
use crate::database::generated::HaexExtensionPermissions;
|
||||||
|
use rusqlite::{params, ToSql};
|
||||||
|
|
||||||
pub struct PermissionManager;
|
pub struct PermissionManager;
|
||||||
|
|
||||||
@ -29,31 +32,29 @@ impl PermissionManager {
|
|||||||
reason: "Failed to lock HLC service".to_string(),
|
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 {
|
for perm in permissions {
|
||||||
let resource_type_str = format!("{:?}", perm.resource_type).to_lowercase();
|
// 1. Konvertiere App-Struct zu DB-Struct
|
||||||
let action_str = format!("{:?}", perm.action).to_lowercase();
|
let db_perm: HaexExtensionPermissions = perm.into();
|
||||||
|
|
||||||
let constraints_json = perm
|
// 2. Erstelle typsichere Parameter
|
||||||
.constraints
|
let params = params![
|
||||||
.as_ref()
|
db_perm.id,
|
||||||
.map(|c| serde_json::to_string(c).ok())
|
db_perm.extension_id,
|
||||||
.flatten();
|
db_perm.resource_type,
|
||||||
|
db_perm.action,
|
||||||
let sql = "INSERT INTO haex_extension_permissions
|
db_perm.target,
|
||||||
(id, extension_id, resource_type, action, target, constraints, status)
|
db_perm.constraints,
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?)";
|
db_perm.status,
|
||||||
|
|
||||||
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()),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
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)?;
|
tx.commit().map_err(DatabaseError::from)?;
|
||||||
@ -77,32 +78,24 @@ impl PermissionManager {
|
|||||||
reason: "Failed to lock HLC service".to_string(),
|
reason: "Failed to lock HLC service".to_string(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let resource_type_str = format!("{:?}", permission.resource_type).to_lowercase();
|
let db_perm: HaexExtensionPermissions = permission.into();
|
||||||
let action_str = format!("{:?}", permission.action).to_lowercase();
|
|
||||||
|
let sql = format!(
|
||||||
|
"UPDATE {} SET resource_type = ?, action = ?, target = ?, constraints = ?, status = ? WHERE id = ?",
|
||||||
|
TABLE_EXTENSION_PERMISSIONS
|
||||||
|
);
|
||||||
|
|
||||||
let constraints_json = permission
|
let params = params![
|
||||||
.constraints
|
db_perm.resource_type,
|
||||||
.as_ref()
|
db_perm.action,
|
||||||
.map(|c| serde_json::to_string(c).ok())
|
db_perm.target,
|
||||||
.flatten();
|
db_perm.constraints,
|
||||||
|
db_perm.status,
|
||||||
let sql = "UPDATE haex_extension_permissions
|
db_perm.id,
|
||||||
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),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
SqlExecutor::execute_internal(&tx, &hlc_service, sql, ¶ms)?;
|
SqlExecutor::execute_internal_typed(&tx, &hlc_service, &sql, params)?;
|
||||||
|
tx.commit().map_err(DatabaseError::from)
|
||||||
tx.commit().map_err(DatabaseError::from)?;
|
|
||||||
Ok(())
|
|
||||||
})
|
})
|
||||||
.map_err(ExtensionError::from)
|
.map_err(ExtensionError::from)
|
||||||
}
|
}
|
||||||
@ -123,16 +116,10 @@ impl PermissionManager {
|
|||||||
reason: "Failed to lock HLC service".to_string(),
|
reason: "Failed to lock HLC service".to_string(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let sql = "UPDATE haex_extension_permissions
|
let sql = format!("UPDATE {} SET status = ? WHERE id = ?", TABLE_EXTENSION_PERMISSIONS);
|
||||||
SET status = ?
|
let params = params![new_status.as_str(), permission_id];
|
||||||
WHERE id = ?";
|
SqlExecutor::execute_internal_typed(&tx, &hlc_service, &sql, params)?;
|
||||||
|
tx.commit().map_err(DatabaseError::from)
|
||||||
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(())
|
|
||||||
})
|
})
|
||||||
.map_err(ExtensionError::from)
|
.map_err(ExtensionError::from)
|
||||||
}
|
}
|
||||||
@ -151,14 +138,9 @@ impl PermissionManager {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Echtes DELETE - wird vom CrdtTransformer zu UPDATE umgewandelt
|
// Echtes DELETE - wird vom CrdtTransformer zu UPDATE umgewandelt
|
||||||
let sql = "DELETE FROM haex_extension_permissions WHERE id = ?";
|
let sql = format!("DELETE FROM {} WHERE id = ?", TABLE_EXTENSION_PERMISSIONS);
|
||||||
|
SqlExecutor::execute_internal_typed(&tx, &hlc_service, &sql, params![permission_id])?;
|
||||||
let params = vec![json!(permission_id)];
|
tx.commit().map_err(DatabaseError::from)
|
||||||
|
|
||||||
SqlExecutor::execute_internal(&tx, &hlc_service, sql, ¶ms)?;
|
|
||||||
|
|
||||||
tx.commit().map_err(DatabaseError::from)?;
|
|
||||||
Ok(())
|
|
||||||
}).map_err(ExtensionError::from)
|
}).map_err(ExtensionError::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,15 +157,9 @@ impl PermissionManager {
|
|||||||
reason: "Failed to lock HLC service".to_string(),
|
reason: "Failed to lock HLC service".to_string(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Echtes DELETE - wird vom CrdtTransformer zu UPDATE umgewandelt
|
let sql = format!("DELETE FROM {} WHERE extension_id = ?", TABLE_EXTENSION_PERMISSIONS);
|
||||||
let sql = "DELETE FROM haex_extension_permissions WHERE extension_id = ?";
|
SqlExecutor::execute_internal_typed(&tx, &hlc_service, &sql, params![extension_id])?;
|
||||||
|
tx.commit().map_err(DatabaseError::from)
|
||||||
let params = vec![json!(extension_id)];
|
|
||||||
|
|
||||||
SqlExecutor::execute_internal(&tx, &hlc_service, sql, ¶ms)?;
|
|
||||||
|
|
||||||
tx.commit().map_err(DatabaseError::from)?;
|
|
||||||
Ok(())
|
|
||||||
}).map_err(ExtensionError::from)
|
}).map_err(ExtensionError::from)
|
||||||
}
|
}
|
||||||
/// Lädt alle Permissions einer Extension
|
/// Lädt alle Permissions einer Extension
|
||||||
@ -192,92 +168,23 @@ impl PermissionManager {
|
|||||||
extension_id: &str,
|
extension_id: &str,
|
||||||
) -> Result<Vec<ExtensionPermission>, ExtensionError> {
|
) -> Result<Vec<ExtensionPermission>, ExtensionError> {
|
||||||
with_connection(&app_state.db, |conn| {
|
with_connection(&app_state.db, |conn| {
|
||||||
let sql = "SELECT id, extension_id, resource_type, action, target, constraints, status, haex_timestamp, haex_tombstone
|
let sql = format!("SELECT * FROM {} WHERE extension_id = ?", TABLE_EXTENSION_PERMISSIONS);
|
||||||
FROM haex_extension_permissions
|
let mut stmt = conn.prepare(&sql).map_err(DatabaseError::from)?;
|
||||||
WHERE extension_id = ?";
|
|
||||||
|
|
||||||
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 permissions = perms_iter
|
||||||
let results = SqlExecutor::select_internal(conn, sql, ¶ms)?;
|
.filter_map(Result::ok)
|
||||||
|
.map(Into::into)
|
||||||
// Parse JSON results zu ExtensionPermission
|
.collect();
|
||||||
let permissions = results
|
|
||||||
.into_iter()
|
|
||||||
.map(|row| Self::parse_permission_from_json(row))
|
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
|
||||||
|
|
||||||
Ok(permissions)
|
Ok(permissions)
|
||||||
}).map_err(ExtensionError::from)
|
}).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
|
/// Prüft Datenbankberechtigungen
|
||||||
pub async fn check_database_permission(
|
pub async fn check_database_permission(
|
||||||
@ -311,7 +218,7 @@ impl PermissionManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prüft Dateisystem-Berechtigungen
|
/* /// Prüft Dateisystem-Berechtigungen
|
||||||
pub async fn check_filesystem_permission(
|
pub async fn check_filesystem_permission(
|
||||||
app_state: &State<'_, AppState>,
|
app_state: &State<'_, AppState>,
|
||||||
extension_id: &str,
|
extension_id: &str,
|
||||||
@ -471,9 +378,9 @@ impl PermissionManager {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
// Helper-Methoden - müssen DatabaseError statt ExtensionError zurückgeben
|
// 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 {
|
match s {
|
||||||
"fs" => Ok(ResourceType::Fs),
|
"fs" => Ok(ResourceType::Fs),
|
||||||
"http" => Ok(ResourceType::Http),
|
"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 {
|
fn matches_path_pattern(pattern: &str, path: &str) -> bool {
|
||||||
if pattern.ends_with("/*") {
|
if pattern.ends_with("/*") {
|
||||||
let prefix = &pattern[..pattern.len() - 2];
|
let prefix = &pattern[..pattern.len() - 2];
|
||||||
@ -557,7 +420,7 @@ impl PermissionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convenience-Funktionen für die verschiedenen Subsysteme
|
// Convenience-Funktionen für die verschiedenen Subsysteme
|
||||||
impl PermissionManager {
|
/* impl PermissionManager {
|
||||||
// Convenience-Methoden
|
// Convenience-Methoden
|
||||||
pub async fn can_read_file(
|
pub async fn can_read_file(
|
||||||
app_state: &State<'_, AppState>,
|
app_state: &State<'_, AppState>,
|
||||||
@ -647,4 +510,4 @@ impl PermissionManager {
|
|||||||
.filter(|perm| perm.status == PermissionStatus::Ask)
|
.filter(|perm| perm.status == PermissionStatus::Ask)
|
||||||
.collect())
|
.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)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct ExtensionPermission {
|
pub struct ExtensionPermission {
|
||||||
@ -20,6 +26,52 @@ pub struct ExtensionPermission {
|
|||||||
pub haex_timestamp: Option<String>,
|
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)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum ResourceType {
|
pub enum ResourceType {
|
||||||
@ -29,13 +81,43 @@ pub enum ResourceType {
|
|||||||
Shell,
|
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")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
Read,
|
Read,
|
||||||
Write,
|
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)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum PermissionStatus {
|
pub enum PermissionStatus {
|
||||||
@ -125,7 +207,7 @@ pub struct EditablePermissions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Oder gruppiert nach Typ:
|
// Oder gruppiert nach Typ:
|
||||||
impl EditablePermissions {
|
/* impl EditablePermissions {
|
||||||
pub fn database_permissions(&self) -> Vec<&ExtensionPermission> {
|
pub fn database_permissions(&self) -> Vec<&ExtensionPermission> {
|
||||||
self.permissions
|
self.permissions
|
||||||
.iter()
|
.iter()
|
||||||
@ -153,4 +235,40 @@ impl EditablePermissions {
|
|||||||
.filter(|p| p.resource_type == ResourceType::Shell)
|
.filter(|p| p.resource_type == ResourceType::Shell)
|
||||||
.collect()
|
.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