mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-16 22:20:51 +01:00
add desktop
This commit is contained in:
9
.claude-session.json
Normal file
9
.claude-session.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"lastUpdated": "2025-10-16T00:00:00.000Z",
|
||||||
|
"todos": [],
|
||||||
|
"context": {
|
||||||
|
"description": "Session context file for Claude Code. This file is automatically updated to persist state across sessions.",
|
||||||
|
"currentFocus": null,
|
||||||
|
"notes": []
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "tauri-app",
|
"name": "haex-hub",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
9
src-tauri/database/migrations/0003_daily_polaris.sql
Normal file
9
src-tauri/database/migrations/0003_daily_polaris.sql
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
CREATE TABLE `haex_desktop_items` (
|
||||||
|
`id` text PRIMARY KEY NOT NULL,
|
||||||
|
`item_type` text NOT NULL,
|
||||||
|
`reference_id` text NOT NULL,
|
||||||
|
`position_x` integer DEFAULT 0 NOT NULL,
|
||||||
|
`position_y` integer DEFAULT 0 NOT NULL,
|
||||||
|
`haex_tombstone` integer,
|
||||||
|
`haex_timestamp` text
|
||||||
|
);
|
||||||
991
src-tauri/database/migrations/meta/0003_snapshot.json
Normal file
991
src-tauri/database/migrations/meta/0003_snapshot.json
Normal file
@ -0,0 +1,991 @@
|
|||||||
|
{
|
||||||
|
"version": "6",
|
||||||
|
"dialect": "sqlite",
|
||||||
|
"id": "2f40a42e-9b3f-42be-8951-8e94baadcd65",
|
||||||
|
"prevId": "5387568f-75b3-4a85-86c5-67f539c3fedf",
|
||||||
|
"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_desktop_items": {
|
||||||
|
"name": "haex_desktop_items",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"item_type": {
|
||||||
|
"name": "item_type",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"reference_id": {
|
||||||
|
"name": "reference_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"position_x": {
|
||||||
|
"name": "position_x",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"position_y": {
|
||||||
|
"name": "position_y",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"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_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
|
||||||
|
},
|
||||||
|
"public_key": {
|
||||||
|
"name": "public_key",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"name": "version",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"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": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'index.html'"
|
||||||
|
},
|
||||||
|
"homepage": {
|
||||||
|
"name": "homepage",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"enabled": {
|
||||||
|
"name": "enabled",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"icon": {
|
||||||
|
"name": "icon",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"signature": {
|
||||||
|
"name": "signature",
|
||||||
|
"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": {
|
||||||
|
"haex_extensions_public_key_name_unique": {
|
||||||
|
"name": "haex_extensions_public_key_name_unique",
|
||||||
|
"columns": [
|
||||||
|
"public_key",
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"isUnique": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -22,6 +22,13 @@
|
|||||||
"when": 1760272083150,
|
"when": 1760272083150,
|
||||||
"tag": "0002_amazing_iron_fist",
|
"tag": "0002_amazing_iron_fist",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 3,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1760611690801,
|
||||||
|
"tag": "0003_daily_polaris",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -5,9 +5,20 @@ import {
|
|||||||
text,
|
text,
|
||||||
unique,
|
unique,
|
||||||
type AnySQLiteColumn,
|
type AnySQLiteColumn,
|
||||||
|
type SQLiteColumnBuilderBase,
|
||||||
} from 'drizzle-orm/sqlite-core'
|
} from 'drizzle-orm/sqlite-core'
|
||||||
import tableNames from '../tableNames.json'
|
import tableNames from '../tableNames.json'
|
||||||
|
|
||||||
|
// Helper function to add common CRDT columns (haexTombstone and haexTimestamp)
|
||||||
|
export const withCrdtColumns = <T extends Record<string, SQLiteColumnBuilderBase>>(
|
||||||
|
columns: T,
|
||||||
|
columnNames: { haexTombstone: string; haexTimestamp: string },
|
||||||
|
) => ({
|
||||||
|
...columns,
|
||||||
|
haexTombstone: integer(columnNames.haexTombstone, { mode: 'boolean' }),
|
||||||
|
haexTimestamp: text(columnNames.haexTimestamp),
|
||||||
|
})
|
||||||
|
|
||||||
export const haexSettings = sqliteTable(tableNames.haex.settings.name, {
|
export const haexSettings = sqliteTable(tableNames.haex.settings.name, {
|
||||||
id: text()
|
id: text()
|
||||||
.primaryKey()
|
.primaryKey()
|
||||||
@ -120,3 +131,27 @@ export const haexNotifications = sqliteTable(
|
|||||||
)
|
)
|
||||||
export type InsertHaexNotifications = typeof haexNotifications.$inferInsert
|
export type InsertHaexNotifications = typeof haexNotifications.$inferInsert
|
||||||
export type SelectHaexNotifications = typeof haexNotifications.$inferSelect
|
export type SelectHaexNotifications = typeof haexNotifications.$inferSelect
|
||||||
|
|
||||||
|
export const haexDesktopItems = sqliteTable(
|
||||||
|
tableNames.haex.desktop_items.name,
|
||||||
|
withCrdtColumns(
|
||||||
|
{
|
||||||
|
id: text(tableNames.haex.desktop_items.columns.id)
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => crypto.randomUUID()),
|
||||||
|
itemType: text(tableNames.haex.desktop_items.columns.itemType, {
|
||||||
|
enum: ['extension', 'file', 'folder'],
|
||||||
|
}).notNull(),
|
||||||
|
referenceId: text(tableNames.haex.desktop_items.columns.referenceId).notNull(), // extensionId für extensions, filePath für files/folders
|
||||||
|
positionX: integer(tableNames.haex.desktop_items.columns.positionX)
|
||||||
|
.notNull()
|
||||||
|
.default(0),
|
||||||
|
positionY: integer(tableNames.haex.desktop_items.columns.positionY)
|
||||||
|
.notNull()
|
||||||
|
.default(0),
|
||||||
|
},
|
||||||
|
tableNames.haex.desktop_items.columns,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
export type InsertHaexDesktopItems = typeof haexDesktopItems.$inferInsert
|
||||||
|
export type SelectHaexDesktopItems = typeof haexDesktopItems.$inferSelect
|
||||||
|
|||||||
@ -63,6 +63,18 @@
|
|||||||
"haexTimestamp": "haex_timestamp"
|
"haexTimestamp": "haex_timestamp"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"desktop_items": {
|
||||||
|
"name": "haex_desktop_items",
|
||||||
|
"columns": {
|
||||||
|
"id": "id",
|
||||||
|
"itemType": "item_type",
|
||||||
|
"referenceId": "reference_id",
|
||||||
|
"positionX": "position_x",
|
||||||
|
"positionY": "position_y",
|
||||||
|
"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",
|
||||||
|
|||||||
Binary file not shown.
@ -12,7 +12,6 @@ use serde_json::Value as JsonValue;
|
|||||||
use sqlparser::ast::{Expr, Query, Select, SetExpr, Statement, TableFactor, TableObject};
|
use sqlparser::ast::{Expr, Query, Select, SetExpr, Statement, TableFactor, TableObject};
|
||||||
use sqlparser::dialect::SQLiteDialect;
|
use sqlparser::dialect::SQLiteDialect;
|
||||||
use sqlparser::parser::Parser;
|
use sqlparser::parser::Parser;
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
/// Öffnet und initialisiert eine Datenbank mit Verschlüsselung
|
/// Öffnet und initialisiert eine Datenbank mit Verschlüsselung
|
||||||
///
|
///
|
||||||
@ -121,7 +120,7 @@ pub fn execute(
|
|||||||
sql: String,
|
sql: String,
|
||||||
params: Vec<JsonValue>,
|
params: Vec<JsonValue>,
|
||||||
connection: &DbConnection,
|
connection: &DbConnection,
|
||||||
) -> Result<usize, DatabaseError> {
|
) -> Result<Vec<Vec<JsonValue>>, DatabaseError> {
|
||||||
// Konvertiere Parameter
|
// Konvertiere Parameter
|
||||||
let params_converted: Vec<RusqliteValue> = params
|
let params_converted: Vec<RusqliteValue> = params
|
||||||
.iter()
|
.iter()
|
||||||
@ -130,19 +129,56 @@ pub fn execute(
|
|||||||
let params_sql: Vec<&dyn ToSql> = params_converted.iter().map(|v| v as &dyn ToSql).collect();
|
let params_sql: Vec<&dyn ToSql> = params_converted.iter().map(|v| v as &dyn ToSql).collect();
|
||||||
|
|
||||||
with_connection(connection, |conn| {
|
with_connection(connection, |conn| {
|
||||||
let affected_rows = conn.execute(&sql, ¶ms_sql[..]).map_err(|e| {
|
// Check if the SQL contains RETURNING clause
|
||||||
// "Lazy Parsing": Extrahiere den Tabellennamen nur, wenn ein Fehler auftritt,
|
let has_returning = sql.to_uppercase().contains("RETURNING");
|
||||||
// um den Overhead bei erfolgreichen Operationen zu vermeiden.
|
|
||||||
let table_name = extract_primary_table_name_from_sql(&sql).unwrap_or(None);
|
|
||||||
|
|
||||||
DatabaseError::ExecutionError {
|
if has_returning {
|
||||||
sql: sql.clone(),
|
// Use prepare + query for RETURNING statements
|
||||||
|
let mut stmt = conn.prepare(&sql).map_err(|e| DatabaseError::PrepareError {
|
||||||
reason: e.to_string(),
|
reason: e.to_string(),
|
||||||
table: table_name,
|
})?;
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(affected_rows)
|
let num_columns = stmt.column_count();
|
||||||
|
let mut rows = stmt
|
||||||
|
.query(¶ms_sql[..])
|
||||||
|
.map_err(|e| DatabaseError::QueryError {
|
||||||
|
reason: e.to_string(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut result_vec: Vec<Vec<JsonValue>> = Vec::new();
|
||||||
|
|
||||||
|
while let Some(row) = rows.next().map_err(|e| DatabaseError::RowProcessingError {
|
||||||
|
reason: format!("Row iteration error: {}", e),
|
||||||
|
})? {
|
||||||
|
let mut row_values: Vec<JsonValue> = Vec::with_capacity(num_columns);
|
||||||
|
|
||||||
|
for i in 0..num_columns {
|
||||||
|
let value_ref = row.get_ref(i).map_err(|e| {
|
||||||
|
DatabaseError::RowProcessingError {
|
||||||
|
reason: format!("Failed to get column {}: {}", i, e),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let json_val = convert_value_ref_to_json(value_ref)?;
|
||||||
|
row_values.push(json_val);
|
||||||
|
}
|
||||||
|
result_vec.push(row_values);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result_vec)
|
||||||
|
} else {
|
||||||
|
// For non-RETURNING statements, just execute and return empty array
|
||||||
|
conn.execute(&sql, ¶ms_sql[..]).map_err(|e| {
|
||||||
|
let table_name = extract_primary_table_name_from_sql(&sql).unwrap_or(None);
|
||||||
|
DatabaseError::ExecutionError {
|
||||||
|
sql: sql.clone(),
|
||||||
|
reason: e.to_string(),
|
||||||
|
table: table_name,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +186,7 @@ pub fn select(
|
|||||||
sql: String,
|
sql: String,
|
||||||
params: Vec<JsonValue>,
|
params: Vec<JsonValue>,
|
||||||
connection: &DbConnection,
|
connection: &DbConnection,
|
||||||
) -> Result<Vec<HashMap<String, JsonValue>>, DatabaseError> {
|
) -> Result<Vec<Vec<JsonValue>>, DatabaseError> {
|
||||||
// Validiere SQL-Statement
|
// Validiere SQL-Statement
|
||||||
let statement = parse_single_statement(&sql)?;
|
let statement = parse_single_statement(&sql)?;
|
||||||
|
|
||||||
@ -176,12 +212,7 @@ pub fn select(
|
|||||||
reason: e.to_string(),
|
reason: e.to_string(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let column_names: Vec<String> = stmt
|
let num_columns = stmt.column_count();
|
||||||
.column_names()
|
|
||||||
.into_iter()
|
|
||||||
.map(|s| s.to_string())
|
|
||||||
.collect();
|
|
||||||
let num_columns = column_names.len();
|
|
||||||
|
|
||||||
let mut rows = stmt
|
let mut rows = stmt
|
||||||
.query(¶ms_sql[..])
|
.query(¶ms_sql[..])
|
||||||
@ -189,34 +220,24 @@ pub fn select(
|
|||||||
reason: e.to_string(),
|
reason: e.to_string(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let mut result_vec: Vec<HashMap<String, JsonValue>> = Vec::new();
|
let mut result_vec: Vec<Vec<JsonValue>> = Vec::new();
|
||||||
|
|
||||||
while let Some(row) = rows.next().map_err(|e| DatabaseError::RowProcessingError {
|
while let Some(row) = rows.next().map_err(|e| DatabaseError::RowProcessingError {
|
||||||
reason: format!("Row iteration error: {}", e),
|
reason: format!("Row iteration error: {}", e),
|
||||||
})? {
|
})? {
|
||||||
let mut row_map: HashMap<String, JsonValue> = HashMap::with_capacity(num_columns);
|
let mut row_values: Vec<JsonValue> = Vec::with_capacity(num_columns);
|
||||||
|
|
||||||
for i in 0..num_columns {
|
for i in 0..num_columns {
|
||||||
let col_name = &column_names[i];
|
|
||||||
|
|
||||||
/* println!(
|
|
||||||
"--- Processing Column --- Index: {}, Name: '{}'",
|
|
||||||
i, col_name
|
|
||||||
); */
|
|
||||||
|
|
||||||
let value_ref = row
|
let value_ref = row
|
||||||
.get_ref(i)
|
.get_ref(i)
|
||||||
.map_err(|e| DatabaseError::RowProcessingError {
|
.map_err(|e| DatabaseError::RowProcessingError {
|
||||||
reason: format!("Failed to get column {} ('{}'): {}", i, col_name, e),
|
reason: format!("Failed to get column {}: {}", i, e),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let json_val = convert_value_ref_to_json(value_ref)?;
|
let json_val = convert_value_ref_to_json(value_ref)?;
|
||||||
|
row_values.push(json_val);
|
||||||
//println!("Column: {} = {}", column_names[i], json_val);
|
|
||||||
|
|
||||||
row_map.insert(col_name.clone(), json_val);
|
|
||||||
}
|
}
|
||||||
result_vec.push(row_map);
|
result_vec.push(row_values);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(result_vec)
|
Ok(result_vec)
|
||||||
|
|||||||
@ -11,7 +11,6 @@ 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;
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::time::UNIX_EPOCH;
|
use std::time::UNIX_EPOCH;
|
||||||
@ -30,7 +29,7 @@ pub fn sql_select(
|
|||||||
sql: String,
|
sql: String,
|
||||||
params: Vec<JsonValue>,
|
params: Vec<JsonValue>,
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
) -> Result<Vec<HashMap<String, JsonValue>>, DatabaseError> {
|
) -> Result<Vec<Vec<JsonValue>>, DatabaseError> {
|
||||||
core::select(sql, params, &state.db)
|
core::select(sql, params, &state.db)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +38,7 @@ pub fn sql_execute(
|
|||||||
sql: String,
|
sql: String,
|
||||||
params: Vec<JsonValue>,
|
params: Vec<JsonValue>,
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
) -> Result<usize, DatabaseError> {
|
) -> Result<Vec<Vec<JsonValue>>, DatabaseError> {
|
||||||
core::execute(sql, params, &state.db)
|
core::execute(sql, params, &state.db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
119
src/components/haex/desktop/icon.vue
Normal file
119
src/components/haex/desktop/icon.vue
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
<template>
|
||||||
|
<UContextMenu :items="contextMenuItems">
|
||||||
|
<div
|
||||||
|
ref="draggableEl"
|
||||||
|
:style="style"
|
||||||
|
class="select-none cursor-grab active:cursor-grabbing"
|
||||||
|
@pointerdown="handlePointerDown"
|
||||||
|
@pointermove="handlePointerMove"
|
||||||
|
@pointerup="handlePointerUp"
|
||||||
|
@dblclick="handleOpen"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col items-center gap-1 p-2">
|
||||||
|
<div
|
||||||
|
class="w-16 h-16 flex items-center justify-center bg-white/90 dark:bg-gray-800/90 rounded-lg shadow-lg hover:shadow-xl transition-shadow"
|
||||||
|
>
|
||||||
|
<img v-if="icon" :src="icon" :alt="label" class="w-12 h-12 object-contain" />
|
||||||
|
<Icon v-else name="i-heroicons-puzzle-piece-solid" class="w-12 h-12 text-gray-500" />
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
class="text-xs text-center max-w-20 truncate bg-white/80 dark:bg-gray-800/80 px-2 py-1 rounded shadow"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</UContextMenu>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const props = defineProps<{
|
||||||
|
id: string
|
||||||
|
itemType: 'extension' | 'file' | 'folder'
|
||||||
|
referenceId: string
|
||||||
|
initialX: number
|
||||||
|
initialY: number
|
||||||
|
label: string
|
||||||
|
icon?: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
positionChanged: [id: string, x: number, y: number]
|
||||||
|
open: [itemType: string, referenceId: string]
|
||||||
|
uninstall: [itemType: string, referenceId: string]
|
||||||
|
dragStart: [id: string, itemType: string, referenceId: string]
|
||||||
|
dragEnd: []
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const desktopStore = useDesktopStore()
|
||||||
|
|
||||||
|
const contextMenuItems = computed(() =>
|
||||||
|
desktopStore.getContextMenuItems(
|
||||||
|
props.id,
|
||||||
|
props.itemType,
|
||||||
|
props.referenceId,
|
||||||
|
handleOpen,
|
||||||
|
handleUninstall,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
const draggableEl = ref<HTMLElement>()
|
||||||
|
const x = ref(props.initialX)
|
||||||
|
const y = ref(props.initialY)
|
||||||
|
const isDragging = ref(false)
|
||||||
|
const offsetX = ref(0)
|
||||||
|
const offsetY = ref(0)
|
||||||
|
|
||||||
|
const style = computed(() => ({
|
||||||
|
position: 'absolute' as const,
|
||||||
|
left: `${x.value}px`,
|
||||||
|
top: `${y.value}px`,
|
||||||
|
touchAction: 'none' as const,
|
||||||
|
}))
|
||||||
|
|
||||||
|
const handlePointerDown = (e: PointerEvent) => {
|
||||||
|
if (!draggableEl.value || !draggableEl.value.parentElement) return
|
||||||
|
|
||||||
|
isDragging.value = true
|
||||||
|
emit('dragStart', props.id, props.itemType, props.referenceId)
|
||||||
|
|
||||||
|
// Get parent offset to convert from viewport coordinates to parent-relative coordinates
|
||||||
|
const parentRect = draggableEl.value.parentElement.getBoundingClientRect()
|
||||||
|
|
||||||
|
// Calculate offset from mouse position to current element position (in parent coordinates)
|
||||||
|
offsetX.value = e.clientX - parentRect.left - x.value
|
||||||
|
offsetY.value = e.clientY - parentRect.top - y.value
|
||||||
|
|
||||||
|
draggableEl.value.setPointerCapture(e.pointerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePointerMove = (e: PointerEvent) => {
|
||||||
|
if (!isDragging.value || !draggableEl.value?.parentElement) return
|
||||||
|
|
||||||
|
const parentRect = draggableEl.value.parentElement.getBoundingClientRect()
|
||||||
|
const newX = e.clientX - parentRect.left - offsetX.value
|
||||||
|
const newY = e.clientY - parentRect.top - offsetY.value
|
||||||
|
|
||||||
|
x.value = newX
|
||||||
|
y.value = newY
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePointerUp = (e: PointerEvent) => {
|
||||||
|
if (!isDragging.value) return
|
||||||
|
|
||||||
|
isDragging.value = false
|
||||||
|
if (draggableEl.value) {
|
||||||
|
draggableEl.value.releasePointerCapture(e.pointerId)
|
||||||
|
}
|
||||||
|
emit('dragEnd')
|
||||||
|
emit('positionChanged', props.id, x.value, y.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOpen = () => {
|
||||||
|
emit('open', props.itemType, props.referenceId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleUninstall = () => {
|
||||||
|
emit('uninstall', props.itemType, props.referenceId)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
250
src/components/haex/desktop/index.vue
Normal file
250
src/components/haex/desktop/index.vue
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="w-full h-full relative overflow-hidden bg-gradient-to-br from-blue-50 to-blue-100 dark:from-gray-900 dark:to-gray-800"
|
||||||
|
>
|
||||||
|
<!-- Dropzones (only visible during drag) -->
|
||||||
|
<Transition name="slide-down">
|
||||||
|
<div
|
||||||
|
v-if="isDragging"
|
||||||
|
class="absolute top-0 left-0 right-0 flex gap-2 p-4 z-50"
|
||||||
|
>
|
||||||
|
<!-- Remove from Desktop Dropzone -->
|
||||||
|
<div
|
||||||
|
ref="removeDropzoneEl"
|
||||||
|
class="flex-1 h-20 flex items-center justify-center gap-2 rounded-lg border-2 border-dashed transition-all"
|
||||||
|
:class="
|
||||||
|
isOverRemoveZone
|
||||||
|
? 'bg-orange-500/20 border-orange-500 dark:bg-orange-400/20 dark:border-orange-400'
|
||||||
|
: 'border-orange-500/50 dark:border-orange-400/50'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name="i-heroicons-x-mark"
|
||||||
|
class="w-6 h-6"
|
||||||
|
:class="
|
||||||
|
isOverRemoveZone
|
||||||
|
? 'text-orange-700 dark:text-orange-300'
|
||||||
|
: 'text-orange-600 dark:text-orange-400'
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
class="font-semibold"
|
||||||
|
:class="
|
||||||
|
isOverRemoveZone
|
||||||
|
? 'text-orange-700 dark:text-orange-300'
|
||||||
|
: 'text-orange-600 dark:text-orange-400'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Von Desktop entfernen
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Uninstall Dropzone -->
|
||||||
|
<div
|
||||||
|
ref="uninstallDropzoneEl"
|
||||||
|
class="flex-1 h-20 flex items-center justify-center gap-2 rounded-lg border-2 border-dashed transition-all"
|
||||||
|
:class="
|
||||||
|
isOverUninstallZone
|
||||||
|
? 'bg-red-500/20 border-red-500 dark:bg-red-400/20 dark:border-red-400'
|
||||||
|
: 'border-red-500/50 dark:border-red-400/50'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name="i-heroicons-trash"
|
||||||
|
class="w-6 h-6"
|
||||||
|
:class="
|
||||||
|
isOverUninstallZone
|
||||||
|
? 'text-red-700 dark:text-red-300'
|
||||||
|
: 'text-red-600 dark:text-red-400'
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
class="font-semibold"
|
||||||
|
:class="
|
||||||
|
isOverUninstallZone
|
||||||
|
? 'text-red-700 dark:text-red-300'
|
||||||
|
: 'text-red-600 dark:text-red-400'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Deinstallieren
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
|
||||||
|
<HaexDesktopIcon
|
||||||
|
v-for="item in desktopItemIcons"
|
||||||
|
:key="item.id"
|
||||||
|
:id="item.id"
|
||||||
|
:item-type="item.itemType"
|
||||||
|
:reference-id="item.referenceId"
|
||||||
|
:initial-x="item.positionX"
|
||||||
|
:initial-y="item.positionY"
|
||||||
|
:label="item.label"
|
||||||
|
:icon="item.icon"
|
||||||
|
@position-changed="handlePositionChanged"
|
||||||
|
@open="handleOpen"
|
||||||
|
@drag-start="handleDragStart"
|
||||||
|
@drag-end="handleDragEnd"
|
||||||
|
@uninstall="handleUninstall"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const desktopStore = useDesktopStore()
|
||||||
|
const extensionsStore = useExtensionsStore()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const { desktopItems } = storeToRefs(desktopStore)
|
||||||
|
const { availableExtensions } = storeToRefs(extensionsStore)
|
||||||
|
|
||||||
|
// Drag state
|
||||||
|
const isDragging = ref(false)
|
||||||
|
const currentDraggedItemId = ref<string>()
|
||||||
|
const currentDraggedItemType = ref<string>()
|
||||||
|
const currentDraggedReferenceId = ref<string>()
|
||||||
|
|
||||||
|
// Dropzone refs
|
||||||
|
const removeDropzoneEl = ref<HTMLElement>()
|
||||||
|
const uninstallDropzoneEl = ref<HTMLElement>()
|
||||||
|
|
||||||
|
// Setup dropzones with VueUse
|
||||||
|
const { isOverDropZone: isOverRemoveZone } = useDropZone(removeDropzoneEl, {
|
||||||
|
onDrop: () => {
|
||||||
|
if (currentDraggedItemId.value) {
|
||||||
|
handleRemoveFromDesktop(currentDraggedItemId.value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const { isOverDropZone: isOverUninstallZone } = useDropZone(uninstallDropzoneEl, {
|
||||||
|
onDrop: () => {
|
||||||
|
if (currentDraggedItemType.value && currentDraggedReferenceId.value) {
|
||||||
|
handleUninstall(currentDraggedItemType.value, currentDraggedReferenceId.value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
interface DesktopItemIcon extends IDesktopItem {
|
||||||
|
label: string
|
||||||
|
icon?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const desktopItemIcons = computed<DesktopItemIcon[]>(() => {
|
||||||
|
return desktopItems.value.map((item) => {
|
||||||
|
if (item.itemType === 'extension') {
|
||||||
|
const extension = availableExtensions.value.find(
|
||||||
|
(ext) => ext.id === item.referenceId,
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
label: extension?.name || 'Unknown',
|
||||||
|
icon: extension?.icon || '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.itemType === 'file') {
|
||||||
|
// Für später: file handling
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
label: item.referenceId,
|
||||||
|
icon: undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.itemType === 'folder') {
|
||||||
|
// Für später: folder handling
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
label: item.referenceId,
|
||||||
|
icon: undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
label: item.referenceId,
|
||||||
|
icon: undefined,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const handlePositionChanged = async (id: string, x: number, y: number) => {
|
||||||
|
try {
|
||||||
|
await desktopStore.updateDesktopItemPositionAsync(id, x, y)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Speichern der Position:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const localePath = useLocalePath()
|
||||||
|
|
||||||
|
const handleOpen = (itemType: string, referenceId: string) => {
|
||||||
|
if (itemType === 'extension') {
|
||||||
|
router.push(
|
||||||
|
localePath({
|
||||||
|
name: 'extension',
|
||||||
|
params: { extensionId: referenceId },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Für später: file und folder handling
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDragStart = (id: string, itemType: string, referenceId: string) => {
|
||||||
|
isDragging.value = true
|
||||||
|
currentDraggedItemId.value = id
|
||||||
|
currentDraggedItemType.value = itemType
|
||||||
|
currentDraggedReferenceId.value = referenceId
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDragEnd = () => {
|
||||||
|
isDragging.value = false
|
||||||
|
currentDraggedItemId.value = undefined
|
||||||
|
currentDraggedItemType.value = undefined
|
||||||
|
currentDraggedReferenceId.value = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleUninstall = async (itemType: string, referenceId: string) => {
|
||||||
|
if (itemType === 'extension') {
|
||||||
|
try {
|
||||||
|
const extension = availableExtensions.value.find((ext) => ext.id === referenceId)
|
||||||
|
if (extension) {
|
||||||
|
await extensionsStore.removeExtensionAsync(
|
||||||
|
extension.publicKey,
|
||||||
|
extension.name,
|
||||||
|
extension.version,
|
||||||
|
)
|
||||||
|
// Reload extensions after uninstall
|
||||||
|
await extensionsStore.loadExtensionsAsync()
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Deinstallieren:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Für später: file und folder handling
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await desktopStore.loadDesktopItemsAsync()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.slide-down-enter-active,
|
||||||
|
.slide-down-leave-active {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-down-enter-from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-down-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-100%);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -67,6 +67,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</UCard>
|
</UCard>
|
||||||
|
|
||||||
|
<!-- Add to Desktop Option -->
|
||||||
|
<UCheckbox
|
||||||
|
v-model="addToDesktop"
|
||||||
|
:label="t('addToDesktop')"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- Permissions Section -->
|
<!-- Permissions Section -->
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<h4 class="text-lg font-semibold">
|
<h4 class="text-lg font-semibold">
|
||||||
@ -140,6 +146,7 @@ const open = defineModel<boolean>('open', { default: false })
|
|||||||
const preview = defineModel<ExtensionPreview | null>('preview', {
|
const preview = defineModel<ExtensionPreview | null>('preview', {
|
||||||
default: null,
|
default: null,
|
||||||
})
|
})
|
||||||
|
const addToDesktop = ref(true)
|
||||||
|
|
||||||
const databasePermissions = computed({
|
const databasePermissions = computed({
|
||||||
get: () => preview.value?.editable_permissions?.database || [],
|
get: () => preview.value?.editable_permissions?.database || [],
|
||||||
@ -217,7 +224,10 @@ const permissionAccordionItems = computed(() => {
|
|||||||
return items
|
return items
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['deny', 'confirm'])
|
const emit = defineEmits<{
|
||||||
|
deny: []
|
||||||
|
confirm: [addToDesktop: boolean]
|
||||||
|
}>()
|
||||||
|
|
||||||
const onDeny = () => {
|
const onDeny = () => {
|
||||||
open.value = false
|
open.value = false
|
||||||
@ -226,7 +236,7 @@ const onDeny = () => {
|
|||||||
|
|
||||||
const onConfirm = () => {
|
const onConfirm = () => {
|
||||||
open.value = false
|
open.value = false
|
||||||
emit('confirm')
|
emit('confirm', addToDesktop.value)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -235,6 +245,7 @@ de:
|
|||||||
title: Erweiterung installieren
|
title: Erweiterung installieren
|
||||||
version: Version
|
version: Version
|
||||||
author: Autor
|
author: Autor
|
||||||
|
addToDesktop: Zum Desktop hinzufügen
|
||||||
signature:
|
signature:
|
||||||
valid: Signatur verifiziert
|
valid: Signatur verifiziert
|
||||||
invalid: Signatur ungültig
|
invalid: Signatur ungültig
|
||||||
@ -249,6 +260,7 @@ en:
|
|||||||
title: Install Extension
|
title: Install Extension
|
||||||
version: Version
|
version: Version
|
||||||
author: Author
|
author: Author
|
||||||
|
addToDesktop: Add to Desktop
|
||||||
signature:
|
signature:
|
||||||
valid: Signature verified
|
valid: Signature verified
|
||||||
invalid: Invalid signature
|
invalid: Invalid signature
|
||||||
|
|||||||
@ -422,7 +422,7 @@ async function handleContextMethodAsync(request: ExtensionRequest) {
|
|||||||
return {
|
return {
|
||||||
theme: contextGetters.getTheme(),
|
theme: contextGetters.getTheme(),
|
||||||
locale: contextGetters.getLocale(),
|
locale: contextGetters.getLocale(),
|
||||||
platform: detectPlatform(),
|
platform: contextGetters.getPlatform(),
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -430,13 +430,6 @@ async function handleContextMethodAsync(request: ExtensionRequest) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function detectPlatform(): 'desktop' | 'mobile' | 'tablet' {
|
|
||||||
const width = window.innerWidth
|
|
||||||
if (width < 768) return 'mobile'
|
|
||||||
if (width < 1024) return 'tablet'
|
|
||||||
return 'desktop'
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// Storage Methods
|
// Storage Methods
|
||||||
// ==========================================
|
// ==========================================
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="">
|
<div class="flex flex-col w-full h-full overflow-hidden">
|
||||||
<!-- class="" -->
|
|
||||||
<UPageHeader
|
<UPageHeader
|
||||||
as="header"
|
as="header"
|
||||||
:ui="{
|
:ui="{
|
||||||
@ -33,7 +32,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</UPageHeader>
|
</UPageHeader>
|
||||||
|
|
||||||
<main class="overflow-scroll flex bg-elevated">
|
<main class="flex-1 overflow-hidden bg-elevated">
|
||||||
<NuxtPage />
|
<NuxtPage />
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="bg-default isolate h-dvh">
|
<div class="bg-default isolate w-dvw h-dvh flex flex-col">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -100,7 +100,11 @@ const { syncLastVaultsAsync, removeVaultAsync } = useLastVaultStore()
|
|||||||
const { lastVaults } = storeToRefs(useLastVaultStore())
|
const { lastVaults } = storeToRefs(useLastVaultStore())
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await syncLastVaultsAsync()
|
try {
|
||||||
|
await syncLastVaultsAsync()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('ERROR: ', error)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const onSelectLocale = async (locale: Locale) => {
|
const onSelectLocale = async (locale: Locale) => {
|
||||||
|
|||||||
@ -1,10 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div class="w-full h-full overflow-y-auto">
|
||||||
:ui="{
|
|
||||||
root: ['h-full w-full bg-elevated lg:flex'],
|
|
||||||
center: ['h-full w-full'],
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<NuxtLayout name="app">
|
<NuxtLayout name="app">
|
||||||
<NuxtPage />
|
<NuxtPage />
|
||||||
</NuxtLayout>
|
</NuxtLayout>
|
||||||
@ -57,14 +52,18 @@ const { setDeviceIdIfNotExistsAsync, addDeviceNameAsync } = useDeviceStore()
|
|||||||
const { deviceId } = storeToRefs(useDeviceStore())
|
const { deviceId } = storeToRefs(useDeviceStore())
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await setDeviceIdIfNotExistsAsync()
|
try {
|
||||||
await loadExtensionsAsync()
|
await setDeviceIdIfNotExistsAsync()
|
||||||
await readNotificationsAsync()
|
await loadExtensionsAsync()
|
||||||
|
await readNotificationsAsync()
|
||||||
|
|
||||||
if (!(await isKnownDeviceAsync())) {
|
if (!(await isKnownDeviceAsync())) {
|
||||||
console.log('not known device')
|
console.log('not known device')
|
||||||
newDeviceName.value = hostname.value ?? 'unknown'
|
newDeviceName.value = hostname.value ?? 'unknown'
|
||||||
showNewDeviceDialog.value = true
|
showNewDeviceDialog.value = true
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('vault mount error:', error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -206,7 +206,7 @@ import {
|
|||||||
} from '~/config/constants'
|
} from '~/config/constants'
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
name: 'haexExtension',
|
name: 'extension',
|
||||||
})
|
})
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|||||||
@ -104,7 +104,7 @@
|
|||||||
<HaexExtensionDialogInstall
|
<HaexExtensionDialogInstall
|
||||||
v-model:open="showConfirmation"
|
v-model:open="showConfirmation"
|
||||||
:preview="preview"
|
:preview="preview"
|
||||||
@confirm="addExtensionAsync"
|
@confirm="(addToDesktop) => addExtensionAsync(addToDesktop)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<HaexExtensionDialogRemove
|
<HaexExtensionDialogRemove
|
||||||
@ -130,6 +130,7 @@ definePageMeta({
|
|||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const extensionStore = useExtensionsStore()
|
const extensionStore = useExtensionsStore()
|
||||||
|
const desktopStore = useDesktopStore()
|
||||||
|
|
||||||
const showConfirmation = ref(false)
|
const showConfirmation = ref(false)
|
||||||
const openOverwriteDialog = ref(false)
|
const openOverwriteDialog = ref(false)
|
||||||
@ -388,18 +389,23 @@ const onSelectExtensionAsync = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const addExtensionAsync = async () => {
|
const addExtensionAsync = async (addToDesktop: boolean = false) => {
|
||||||
try {
|
try {
|
||||||
console.log(
|
console.log(
|
||||||
'preview.value?.editable_permissions',
|
'preview.value?.editable_permissions',
|
||||||
preview.value?.editable_permissions,
|
preview.value?.editable_permissions,
|
||||||
)
|
)
|
||||||
await extensionStore.installAsync(
|
const extensionId = await extensionStore.installAsync(
|
||||||
extension.path,
|
extension.path,
|
||||||
preview.value?.editable_permissions,
|
preview.value?.editable_permissions,
|
||||||
)
|
)
|
||||||
await extensionStore.loadExtensionsAsync()
|
await extensionStore.loadExtensionsAsync()
|
||||||
|
|
||||||
|
// Add to desktop if requested
|
||||||
|
if (addToDesktop && extensionId) {
|
||||||
|
await desktopStore.addDesktopItemAsync('extension', extensionId, 50, 50)
|
||||||
|
}
|
||||||
|
|
||||||
add({
|
add({
|
||||||
color: 'success',
|
color: 'success',
|
||||||
title: t('extension.success.title', {
|
title: t('extension.success.title', {
|
||||||
|
|||||||
@ -1,11 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="w-full">
|
<div class="w-full h-full flex items-center justify-center">
|
||||||
<div class="h-screen bg-amber-300 flex-1 flex-wrap">
|
<HaexDesktop />
|
||||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
</div>
|
|
||||||
<div class="h-screen bg-teal-300 flex-1">
|
|
||||||
abbbbbbbbbbbbbbbbbbbbb availableThemes:{{ uiStore.availableThemes }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -13,6 +8,4 @@
|
|||||||
definePageMeta({
|
definePageMeta({
|
||||||
name: 'vaultOverview',
|
name: 'vaultOverview',
|
||||||
})
|
})
|
||||||
|
|
||||||
const uiStore = useUiStore()
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -57,14 +57,14 @@ function interceptConsole(level: 'log' | 'info' | 'warn' | 'error' | 'debug') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default defineNuxtPlugin(() => {
|
export default defineNuxtPlugin(() => {
|
||||||
// Install console interceptors immediately
|
// TEMPORARILY DISABLED - Console interceptor causes too many logs
|
||||||
interceptConsole('log')
|
// interceptConsole('log')
|
||||||
interceptConsole('info')
|
// interceptConsole('info')
|
||||||
interceptConsole('warn')
|
// interceptConsole('warn')
|
||||||
interceptConsole('error')
|
// interceptConsole('error')
|
||||||
interceptConsole('debug')
|
// interceptConsole('debug')
|
||||||
|
|
||||||
console.log('[HaexHub] Global console interceptor installed')
|
// console.log('[HaexHub] Global console interceptor installed')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
provide: {
|
provide: {
|
||||||
|
|||||||
170
src/stores/vault/desktop.ts
Normal file
170
src/stores/vault/desktop.ts
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
import { eq } from 'drizzle-orm'
|
||||||
|
import { haexDesktopItems } from '~~/src-tauri/database/schemas'
|
||||||
|
import type {
|
||||||
|
InsertHaexDesktopItems,
|
||||||
|
SelectHaexDesktopItems,
|
||||||
|
} from '~~/src-tauri/database/schemas'
|
||||||
|
|
||||||
|
export type DesktopItemType = 'extension' | 'file' | 'folder'
|
||||||
|
|
||||||
|
export interface IDesktopItem extends SelectHaexDesktopItems {
|
||||||
|
label?: string
|
||||||
|
icon?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useDesktopStore = defineStore('desktopStore', () => {
|
||||||
|
const { currentVault } = storeToRefs(useVaultStore())
|
||||||
|
|
||||||
|
const desktopItems = ref<IDesktopItem[]>([])
|
||||||
|
|
||||||
|
const loadDesktopItemsAsync = async () => {
|
||||||
|
if (!currentVault.value?.drizzle) {
|
||||||
|
console.error('Kein Vault geöffnet')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const items = await currentVault.value.drizzle
|
||||||
|
.select()
|
||||||
|
.from(haexDesktopItems)
|
||||||
|
|
||||||
|
desktopItems.value = items
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Laden der Desktop-Items:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const addDesktopItemAsync = async (
|
||||||
|
itemType: DesktopItemType,
|
||||||
|
referenceId: string,
|
||||||
|
positionX: number = 0,
|
||||||
|
positionY: number = 0,
|
||||||
|
) => {
|
||||||
|
if (!currentVault.value?.drizzle) {
|
||||||
|
throw new Error('Kein Vault geöffnet')
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const newItem: InsertHaexDesktopItems = {
|
||||||
|
itemType: itemType,
|
||||||
|
referenceId: referenceId,
|
||||||
|
positionX: positionX,
|
||||||
|
positionY: positionY,
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await currentVault.value.drizzle
|
||||||
|
.insert(haexDesktopItems)
|
||||||
|
.values(newItem)
|
||||||
|
.returning()
|
||||||
|
|
||||||
|
if (result.length > 0 && result[0]) {
|
||||||
|
desktopItems.value.push(result[0])
|
||||||
|
return result[0]
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Hinzufügen des Desktop-Items:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateDesktopItemPositionAsync = async (
|
||||||
|
id: string,
|
||||||
|
positionX: number,
|
||||||
|
positionY: number,
|
||||||
|
) => {
|
||||||
|
if (!currentVault.value?.drizzle) {
|
||||||
|
throw new Error('Kein Vault geöffnet')
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await currentVault.value.drizzle
|
||||||
|
.update(haexDesktopItems)
|
||||||
|
.set({
|
||||||
|
positionX: positionX,
|
||||||
|
positionY: positionY,
|
||||||
|
})
|
||||||
|
.where(eq(haexDesktopItems.id, id))
|
||||||
|
.returning()
|
||||||
|
|
||||||
|
if (result.length > 0 && result[0]) {
|
||||||
|
const index = desktopItems.value.findIndex((item) => item.id === id)
|
||||||
|
if (index !== -1) {
|
||||||
|
desktopItems.value[index] = result[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Aktualisieren der Position:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeDesktopItemAsync = async (id: string) => {
|
||||||
|
if (!currentVault.value?.drizzle) {
|
||||||
|
throw new Error('Kein Vault geöffnet')
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Soft delete using haexTombstone
|
||||||
|
await currentVault.value.drizzle
|
||||||
|
.delete(haexDesktopItems)
|
||||||
|
.where(eq(haexDesktopItems.id, id))
|
||||||
|
|
||||||
|
desktopItems.value = desktopItems.value.filter((item) => item.id !== id)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Entfernen des Desktop-Items:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDesktopItemByReference = (
|
||||||
|
itemType: DesktopItemType,
|
||||||
|
referenceId: string,
|
||||||
|
) => {
|
||||||
|
return desktopItems.value.find(
|
||||||
|
(item) => item.itemType === itemType && item.referenceId === referenceId,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getContextMenuItems = (
|
||||||
|
id: string,
|
||||||
|
itemType: DesktopItemType,
|
||||||
|
referenceId: string,
|
||||||
|
onOpen: () => void,
|
||||||
|
onUninstall: () => void,
|
||||||
|
) => {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
label: 'Öffnen',
|
||||||
|
icon: 'i-heroicons-arrow-top-right-on-square',
|
||||||
|
click: onOpen,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
label: 'Von Desktop entfernen',
|
||||||
|
icon: 'i-heroicons-x-mark',
|
||||||
|
click: async () => {
|
||||||
|
await removeDesktopItemAsync(id)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Deinstallieren',
|
||||||
|
icon: 'i-heroicons-trash',
|
||||||
|
click: onUninstall,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
desktopItems,
|
||||||
|
loadDesktopItemsAsync,
|
||||||
|
addDesktopItemAsync,
|
||||||
|
updateDesktopItemPositionAsync,
|
||||||
|
removeDesktopItemAsync,
|
||||||
|
getDesktopItemByReference,
|
||||||
|
getContextMenuItems,
|
||||||
|
}
|
||||||
|
})
|
||||||
@ -51,11 +51,12 @@ export const useDeviceStore = defineStore('vaultInstanceStore', () => {
|
|||||||
|
|
||||||
const readDeviceNameAsync = async (id?: string) => {
|
const readDeviceNameAsync = async (id?: string) => {
|
||||||
const { readDeviceNameAsync } = useVaultSettingsStore()
|
const { readDeviceNameAsync } = useVaultSettingsStore()
|
||||||
const _id = id ?? deviceId.value
|
const _id = id || deviceId.value
|
||||||
console.log('readDeviceNameAsync id', _id)
|
console.log('readDeviceNameAsync id', _id)
|
||||||
if (!_id) return
|
if (!_id) return
|
||||||
|
|
||||||
deviceName.value = (await readDeviceNameAsync(_id))?.value ?? ''
|
deviceName.value = (await readDeviceNameAsync(_id))?.value ?? ''
|
||||||
|
|
||||||
return deviceName.value
|
return deviceName.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,10 @@
|
|||||||
import { drizzle } from 'drizzle-orm/sqlite-proxy'
|
import { drizzle } from 'drizzle-orm/sqlite-proxy'
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
import { schema } from '@/../src-tauri/database/index'
|
import { schema } from '@/../src-tauri/database/index'
|
||||||
import type { SqliteRemoteDatabase } from 'drizzle-orm/sqlite-proxy'
|
import type {
|
||||||
|
AsyncRemoteCallback,
|
||||||
|
SqliteRemoteDatabase,
|
||||||
|
} from 'drizzle-orm/sqlite-proxy'
|
||||||
|
|
||||||
interface IVault {
|
interface IVault {
|
||||||
name: string
|
name: string
|
||||||
@ -55,40 +58,10 @@ export const useVaultStore = defineStore('vaultStore', () => {
|
|||||||
...openVaults.value,
|
...openVaults.value,
|
||||||
[vaultId]: {
|
[vaultId]: {
|
||||||
name: fileName,
|
name: fileName,
|
||||||
drizzle: drizzle<typeof schema>(
|
drizzle: drizzle<typeof schema>(drizzleCallback, {
|
||||||
async (sql, params: unknown[], method) => {
|
schema: schema,
|
||||||
let rows: any[] = []
|
logger: false,
|
||||||
let results: any[] = []
|
}),
|
||||||
|
|
||||||
// If the query is a SELECT, use the select method
|
|
||||||
if (isSelectQuery(sql)) {
|
|
||||||
console.log('sql_select', sql, params, method)
|
|
||||||
rows = await invoke<unknown[]>('sql_select', {
|
|
||||||
sql,
|
|
||||||
params,
|
|
||||||
}).catch((e) => {
|
|
||||||
console.error('SQL select Error:', e, sql, params)
|
|
||||||
return []
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
console.log('sql_execute', sql, params, method)
|
|
||||||
// Otherwise, use the execute method
|
|
||||||
rows = await invoke<unknown[]>('sql_execute', {
|
|
||||||
sql,
|
|
||||||
params,
|
|
||||||
}).catch((e) => {
|
|
||||||
console.error('SQL execute Error:', e, sql, params)
|
|
||||||
return []
|
|
||||||
})
|
|
||||||
return { rows: [] }
|
|
||||||
}
|
|
||||||
|
|
||||||
results = method === 'all' ? rows : rows[0]
|
|
||||||
|
|
||||||
return { rows: results }
|
|
||||||
},
|
|
||||||
{ schema: schema, logger: true },
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,7 +114,34 @@ const getVaultIdAsync = async (path: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isSelectQuery = (sql: string) => {
|
const isSelectQuery = (sql: string) => {
|
||||||
console.log('check isSelectQuery', sql)
|
|
||||||
const selectRegex = /^\s*SELECT\b/i
|
const selectRegex = /^\s*SELECT\b/i
|
||||||
return selectRegex.test(sql)
|
return selectRegex.test(sql)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const drizzleCallback = (async (
|
||||||
|
sql: string,
|
||||||
|
params: unknown[],
|
||||||
|
method: 'get' | 'run' | 'all' | 'values',
|
||||||
|
) => {
|
||||||
|
let rows: unknown[] = []
|
||||||
|
|
||||||
|
if (isSelectQuery(sql)) {
|
||||||
|
rows = await invoke<unknown[]>('sql_select', { sql, params }).catch((e) => {
|
||||||
|
console.error('SQL select Error:', e, sql, params)
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
rows = await invoke<unknown[]>('sql_execute', { sql, params }).catch(
|
||||||
|
(e) => {
|
||||||
|
console.error('SQL execute Error:', e, sql, params)
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method === 'get') {
|
||||||
|
return { rows: rows.length > 0 ? [rows[0]] : [] }
|
||||||
|
} else {
|
||||||
|
return { rows }
|
||||||
|
}
|
||||||
|
}) satisfies AsyncRemoteCallback
|
||||||
|
|||||||
@ -128,7 +128,7 @@ export const useVaultSettingsStore = defineStore('vaultSettingsStore', () => {
|
|||||||
eq(schema.haexSettings.key, id),
|
eq(schema.haexSettings.key, id),
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
console.log('readDeviceNameAsync', deviceName)
|
console.log('store: readDeviceNameAsync', deviceName)
|
||||||
return deviceName
|
return deviceName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
todos.md
2
todos.md
@ -2,3 +2,5 @@
|
|||||||
|
|
||||||
- tabellen von erweiterungen müssen mit namensschema generiert werden
|
- tabellen von erweiterungen müssen mit namensschema generiert werden
|
||||||
`${publicKey}_${extensionName}_${tableName}`
|
`${publicKey}_${extensionName}_${tableName}`
|
||||||
|
|
||||||
|
- im sdk und generell, muss ich das namensschema überdenken. das trennzeichen sollte vermutlich etwas anderes sein, als der "\_"
|
||||||
|
|||||||
Reference in New Issue
Block a user