mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-18 06:50:51 +01:00
Compare commits
2 Commits
922ae539ba
...
4f839aa856
| Author | SHA1 | Date | |
|---|---|---|---|
| 4f839aa856 | |||
| 99ccadce00 |
@ -1,227 +0,0 @@
|
||||
{
|
||||
"session_date": "2025-10-20",
|
||||
"project": "haex-hub System Windows Architecture + Drizzle CRDT RETURNING Fix + PK-Remapping Refactor",
|
||||
"status": "system_windows_ui_integration_completed",
|
||||
|
||||
"context": {
|
||||
"main_work_today": [
|
||||
"Fixed Drizzle CRDT integration with RETURNING support",
|
||||
"Implemented System Windows architecture (Settings, Marketplace as Desktop Windows)",
|
||||
"Refactored executor functions: Split execute/query paths for cleaner code",
|
||||
"Integrated System Windows UI: Launcher, Window Component, Placeholder Components"
|
||||
],
|
||||
"completed_today": [
|
||||
"Added AST-based statement_has_returning() for safe RETURNING detection",
|
||||
"Created SqlExecutor::query_internal() for INSERT/UPDATE/DELETE with RETURNING",
|
||||
"Simplified execute_internal to use execute_internal_typed (now with PK-Remapping!)",
|
||||
"Added sql_query_with_crdt Tauri command",
|
||||
"Updated drizzleCallback in index.ts to route correctly",
|
||||
"Extended IWindow interface: type ('system' | 'extension'), sourceId",
|
||||
"Added SystemWindowDefinition interface",
|
||||
"Created system windows registry in windowManager store",
|
||||
"Extended openWindow() to support both system and extension windows",
|
||||
"Added singleton support for system windows",
|
||||
"Split execute_internal_typed_with_context into two functions (execute vs query)",
|
||||
"Created query_internal_typed_with_context with full PK-Remapping support",
|
||||
"Updated query_internal to use new typed function with PK-Remapping",
|
||||
"Fixed manager.rs to use query_internal_typed_with_context for INSERT RETURNING",
|
||||
"Extended Launcher to show System Windows + Extensions alphabetically",
|
||||
"Adapted Window Component to render System Windows as Vue Components, Extensions as iFrames",
|
||||
"Created placeholder components: Settings.vue and Marketplace.vue"
|
||||
],
|
||||
"tech_stack": "Vue 3, TypeScript, Pinia, Nuxt UI, Tauri, Rust, Drizzle ORM, SQLite"
|
||||
},
|
||||
|
||||
"drizzle_crdt_implementation": {
|
||||
"problem": "Drizzle .insert().returning() executed SQL twice and lost RETURNING data",
|
||||
"solution": "Separate execute and query paths based on RETURNING clause",
|
||||
|
||||
"typescript_side": {
|
||||
"file": "src/stores/vault/index.ts",
|
||||
"drizzleCallback_logic": {
|
||||
"select": "sql_select (unchanged)",
|
||||
"with_returning": "sql_query_with_crdt (NEW)",
|
||||
"without_returning": "sql_execute_with_crdt (unchanged)"
|
||||
},
|
||||
"hasReturning_check": "String-based RETURNING regex (safe enough for generated SQL)"
|
||||
},
|
||||
|
||||
"rust_side": {
|
||||
"files": [
|
||||
"src-tauri/src/database/core.rs",
|
||||
"src-tauri/src/extension/database/executor.rs",
|
||||
"src-tauri/src/database/mod.rs"
|
||||
],
|
||||
"core_rs_changes": {
|
||||
"statement_has_returning": {
|
||||
"line": 84,
|
||||
"purpose": "AST-based RETURNING check (INSERT, UPDATE, DELETE)",
|
||||
"safety": "Checks actual AST, not string matching"
|
||||
},
|
||||
"convert_value_ref_to_json": {
|
||||
"line": 333,
|
||||
"visibility": "Made public for reuse"
|
||||
},
|
||||
"removed": "query_with_crdt function (replaced by SqlExecutor::query_internal)"
|
||||
},
|
||||
"executor_rs_changes": {
|
||||
"execute_internal_typed_with_context": {
|
||||
"line": 100,
|
||||
"purpose": "Execute SQL WITHOUT RETURNING (with CRDT and FK-Remapping)",
|
||||
"returns": "Result<HashSet<String>, DatabaseError>",
|
||||
"behavior": "Handles INSERTs with FK-Remapping, uses execute()"
|
||||
},
|
||||
"query_internal_typed_with_context": {
|
||||
"line": 186,
|
||||
"purpose": "Execute SQL WITH RETURNING (with CRDT, PK-Remapping, FK-Remapping)",
|
||||
"returns": "Result<(HashSet<String>, Vec<Vec<JsonValue>>), DatabaseError>",
|
||||
"behavior": "Handles INSERTs with full PK-Remapping + FK-Remapping, returns all RETURNING columns"
|
||||
},
|
||||
"query_internal": {
|
||||
"line": 454,
|
||||
"purpose": "Execute with CRDT + return full RETURNING results (JsonValue params)",
|
||||
"behavior": "Wrapper around query_internal_typed_with_context"
|
||||
},
|
||||
"execute_internal_refactor": {
|
||||
"line": 345,
|
||||
"change": "Now wrapper around execute_internal_typed",
|
||||
"benefit": "Drizzle now gets PK-Remapping for ON CONFLICT!"
|
||||
}
|
||||
},
|
||||
"mod_rs_changes": {
|
||||
"sql_query_with_crdt": {
|
||||
"line": 59,
|
||||
"calls": "SqlExecutor::query_internal",
|
||||
"returns": "Vec<Vec<JsonValue>>"
|
||||
}
|
||||
},
|
||||
"lib_rs_changes": {
|
||||
"registered_command": "sql_query_with_crdt added to invoke_handler"
|
||||
}
|
||||
},
|
||||
|
||||
"benefits": [
|
||||
"SQL executed only once (not twice)",
|
||||
"Full RETURNING results available to Drizzle",
|
||||
"PK-Remapping now works for Drizzle (both execute and query paths)",
|
||||
"AST-based RETURNING detection (safe)",
|
||||
"Less code duplication",
|
||||
"Cleaner code separation: execute vs query functions",
|
||||
"FK-Remapping works across transactions with PkRemappingContext"
|
||||
]
|
||||
},
|
||||
|
||||
"system_windows_architecture": {
|
||||
"concept": "ALL UI (Settings, Marketplace, etc.) as DesktopWindows, same as Extensions",
|
||||
"status": "Store completed, UI integration pending",
|
||||
|
||||
"window_manager_store": {
|
||||
"file": "src/stores/desktop/windowManager.ts",
|
||||
"changes": {
|
||||
"IWindow_interface": {
|
||||
"added_fields": [
|
||||
"type: 'system' | 'extension'",
|
||||
"sourceId: string (replaces extensionId)"
|
||||
],
|
||||
"removed_fields": ["extensionId"]
|
||||
},
|
||||
"SystemWindowDefinition": {
|
||||
"fields": "id, name, icon, component, defaultWidth, defaultHeight, resizable, singleton"
|
||||
},
|
||||
"system_windows_registry": {
|
||||
"line": 46,
|
||||
"entries": ["settings", "marketplace"],
|
||||
"structure": "Record<string, SystemWindowDefinition>"
|
||||
},
|
||||
"openWindow_function": {
|
||||
"line": 101,
|
||||
"signature": "(type, sourceId, title?, icon?, width?, height?, sourcePosition?)",
|
||||
"features": [
|
||||
"Type-based handling (system vs extension)",
|
||||
"Singleton check for system windows",
|
||||
"Auto-loads defaults from registry",
|
||||
"Activates existing singleton if already open"
|
||||
]
|
||||
},
|
||||
"new_exports": ["getAllSystemWindows", "getSystemWindow"]
|
||||
}
|
||||
},
|
||||
|
||||
"ui_integration": {
|
||||
"launcher": {
|
||||
"file": "src/components/haex/extension/launcher.vue",
|
||||
"changes": [
|
||||
"Combined system windows and extensions in unified launcherItems computed",
|
||||
"Alphabetically sorted by name",
|
||||
"openItem() function handles both types with correct openWindow() signature",
|
||||
"Uses windowManagerStore.getAllSystemWindows()"
|
||||
]
|
||||
},
|
||||
"desktop_window_component": {
|
||||
"file": "src/components/haex/desktop/index.vue",
|
||||
"changes": [
|
||||
"Dynamic component rendering: <component :is='getSystemWindowComponent()'> for system windows",
|
||||
"HaexDesktopExtensionFrame for extensions (iFrame)",
|
||||
"getSystemWindowComponent() function to retrieve Vue component from registry",
|
||||
"Applied to both normal mode and overview mode"
|
||||
]
|
||||
},
|
||||
"placeholder_components": {
|
||||
"created": [
|
||||
"src/components/haex/system/settings.vue",
|
||||
"src/components/haex/system/marketplace.vue"
|
||||
],
|
||||
"description": "Simple placeholder UI with sections and styling"
|
||||
}
|
||||
},
|
||||
|
||||
"next_steps": {
|
||||
"priority": [
|
||||
"Desktop Icons: Support system window icons (alongside extension icons)",
|
||||
"Drag & Drop: Launcher → Desktop for all types (system + extension)"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
"workspace_overview_context": {
|
||||
"still_active": "GNOME-style workspace overview with UDrawer, implemented yesterday",
|
||||
"file": "src/components/haex/desktop/index.vue",
|
||||
"status": "Working, workspace switching functional"
|
||||
},
|
||||
|
||||
"file_changes_today": {
|
||||
"modified": [
|
||||
"src/stores/vault/index.ts (drizzleCallback with hasReturning)",
|
||||
"src-tauri/src/database/core.rs (statement_has_returning, removed query_with_crdt)",
|
||||
"src-tauri/src/extension/database/executor.rs (split execute/query functions, PK-Remapping)",
|
||||
"src-tauri/src/database/mod.rs (sql_query_with_crdt command)",
|
||||
"src-tauri/src/lib.rs (registered sql_query_with_crdt)",
|
||||
"src/stores/desktop/windowManager.ts (system windows support)",
|
||||
"src-tauri/src/extension/core/manager.rs (updated to use query_internal_typed_with_context)",
|
||||
"src/components/haex/extension/launcher.vue (unified launcher for system + extensions)",
|
||||
"src/components/haex/desktop/index.vue (dynamic component rendering)"
|
||||
],
|
||||
"created": [
|
||||
"src/components/haex/system/settings.vue",
|
||||
"src/components/haex/system/marketplace.vue"
|
||||
],
|
||||
"deleted": []
|
||||
},
|
||||
|
||||
"important_notes": [
|
||||
"Drizzle RETURNING now fully functional with CRDT",
|
||||
"System windows use Vue components, Extensions use iFrame",
|
||||
"sourceId is generic: extensionId for extensions, systemWindowId for system windows",
|
||||
"Singleton system windows auto-activate if already open",
|
||||
"PK-Remapping now works for both execute and query paths",
|
||||
"executor.rs: Two separate functions for execute (no RETURNING) vs query (with RETURNING)",
|
||||
"query_internal_typed_with_context returns full RETURNING results as Vec<Vec<JsonValue>>",
|
||||
"FK-Remapping works across transaction using PkRemappingContext",
|
||||
"Next session: Implement Launcher UI integration for system windows"
|
||||
],
|
||||
|
||||
"todos_remaining": [
|
||||
"Desktop Icons für System Windows unterstützen (neben Extension Icons)",
|
||||
"Drag & Drop vom Launcher zum Desktop implementieren (für beide Typen)"
|
||||
]
|
||||
}
|
||||
@ -170,6 +170,14 @@ use serde::{Deserialize, Serialize};
|
||||
table: schema.haexCrdtSnapshots,
|
||||
},
|
||||
{ name: tablesNames.haex.crdt.configs.name, table: schema.haexCrdtConfigs },
|
||||
{
|
||||
name: tablesNames.haex.desktop_items.name,
|
||||
table: schema.haexDesktopItems,
|
||||
},
|
||||
{
|
||||
name: tablesNames.haex.workspaces.name,
|
||||
table: schema.haexWorkspaces,
|
||||
},
|
||||
]
|
||||
|
||||
for (const { name, table } of schemas) {
|
||||
|
||||
@ -24,9 +24,20 @@ CREATE TABLE `haex_crdt_snapshots` (
|
||||
`file_size_bytes` integer
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `haex_desktop_items` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`workspace_id` text 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_timestamp` text,
|
||||
FOREIGN KEY (`workspace_id`) REFERENCES `haex_workspaces`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `haex_extension_permissions` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`extension_id` text,
|
||||
`extension_id` text NOT NULL,
|
||||
`resource_type` text,
|
||||
`action` text,
|
||||
`target` text,
|
||||
@ -34,38 +45,27 @@ CREATE TABLE `haex_extension_permissions` (
|
||||
`status` text DEFAULT 'denied' NOT NULL,
|
||||
`created_at` text DEFAULT (CURRENT_TIMESTAMP),
|
||||
`updated_at` integer,
|
||||
`haex_tombstone` integer,
|
||||
`haex_timestamp` text,
|
||||
FOREIGN KEY (`extension_id`) REFERENCES `haex_extensions`(`id`) ON UPDATE no action ON DELETE no action
|
||||
FOREIGN KEY (`extension_id`) REFERENCES `haex_extensions`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `haex_extension_permissions_extension_id_resource_type_action_target_unique` ON `haex_extension_permissions` (`extension_id`,`resource_type`,`action`,`target`);--> statement-breakpoint
|
||||
CREATE TABLE `haex_extensions` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`public_key` text NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`version` text NOT NULL,
|
||||
`author` text,
|
||||
`description` text,
|
||||
`entry` text,
|
||||
`entry` text DEFAULT 'index.html' NOT NULL,
|
||||
`homepage` text,
|
||||
`enabled` integer,
|
||||
`enabled` integer DEFAULT true,
|
||||
`icon` text,
|
||||
`name` text,
|
||||
`public_key` text,
|
||||
`signature` text,
|
||||
`url` text,
|
||||
`version` text,
|
||||
`haex_tombstone` integer,
|
||||
`haex_timestamp` text
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `haex_settings` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`key` text,
|
||||
`type` text,
|
||||
`value` text,
|
||||
`haex_tombstone` integer,
|
||||
`signature` text NOT NULL,
|
||||
`haex_timestamp` text
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `haex_extensions_public_key_name_unique` ON `haex_extensions` (`public_key`,`name`);--> statement-breakpoint
|
||||
CREATE TABLE `haex_notifications` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`alt` text,
|
||||
@ -77,63 +77,23 @@ CREATE TABLE `haex_notifications` (
|
||||
`text` text,
|
||||
`title` text,
|
||||
`type` text NOT NULL,
|
||||
`haex_tombstone` integer
|
||||
`haex_timestamp` text
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `haex_passwords_group_items` (
|
||||
`group_id` text,
|
||||
`item_id` text,
|
||||
`haex_tombstone` integer,
|
||||
PRIMARY KEY(`item_id`, `group_id`),
|
||||
FOREIGN KEY (`group_id`) REFERENCES `haex_passwords_groups`(`id`) ON UPDATE no action ON DELETE no action,
|
||||
FOREIGN KEY (`item_id`) REFERENCES `haex_passwords_item_details`(`id`) ON UPDATE no action ON DELETE no action
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `haex_passwords_groups` (
|
||||
CREATE TABLE `haex_settings` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`name` text,
|
||||
`description` text,
|
||||
`icon` text,
|
||||
`order` integer,
|
||||
`color` text,
|
||||
`parent_id` text,
|
||||
`created_at` text DEFAULT (CURRENT_TIMESTAMP),
|
||||
`updated_at` integer,
|
||||
`haex_tombstone` integer,
|
||||
FOREIGN KEY (`parent_id`) REFERENCES `haex_passwords_groups`(`id`) ON UPDATE no action ON DELETE no action
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `haex_passwords_item_details` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`title` text,
|
||||
`username` text,
|
||||
`password` text,
|
||||
`note` text,
|
||||
`icon` text,
|
||||
`tags` text,
|
||||
`url` text,
|
||||
`created_at` text DEFAULT (CURRENT_TIMESTAMP),
|
||||
`updated_at` integer,
|
||||
`haex_tombstone` integer
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `haex_passwords_item_history` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`item_id` text,
|
||||
`changed_property` text,
|
||||
`old_value` text,
|
||||
`new_value` text,
|
||||
`created_at` text DEFAULT (CURRENT_TIMESTAMP),
|
||||
`haex_tombstone` integer,
|
||||
FOREIGN KEY (`item_id`) REFERENCES `haex_passwords_item_details`(`id`) ON UPDATE no action ON DELETE no action
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `haex_passwords_item_key_values` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`item_id` text,
|
||||
`key` text,
|
||||
`type` text,
|
||||
`value` text,
|
||||
`updated_at` integer,
|
||||
`haex_tombstone` integer,
|
||||
FOREIGN KEY (`item_id`) REFERENCES `haex_passwords_item_details`(`id`) ON UPDATE no action ON DELETE no action
|
||||
`haex_timestamp` text
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `haex_settings_key_type_value_unique` ON `haex_settings` (`key`,`type`,`value`);--> statement-breakpoint
|
||||
CREATE TABLE `haex_workspaces` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`position` integer DEFAULT 0 NOT NULL,
|
||||
`haex_timestamp` text
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `haex_workspaces_position_unique` ON `haex_workspaces` (`position`);
|
||||
@ -1 +0,0 @@
|
||||
ALTER TABLE `haex_notifications` ADD `haex_timestamp` text;
|
||||
@ -1,22 +0,0 @@
|
||||
PRAGMA foreign_keys=OFF;--> statement-breakpoint
|
||||
CREATE TABLE `__new_haex_extensions` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`public_key` text NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`version` text NOT NULL,
|
||||
`author` text,
|
||||
`description` text,
|
||||
`entry` text DEFAULT 'index.html' NOT NULL,
|
||||
`homepage` text,
|
||||
`enabled` integer DEFAULT true,
|
||||
`icon` text,
|
||||
`signature` text NOT NULL,
|
||||
`haex_tombstone` integer,
|
||||
`haex_timestamp` text
|
||||
);
|
||||
--> statement-breakpoint
|
||||
INSERT INTO `__new_haex_extensions`("id", "public_key", "name", "version", "author", "description", "entry", "homepage", "enabled", "icon", "signature", "haex_tombstone", "haex_timestamp") SELECT "id", "public_key", "name", "version", "author", "description", "entry", "homepage", "enabled", "icon", "signature", "haex_tombstone", "haex_timestamp" FROM `haex_extensions`;--> statement-breakpoint
|
||||
DROP TABLE `haex_extensions`;--> statement-breakpoint
|
||||
ALTER TABLE `__new_haex_extensions` RENAME TO `haex_extensions`;--> statement-breakpoint
|
||||
PRAGMA foreign_keys=ON;--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `haex_extensions_public_key_name_unique` ON `haex_extensions` (`public_key`,`name`);
|
||||
@ -1,9 +0,0 @@
|
||||
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
|
||||
);
|
||||
@ -1,10 +0,0 @@
|
||||
CREATE TABLE `haex_workspaces` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`position` integer DEFAULT 0 NOT NULL,
|
||||
`created_at` integer NOT NULL,
|
||||
`haex_tombstone` integer,
|
||||
`haex_timestamp` text
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE `haex_desktop_items` ADD `workspace_id` text NOT NULL REFERENCES haex_workspaces(id);
|
||||
@ -1 +0,0 @@
|
||||
CREATE UNIQUE INDEX `haex_workspaces_name_unique` ON `haex_workspaces` (`name`);
|
||||
@ -1,3 +0,0 @@
|
||||
DROP INDEX `haex_workspaces_name_unique`;--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `haex_workspaces_position_unique` ON `haex_workspaces` (`position`);--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `haex_settings_key_type_value_unique` ON `haex_settings` (`key`,`type`,`value`);
|
||||
@ -1,2 +0,0 @@
|
||||
DROP INDEX `haex_workspaces_position_unique`;--> statement-breakpoint
|
||||
ALTER TABLE `haex_workspaces` DROP COLUMN `created_at`;
|
||||
@ -1 +0,0 @@
|
||||
CREATE UNIQUE INDEX `haex_workspaces_position_unique` ON `haex_workspaces` (`position`);
|
||||
@ -1,92 +0,0 @@
|
||||
PRAGMA foreign_keys=OFF;--> statement-breakpoint
|
||||
CREATE TABLE `__new_haex_desktop_items` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`workspace_id` text 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,
|
||||
FOREIGN KEY (`workspace_id`) REFERENCES `haex_workspaces`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
INSERT INTO `__new_haex_desktop_items`("id", "workspace_id", "item_type", "reference_id", "position_x", "position_y", "haex_tombstone", "haex_timestamp") SELECT "id", "workspace_id", "item_type", "reference_id", "position_x", "position_y", "haex_tombstone", "haex_timestamp" FROM `haex_desktop_items`;--> statement-breakpoint
|
||||
DROP TABLE `haex_desktop_items`;--> statement-breakpoint
|
||||
ALTER TABLE `__new_haex_desktop_items` RENAME TO `haex_desktop_items`;--> statement-breakpoint
|
||||
PRAGMA foreign_keys=ON;--> statement-breakpoint
|
||||
CREATE TABLE `__new_haex_extension_permissions` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`extension_id` text NOT NULL,
|
||||
`resource_type` text,
|
||||
`action` text,
|
||||
`target` text,
|
||||
`constraints` text,
|
||||
`status` text DEFAULT 'denied' NOT NULL,
|
||||
`created_at` text DEFAULT (CURRENT_TIMESTAMP),
|
||||
`updated_at` integer,
|
||||
`haex_tombstone` integer,
|
||||
`haex_timestamp` text,
|
||||
FOREIGN KEY (`extension_id`) REFERENCES `haex_extensions`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
INSERT INTO `__new_haex_extension_permissions`("id", "extension_id", "resource_type", "action", "target", "constraints", "status", "created_at", "updated_at", "haex_tombstone", "haex_timestamp") SELECT "id", "extension_id", "resource_type", "action", "target", "constraints", "status", "created_at", "updated_at", "haex_tombstone", "haex_timestamp" FROM `haex_extension_permissions`;--> statement-breakpoint
|
||||
DROP TABLE `haex_extension_permissions`;--> statement-breakpoint
|
||||
ALTER TABLE `__new_haex_extension_permissions` RENAME TO `haex_extension_permissions`;--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `haex_extension_permissions_extension_id_resource_type_action_target_unique` ON `haex_extension_permissions` (`extension_id`,`resource_type`,`action`,`target`);--> statement-breakpoint
|
||||
CREATE TABLE `__new_haex_passwords_group_items` (
|
||||
`group_id` text NOT NULL,
|
||||
`item_id` text NOT NULL,
|
||||
`haex_tombstone` integer,
|
||||
PRIMARY KEY(`item_id`, `group_id`),
|
||||
FOREIGN KEY (`group_id`) REFERENCES `haex_passwords_groups`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`item_id`) REFERENCES `haex_passwords_item_details`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
INSERT INTO `__new_haex_passwords_group_items`("group_id", "item_id", "haex_tombstone") SELECT "group_id", "item_id", "haex_tombstone" FROM `haex_passwords_group_items`;--> statement-breakpoint
|
||||
DROP TABLE `haex_passwords_group_items`;--> statement-breakpoint
|
||||
ALTER TABLE `__new_haex_passwords_group_items` RENAME TO `haex_passwords_group_items`;--> statement-breakpoint
|
||||
CREATE TABLE `__new_haex_passwords_groups` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`name` text,
|
||||
`description` text,
|
||||
`icon` text,
|
||||
`order` integer,
|
||||
`color` text,
|
||||
`parent_id` text,
|
||||
`created_at` text DEFAULT (CURRENT_TIMESTAMP),
|
||||
`updated_at` integer,
|
||||
`haex_tombstone` integer,
|
||||
FOREIGN KEY (`parent_id`) REFERENCES `haex_passwords_groups`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
INSERT INTO `__new_haex_passwords_groups`("id", "name", "description", "icon", "order", "color", "parent_id", "created_at", "updated_at", "haex_tombstone") SELECT "id", "name", "description", "icon", "order", "color", "parent_id", "created_at", "updated_at", "haex_tombstone" FROM `haex_passwords_groups`;--> statement-breakpoint
|
||||
DROP TABLE `haex_passwords_groups`;--> statement-breakpoint
|
||||
ALTER TABLE `__new_haex_passwords_groups` RENAME TO `haex_passwords_groups`;--> statement-breakpoint
|
||||
CREATE TABLE `__new_haex_passwords_item_history` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`item_id` text NOT NULL,
|
||||
`changed_property` text,
|
||||
`old_value` text,
|
||||
`new_value` text,
|
||||
`created_at` text DEFAULT (CURRENT_TIMESTAMP),
|
||||
`haex_tombstone` integer,
|
||||
FOREIGN KEY (`item_id`) REFERENCES `haex_passwords_item_details`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
INSERT INTO `__new_haex_passwords_item_history`("id", "item_id", "changed_property", "old_value", "new_value", "created_at", "haex_tombstone") SELECT "id", "item_id", "changed_property", "old_value", "new_value", "created_at", "haex_tombstone" FROM `haex_passwords_item_history`;--> statement-breakpoint
|
||||
DROP TABLE `haex_passwords_item_history`;--> statement-breakpoint
|
||||
ALTER TABLE `__new_haex_passwords_item_history` RENAME TO `haex_passwords_item_history`;--> statement-breakpoint
|
||||
CREATE TABLE `__new_haex_passwords_item_key_values` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`item_id` text NOT NULL,
|
||||
`key` text,
|
||||
`value` text,
|
||||
`updated_at` integer,
|
||||
`haex_tombstone` integer,
|
||||
FOREIGN KEY (`item_id`) REFERENCES `haex_passwords_item_details`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
INSERT INTO `__new_haex_passwords_item_key_values`("id", "item_id", "key", "value", "updated_at", "haex_tombstone") SELECT "id", "item_id", "key", "value", "updated_at", "haex_tombstone" FROM `haex_passwords_item_key_values`;--> statement-breakpoint
|
||||
DROP TABLE `haex_passwords_item_key_values`;--> statement-breakpoint
|
||||
ALTER TABLE `__new_haex_passwords_item_key_values` RENAME TO `haex_passwords_item_key_values`;
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "3bbe52b8-5933-4b21-8b24-de3927a2f9b0",
|
||||
"id": "21ca1268-1057-48c1-8647-29bd7cb67d49",
|
||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||
"tables": {
|
||||
"haex_crdt_configs": {
|
||||
@ -155,6 +155,81 @@
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_desktop_items": {
|
||||
"name": "haex_desktop_items",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"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_timestamp": {
|
||||
"name": "haex_timestamp",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"haex_desktop_items_workspace_id_haex_workspaces_id_fk": {
|
||||
"name": "haex_desktop_items_workspace_id_haex_workspaces_id_fk",
|
||||
"tableFrom": "haex_desktop_items",
|
||||
"tableTo": "haex_workspaces",
|
||||
"columnsFrom": [
|
||||
"workspace_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_extension_permissions": {
|
||||
"name": "haex_extension_permissions",
|
||||
"columns": {
|
||||
@ -169,7 +244,7 @@
|
||||
"name": "extension_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"resource_type": {
|
||||
@ -223,13 +298,6 @@
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"haex_tombstone": {
|
||||
"name": "haex_tombstone",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"haex_timestamp": {
|
||||
"name": "haex_timestamp",
|
||||
"type": "text",
|
||||
@ -261,7 +329,7 @@
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
@ -279,6 +347,27 @@
|
||||
"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",
|
||||
@ -297,8 +386,9 @@
|
||||
"name": "entry",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'index.html'"
|
||||
},
|
||||
"homepage": {
|
||||
"name": "homepage",
|
||||
@ -312,7 +402,8 @@
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
"autoincrement": false,
|
||||
"default": true
|
||||
},
|
||||
"icon": {
|
||||
"name": "icon",
|
||||
@ -321,100 +412,13 @@
|
||||
"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_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",
|
||||
@ -423,7 +427,16 @@
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"indexes": {
|
||||
"haex_extensions_public_key_name_unique": {
|
||||
"name": "haex_extensions_public_key_name_unique",
|
||||
"columns": [
|
||||
"public_key",
|
||||
"name"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
@ -502,9 +515,9 @@
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"haex_tombstone": {
|
||||
"name": "haex_tombstone",
|
||||
"type": "integer",
|
||||
"haex_timestamp": {
|
||||
"name": "haex_timestamp",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
@ -516,74 +529,8 @@
|
||||
"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",
|
||||
"haex_settings": {
|
||||
"name": "haex_settings",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
@ -592,270 +539,6 @@
|
||||
"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",
|
||||
@ -863,6 +546,13 @@
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"type": {
|
||||
"name": "type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"value": {
|
||||
"name": "value",
|
||||
"type": "text",
|
||||
@ -870,37 +560,73 @@
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "integer",
|
||||
"haex_timestamp": {
|
||||
"name": "haex_timestamp",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"haex_settings_key_type_value_unique": {
|
||||
"name": "haex_settings_key_type_value_unique",
|
||||
"columns": [
|
||||
"key",
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_workspaces": {
|
||||
"name": "haex_workspaces",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"haex_tombstone": {
|
||||
"name": "haex_tombstone",
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"position": {
|
||||
"name": "position",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
},
|
||||
"haex_timestamp": {
|
||||
"name": "haex_timestamp",
|
||||
"type": "text",
|
||||
"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"
|
||||
"indexes": {
|
||||
"haex_workspaces_position_unique": {
|
||||
"name": "haex_workspaces_position_unique",
|
||||
"columns": [
|
||||
"position"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
|
||||
@ -1,926 +0,0 @@
|
||||
{
|
||||
"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": {}
|
||||
}
|
||||
}
|
||||
@ -1,930 +0,0 @@
|
||||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "5387568f-75b3-4a85-86c5-67f539c3fedf",
|
||||
"prevId": "862ac1d5-3065-4244-8652-2b6782254862",
|
||||
"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
|
||||
},
|
||||
"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": {}
|
||||
}
|
||||
}
|
||||
@ -1,991 +0,0 @@
|
||||
{
|
||||
"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": {}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -5,71 +5,8 @@
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "6",
|
||||
"when": 1759402321133,
|
||||
"tag": "0000_glamorous_hulk",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "6",
|
||||
"when": 1759418087677,
|
||||
"tag": "0001_green_stark_industries",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"version": "6",
|
||||
"when": 1760272083150,
|
||||
"tag": "0002_amazing_iron_fist",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 3,
|
||||
"version": "6",
|
||||
"when": 1760611690801,
|
||||
"tag": "0003_daily_polaris",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 4,
|
||||
"version": "6",
|
||||
"when": 1760817142340,
|
||||
"tag": "0004_mature_viper",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 5,
|
||||
"version": "6",
|
||||
"when": 1760964548034,
|
||||
"tag": "0005_tidy_yellowjacket",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 6,
|
||||
"version": "6",
|
||||
"when": 1761137108127,
|
||||
"tag": "0006_gigantic_bloodaxe",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 7,
|
||||
"version": "6",
|
||||
"when": 1761141111765,
|
||||
"tag": "0007_stale_longshot",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 8,
|
||||
"version": "6",
|
||||
"when": 1761145177028,
|
||||
"tag": "0008_dizzy_blue_shield",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 9,
|
||||
"version": "6",
|
||||
"when": 1761203548348,
|
||||
"tag": "0009_boring_arclight",
|
||||
"when": 1761216357702,
|
||||
"tag": "0000_bumpy_valkyrie",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
|
||||
@ -9,15 +9,14 @@ import {
|
||||
} from 'drizzle-orm/sqlite-core'
|
||||
import tableNames from '../tableNames.json'
|
||||
|
||||
// Helper function to add common CRDT columns (haexTombstone and haexTimestamp)
|
||||
// Helper function to add common CRDT columns ( haexTimestamp)
|
||||
export const withCrdtColumns = <
|
||||
T extends Record<string, SQLiteColumnBuilderBase>,
|
||||
>(
|
||||
columns: T,
|
||||
columnNames: { haexTombstone: string; haexTimestamp: string },
|
||||
columnNames: { haexTimestamp: string },
|
||||
) => ({
|
||||
...columns,
|
||||
haexTombstone: integer(columnNames.haexTombstone, { mode: 'boolean' }),
|
||||
haexTimestamp: text(columnNames.haexTimestamp),
|
||||
})
|
||||
|
||||
@ -30,9 +29,7 @@ export const haexSettings = sqliteTable(
|
||||
key: text(),
|
||||
type: text(),
|
||||
value: text(),
|
||||
haexTombstone: integer(tableNames.haex.settings.columns.haexTombstone, {
|
||||
mode: 'boolean',
|
||||
}),
|
||||
|
||||
haexTimestamp: text(tableNames.haex.settings.columns.haexTimestamp),
|
||||
},
|
||||
(table) => [unique().on(table.key, table.type, table.value)],
|
||||
@ -56,9 +53,6 @@ export const haexExtensions = sqliteTable(
|
||||
enabled: integer({ mode: 'boolean' }).default(true),
|
||||
icon: text(),
|
||||
signature: text().notNull(),
|
||||
haexTombstone: integer(tableNames.haex.extensions.columns.haexTombstone, {
|
||||
mode: 'boolean',
|
||||
}),
|
||||
haexTimestamp: text(tableNames.haex.extensions.columns.haexTimestamp),
|
||||
},
|
||||
(table) => [
|
||||
@ -75,9 +69,7 @@ export const haexExtensionPermissions = sqliteTable(
|
||||
id: text()
|
||||
.primaryKey()
|
||||
.$defaultFn(() => crypto.randomUUID()),
|
||||
extensionId: text(
|
||||
tableNames.haex.extension_permissions.columns.extensionId,
|
||||
)
|
||||
extensionId: text(tableNames.haex.extension_permissions.columns.extensionId)
|
||||
.notNull()
|
||||
.references((): AnySQLiteColumn => haexExtensions.id, {
|
||||
onDelete: 'cascade',
|
||||
@ -95,10 +87,6 @@ export const haexExtensionPermissions = sqliteTable(
|
||||
updateAt: integer('updated_at', { mode: 'timestamp' }).$onUpdate(
|
||||
() => new Date(),
|
||||
),
|
||||
haexTombstone: integer(
|
||||
tableNames.haex.extension_permissions.columns.haexTombstone,
|
||||
{ mode: 'boolean' },
|
||||
),
|
||||
haexTimestamp: text(
|
||||
tableNames.haex.extension_permissions.columns.haexTimestamp,
|
||||
),
|
||||
@ -132,10 +120,6 @@ export const haexNotifications = sqliteTable(
|
||||
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),
|
||||
},
|
||||
)
|
||||
|
||||
@ -1,121 +0,0 @@
|
||||
import { sql } from 'drizzle-orm'
|
||||
import {
|
||||
integer,
|
||||
primaryKey,
|
||||
sqliteTable,
|
||||
text,
|
||||
type AnySQLiteColumn,
|
||||
} from 'drizzle-orm/sqlite-core'
|
||||
import tableNames from '../tableNames.json'
|
||||
|
||||
export const haexPasswordsItemDetails = sqliteTable(
|
||||
tableNames.haex.passwords.item_details,
|
||||
{
|
||||
id: text().primaryKey(),
|
||||
title: text(),
|
||||
username: text(),
|
||||
password: text(),
|
||||
note: text(),
|
||||
icon: text(),
|
||||
tags: text(),
|
||||
url: text(),
|
||||
createdAt: text('created_at').default(sql`(CURRENT_TIMESTAMP)`),
|
||||
updateAt: integer('updated_at', { mode: 'timestamp' }).$onUpdate(
|
||||
() => new Date(),
|
||||
),
|
||||
haex_tombstone: integer({ mode: 'boolean' }),
|
||||
},
|
||||
)
|
||||
export type InsertHaexPasswordsItemDetails =
|
||||
typeof haexPasswordsItemDetails.$inferInsert
|
||||
export type SelectHaexPasswordsItemDetails =
|
||||
typeof haexPasswordsItemDetails.$inferSelect
|
||||
|
||||
export const haexPasswordsItemKeyValues = sqliteTable(
|
||||
tableNames.haex.passwords.item_key_values,
|
||||
{
|
||||
id: text().primaryKey(),
|
||||
itemId: text('item_id')
|
||||
.notNull()
|
||||
.references((): AnySQLiteColumn => haexPasswordsItemDetails.id, {
|
||||
onDelete: 'cascade',
|
||||
}),
|
||||
key: text(),
|
||||
value: text(),
|
||||
updateAt: integer('updated_at', { mode: 'timestamp' }).$onUpdate(
|
||||
() => new Date(),
|
||||
),
|
||||
haex_tombstone: integer({ mode: 'boolean' }),
|
||||
},
|
||||
)
|
||||
export type InserthaexPasswordsItemKeyValues =
|
||||
typeof haexPasswordsItemKeyValues.$inferInsert
|
||||
export type SelectHaexPasswordsItemKeyValues =
|
||||
typeof haexPasswordsItemKeyValues.$inferSelect
|
||||
|
||||
export const haexPasswordsItemHistory = sqliteTable(
|
||||
tableNames.haex.passwords.item_histories,
|
||||
{
|
||||
id: text().primaryKey(),
|
||||
itemId: text('item_id')
|
||||
.notNull()
|
||||
.references((): AnySQLiteColumn => haexPasswordsItemDetails.id, {
|
||||
onDelete: 'cascade',
|
||||
}),
|
||||
changedProperty:
|
||||
text('changed_property').$type<keyof typeof haexPasswordsItemDetails>(),
|
||||
oldValue: text('old_value'),
|
||||
newValue: text('new_value'),
|
||||
createdAt: text('created_at').default(sql`(CURRENT_TIMESTAMP)`),
|
||||
haex_tombstone: integer({ mode: 'boolean' }),
|
||||
},
|
||||
)
|
||||
export type InserthaexPasswordsItemHistory =
|
||||
typeof haexPasswordsItemHistory.$inferInsert
|
||||
export type SelectHaexPasswordsItemHistory =
|
||||
typeof haexPasswordsItemHistory.$inferSelect
|
||||
|
||||
export const haexPasswordsGroups = sqliteTable(
|
||||
tableNames.haex.passwords.groups,
|
||||
{
|
||||
id: text().primaryKey(),
|
||||
name: text(),
|
||||
description: text(),
|
||||
icon: text(),
|
||||
order: integer(),
|
||||
color: text(),
|
||||
parentId: text('parent_id').references(
|
||||
(): AnySQLiteColumn => haexPasswordsGroups.id,
|
||||
{ onDelete: 'cascade' },
|
||||
),
|
||||
createdAt: text('created_at').default(sql`(CURRENT_TIMESTAMP)`),
|
||||
updateAt: integer('updated_at', { mode: 'timestamp' }).$onUpdate(
|
||||
() => new Date(),
|
||||
),
|
||||
haex_tombstone: integer({ mode: 'boolean' }),
|
||||
},
|
||||
)
|
||||
export type InsertHaexPasswordsGroups = typeof haexPasswordsGroups.$inferInsert
|
||||
export type SelectHaexPasswordsGroups = typeof haexPasswordsGroups.$inferSelect
|
||||
|
||||
export const haexPasswordsGroupItems = sqliteTable(
|
||||
tableNames.haex.passwords.group_items,
|
||||
{
|
||||
groupId: text('group_id')
|
||||
.notNull()
|
||||
.references((): AnySQLiteColumn => haexPasswordsGroups.id, {
|
||||
onDelete: 'cascade',
|
||||
}),
|
||||
itemId: text('item_id')
|
||||
.notNull()
|
||||
.references((): AnySQLiteColumn => haexPasswordsItemDetails.id, {
|
||||
onDelete: 'cascade',
|
||||
}),
|
||||
haex_tombstone: integer({ mode: 'boolean' }),
|
||||
},
|
||||
(table) => [primaryKey({ columns: [table.itemId, table.groupId] })],
|
||||
)
|
||||
export type InsertHaexPasswordsGroupItems =
|
||||
typeof haexPasswordsGroupItems.$inferInsert
|
||||
export type SelectHaexPasswordsGroupItems =
|
||||
typeof haexPasswordsGroupItems.$inferSelect
|
||||
@ -7,7 +7,7 @@
|
||||
"key": "key",
|
||||
"type": "type",
|
||||
"value": "value",
|
||||
"haexTombstone": "haex_tombstone",
|
||||
|
||||
"haexTimestamp": "haex_timestamp"
|
||||
}
|
||||
},
|
||||
@ -26,7 +26,7 @@
|
||||
"signature": "signature",
|
||||
"url": "url",
|
||||
"version": "version",
|
||||
"haexTombstone": "haex_tombstone",
|
||||
|
||||
"haexTimestamp": "haex_timestamp"
|
||||
}
|
||||
},
|
||||
@ -42,7 +42,7 @@
|
||||
"status": "status",
|
||||
"createdAt": "created_at",
|
||||
"updateAt": "updated_at",
|
||||
"haexTombstone": "haex_tombstone",
|
||||
|
||||
"haexTimestamp": "haex_timestamp"
|
||||
}
|
||||
},
|
||||
@ -59,7 +59,7 @@
|
||||
"text": "text",
|
||||
"title": "title",
|
||||
"type": "type",
|
||||
"haexTombstone": "haex_tombstone",
|
||||
|
||||
"haexTimestamp": "haex_timestamp"
|
||||
}
|
||||
},
|
||||
@ -70,7 +70,7 @@
|
||||
"name": "name",
|
||||
"position": "position",
|
||||
"createdAt": "created_at",
|
||||
"haexTombstone": "haex_tombstone",
|
||||
|
||||
"haexTimestamp": "haex_timestamp"
|
||||
}
|
||||
},
|
||||
@ -83,17 +83,11 @@
|
||||
"referenceId": "reference_id",
|
||||
"positionX": "position_x",
|
||||
"positionY": "position_y",
|
||||
"haexTombstone": "haex_tombstone",
|
||||
|
||||
"haexTimestamp": "haex_timestamp"
|
||||
}
|
||||
},
|
||||
"passwords": {
|
||||
"groups": "haex_passwords_groups",
|
||||
"group_items": "haex_passwords_group_items",
|
||||
"item_details": "haex_passwords_item_details",
|
||||
"item_key_values": "haex_passwords_item_key_values",
|
||||
"item_histories": "haex_passwords_item_history"
|
||||
},
|
||||
|
||||
"crdt": {
|
||||
"logs": {
|
||||
"name": "haex_crdt_logs",
|
||||
|
||||
Binary file not shown.
@ -1,5 +1,6 @@
|
||||
// src-tarui/src/build/table_names.rs
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
@ -8,25 +9,7 @@ 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,
|
||||
desktop_items: TableDefinition,
|
||||
crdt: Crdt,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Crdt {
|
||||
logs: TableDefinition,
|
||||
snapshots: TableDefinition,
|
||||
configs: TableDefinition,
|
||||
haex: HashMap<String, Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@ -45,185 +28,39 @@ pub fn generate_table_names() {
|
||||
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!(
|
||||
let mut code = String::from(
|
||||
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_desktop_items ---
|
||||
pub const TABLE_DESKTOP_ITEMS: &str = "{t_desktop_items}";
|
||||
pub const COL_DESKTOP_ITEMS_ID: &str = "{c_desktop_id}";
|
||||
pub const COL_DESKTOP_ITEMS_ITEM_TYPE: &str = "{c_desktop_itemType}";
|
||||
pub const COL_DESKTOP_ITEMS_REFERENCE_ID: &str = "{c_desktop_referenceId}";
|
||||
pub const COL_DESKTOP_ITEMS_POSITION_X: &str = "{c_desktop_positionX}";
|
||||
pub const COL_DESKTOP_ITEMS_POSITION_Y: &str = "{c_desktop_positionY}";
|
||||
pub const COL_DESKTOP_ITEMS_HAEX_TOMBSTONE: &str = "{c_desktop_tombstone}";
|
||||
pub const COL_DESKTOP_ITEMS_HAEX_TIMESTAMP: &str = "{c_desktop_timestamp}";
|
||||
|
||||
// --- 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"],
|
||||
// Desktop Items
|
||||
t_desktop_items = haex.desktop_items.name,
|
||||
c_desktop_id = haex.desktop_items.columns["id"],
|
||||
c_desktop_itemType = haex.desktop_items.columns["itemType"],
|
||||
c_desktop_referenceId = haex.desktop_items.columns["referenceId"],
|
||||
c_desktop_positionX = haex.desktop_items.columns["positionX"],
|
||||
c_desktop_positionY = haex.desktop_items.columns["positionY"],
|
||||
c_desktop_tombstone = haex.desktop_items.columns["haexTombstone"],
|
||||
c_desktop_timestamp = haex.desktop_items.columns["haexTimestamp"],
|
||||
// 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"]
|
||||
);
|
||||
|
||||
// Dynamisch über alle Einträge in haex iterieren
|
||||
for (key, value) in &schema.haex {
|
||||
// Spezialbehandlung für nested structures wie "crdt"
|
||||
if key == "crdt" {
|
||||
if let Some(crdt_obj) = value.as_object() {
|
||||
for (crdt_key, crdt_value) in crdt_obj {
|
||||
if let Ok(table) = serde_json::from_value::<TableDefinition>(crdt_value.clone())
|
||||
{
|
||||
let const_prefix = format!("CRDT_{}", to_screaming_snake_case(crdt_key));
|
||||
code.push_str(&generate_table_constants(&table, &const_prefix));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Normale Tabelle (settings, extensions, notifications, workspaces, desktop_items, etc.)
|
||||
if let Ok(table) = serde_json::from_value::<TableDefinition>(value.clone()) {
|
||||
let const_prefix = to_screaming_snake_case(key);
|
||||
code.push_str(&generate_table_constants(&table, &const_prefix));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Datei schreiben ---
|
||||
let mut f = File::create(&dest_path).expect("Konnte Zieldatei nicht erstellen");
|
||||
f.write_all(code.as_bytes())
|
||||
@ -231,3 +68,51 @@ pub const COL_CRDT_CONFIGS_VALUE: &str = "{c_crdt_configs_value}";
|
||||
|
||||
println!("cargo:rerun-if-changed=database/tableNames.json");
|
||||
}
|
||||
|
||||
/// Konvertiert einen String zu SCREAMING_SNAKE_CASE
|
||||
fn to_screaming_snake_case(s: &str) -> String {
|
||||
let mut result = String::new();
|
||||
let mut prev_is_lower = false;
|
||||
|
||||
for (i, ch) in s.chars().enumerate() {
|
||||
if ch == '_' {
|
||||
result.push('_');
|
||||
prev_is_lower = false;
|
||||
} else if ch.is_uppercase() {
|
||||
if i > 0 && prev_is_lower {
|
||||
result.push('_');
|
||||
}
|
||||
result.push(ch);
|
||||
prev_is_lower = false;
|
||||
} else {
|
||||
result.push(ch.to_ascii_uppercase());
|
||||
prev_is_lower = true;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Generiert die Konstanten für eine Tabelle
|
||||
fn generate_table_constants(table: &TableDefinition, const_prefix: &str) -> String {
|
||||
let mut code = String::new();
|
||||
|
||||
// Tabellenname
|
||||
code.push_str(&format!("// --- Table: {} ---\n", table.name));
|
||||
code.push_str(&format!(
|
||||
"pub const TABLE_{}: &str = \"{}\";\n",
|
||||
const_prefix, table.name
|
||||
));
|
||||
|
||||
// Spalten
|
||||
for (col_key, col_value) in &table.columns {
|
||||
let col_const_name = format!("COL_{}_{}", const_prefix, to_screaming_snake_case(col_key));
|
||||
code.push_str(&format!(
|
||||
"pub const {}: &str = \"{}\";\n",
|
||||
col_const_name, col_value
|
||||
));
|
||||
}
|
||||
|
||||
code.push('\n');
|
||||
code
|
||||
}
|
||||
|
||||
@ -1,21 +1,19 @@
|
||||
// src-tauri/src/crdt/insert_transformer.rs
|
||||
// INSERT-spezifische CRDT-Transformationen (ON CONFLICT, RETURNING)
|
||||
|
||||
use crate::crdt::trigger::{HLC_TIMESTAMP_COLUMN, TOMBSTONE_COLUMN};
|
||||
use crate::crdt::trigger::HLC_TIMESTAMP_COLUMN;
|
||||
use crate::database::error::DatabaseError;
|
||||
use sqlparser::ast::{Expr, Ident, Insert, SelectItem, SetExpr, Value};
|
||||
use uhlc::Timestamp;
|
||||
|
||||
/// Helper-Struct für INSERT-Transformationen
|
||||
pub struct InsertTransformer {
|
||||
tombstone_column: &'static str,
|
||||
hlc_timestamp_column: &'static str,
|
||||
}
|
||||
|
||||
impl InsertTransformer {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
tombstone_column: TOMBSTONE_COLUMN,
|
||||
hlc_timestamp_column: HLC_TIMESTAMP_COLUMN,
|
||||
}
|
||||
}
|
||||
@ -58,11 +56,9 @@ impl InsertTransformer {
|
||||
insert_stmt: &mut Insert,
|
||||
timestamp: &Timestamp,
|
||||
) -> Result<(), DatabaseError> {
|
||||
// Add both haex_timestamp and haex_tombstone columns if not exists
|
||||
// Add haex_timestamp column if not exists
|
||||
let hlc_col_index =
|
||||
Self::find_or_add_column(&mut insert_stmt.columns, self.hlc_timestamp_column);
|
||||
let tombstone_col_index =
|
||||
Self::find_or_add_column(&mut insert_stmt.columns, self.tombstone_column);
|
||||
|
||||
// ON CONFLICT Logik komplett entfernt!
|
||||
// Bei Hard Deletes gibt es keine Tombstone-Einträge mehr zu reaktivieren
|
||||
@ -74,24 +70,15 @@ impl InsertTransformer {
|
||||
for row in &mut values.rows {
|
||||
let hlc_value =
|
||||
Expr::Value(Value::SingleQuotedString(timestamp.to_string()).into());
|
||||
let tombstone_value =
|
||||
Expr::Value(Value::Number("0".to_string(), false).into());
|
||||
|
||||
Self::set_or_push_value(row, hlc_col_index, hlc_value);
|
||||
Self::set_or_push_value(row, tombstone_col_index, tombstone_value);
|
||||
}
|
||||
}
|
||||
SetExpr::Select(select) => {
|
||||
let hlc_value =
|
||||
Expr::Value(Value::SingleQuotedString(timestamp.to_string()).into());
|
||||
let tombstone_value = Expr::Value(Value::Number("0".to_string(), false).into());
|
||||
|
||||
Self::set_or_push_projection(&mut select.projection, hlc_col_index, hlc_value);
|
||||
Self::set_or_push_projection(
|
||||
&mut select.projection,
|
||||
tombstone_col_index,
|
||||
tombstone_value,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
return Err(DatabaseError::UnsupportedStatement {
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
// src-tauri/src/crdt/transformer.rs
|
||||
|
||||
use crate::crdt::insert_transformer::InsertTransformer;
|
||||
use crate::crdt::trigger::{HLC_TIMESTAMP_COLUMN, TOMBSTONE_COLUMN};
|
||||
use crate::crdt::trigger::HLC_TIMESTAMP_COLUMN;
|
||||
use crate::database::error::DatabaseError;
|
||||
use crate::table_names::{TABLE_CRDT_CONFIGS, TABLE_CRDT_LOGS};
|
||||
use sqlparser::ast::{
|
||||
Assignment, AssignmentTarget, ColumnDef, DataType, Expr, Ident, ObjectName,
|
||||
ObjectNamePart, Statement, TableFactor, TableObject, Value,
|
||||
Assignment, AssignmentTarget, ColumnDef, DataType, Expr, Ident, ObjectName, ObjectNamePart,
|
||||
Statement, TableFactor, TableObject, Value,
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashSet;
|
||||
@ -15,13 +15,11 @@ use uhlc::Timestamp;
|
||||
/// Konfiguration für CRDT-Spalten
|
||||
#[derive(Clone)]
|
||||
struct CrdtColumns {
|
||||
tombstone: &'static str,
|
||||
hlc_timestamp: &'static str,
|
||||
}
|
||||
|
||||
impl CrdtColumns {
|
||||
const DEFAULT: Self = Self {
|
||||
tombstone: TOMBSTONE_COLUMN,
|
||||
hlc_timestamp: HLC_TIMESTAMP_COLUMN,
|
||||
};
|
||||
|
||||
@ -37,13 +35,6 @@ impl CrdtColumns {
|
||||
|
||||
/// Fügt CRDT-Spalten zu einer Tabellendefinition hinzu
|
||||
fn add_to_table_definition(&self, columns: &mut Vec<ColumnDef>) {
|
||||
if !columns.iter().any(|c| c.name.value == self.tombstone) {
|
||||
columns.push(ColumnDef {
|
||||
name: Ident::new(self.tombstone),
|
||||
data_type: DataType::Integer(None),
|
||||
options: vec![],
|
||||
});
|
||||
}
|
||||
if !columns.iter().any(|c| c.name.value == self.hlc_timestamp) {
|
||||
columns.push(ColumnDef {
|
||||
name: Ident::new(self.hlc_timestamp),
|
||||
@ -86,7 +77,7 @@ impl CrdtTransformer {
|
||||
// =================================================================
|
||||
// ÖFFENTLICHE API-METHODEN
|
||||
// =================================================================
|
||||
|
||||
|
||||
pub fn transform_execute_statement_with_table_info(
|
||||
&self,
|
||||
stmt: &mut Statement,
|
||||
@ -171,7 +162,7 @@ impl CrdtTransformer {
|
||||
Statement::Update {
|
||||
table, assignments, ..
|
||||
} => {
|
||||
if let TableFactor::Table { name, ..} = &table.relation {
|
||||
if let TableFactor::Table { name, .. } = &table.relation {
|
||||
if self.is_crdt_sync_table(name) {
|
||||
assignments.push(self.columns.create_hlc_assignment(hlc_timestamp));
|
||||
}
|
||||
|
||||
@ -12,18 +12,16 @@ const UPDATE_TRIGGER_TPL: &str = "z_crdt_{TABLE_NAME}_update";
|
||||
const DELETE_TRIGGER_TPL: &str = "z_crdt_{TABLE_NAME}_delete";
|
||||
|
||||
//const SYNC_ACTIVE_KEY: &str = "sync_active";
|
||||
pub const TOMBSTONE_COLUMN: &str = "haex_tombstone";
|
||||
|
||||
pub const HLC_TIMESTAMP_COLUMN: &str = "haex_timestamp";
|
||||
|
||||
/// Name der custom UUID-Generierungs-Funktion (registriert in database::core::open_and_init_db)
|
||||
pub const UUID_FUNCTION_NAME: &str = "gen_uuid";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CrdtSetupError {
|
||||
/// Kapselt einen Fehler, der von der rusqlite-Bibliothek kommt.
|
||||
DatabaseError(rusqlite::Error),
|
||||
/// Die Tabelle hat keine Tombstone-Spalte, was eine CRDT-Voraussetzung ist.
|
||||
TombstoneColumnMissing {
|
||||
table_name: String,
|
||||
column_name: String,
|
||||
},
|
||||
HlcColumnMissing {
|
||||
table_name: String,
|
||||
column_name: String,
|
||||
@ -37,14 +35,6 @@ impl Display for CrdtSetupError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
CrdtSetupError::DatabaseError(e) => write!(f, "Database error: {}", e),
|
||||
CrdtSetupError::TombstoneColumnMissing {
|
||||
table_name,
|
||||
column_name,
|
||||
} => write!(
|
||||
f,
|
||||
"Table '{}' is missing the required tombstone column '{}'",
|
||||
table_name, column_name
|
||||
),
|
||||
CrdtSetupError::HlcColumnMissing {
|
||||
table_name,
|
||||
column_name,
|
||||
@ -110,13 +100,6 @@ pub fn setup_triggers_for_table(
|
||||
return Ok(TriggerSetupResult::TableNotFound);
|
||||
}
|
||||
|
||||
if !columns.iter().any(|c| c.name == TOMBSTONE_COLUMN) {
|
||||
return Err(CrdtSetupError::TombstoneColumnMissing {
|
||||
table_name: table_name.to_string(),
|
||||
column_name: TOMBSTONE_COLUMN.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
if !columns.iter().any(|c| c.name == HLC_TIMESTAMP_COLUMN) {
|
||||
return Err(CrdtSetupError::HlcColumnMissing {
|
||||
table_name: table_name.to_string(),
|
||||
@ -138,7 +121,7 @@ pub fn setup_triggers_for_table(
|
||||
|
||||
let cols_to_track: Vec<String> = columns
|
||||
.iter()
|
||||
.filter(|c| !c.is_pk) //&& c.name != TOMBSTONE_COLUMN && c.name != HLC_TIMESTAMP_COLUMN
|
||||
.filter(|c| !c.is_pk)
|
||||
.map(|c| c.name.clone())
|
||||
.collect();
|
||||
|
||||
@ -269,9 +252,10 @@ fn generate_insert_trigger_sql(table_name: &str, pks: &[String], cols: &[String]
|
||||
let column_inserts = if cols.is_empty() {
|
||||
// Nur PKs -> einfacher Insert ins Log
|
||||
format!(
|
||||
"INSERT INTO {log_table} (haex_timestamp, op_type, table_name, row_pks)
|
||||
VALUES (NEW.\"{hlc_col}\", 'INSERT', '{table}', json_object({pk_payload}));",
|
||||
"INSERT INTO {log_table} (id, haex_timestamp, op_type, table_name, row_pks)
|
||||
VALUES ({uuid_fn}(), NEW.\"{hlc_col}\", 'INSERT', '{table}', json_object({pk_payload}));",
|
||||
log_table = TABLE_CRDT_LOGS,
|
||||
uuid_fn = UUID_FUNCTION_NAME,
|
||||
hlc_col = HLC_TIMESTAMP_COLUMN,
|
||||
table = table_name,
|
||||
pk_payload = pk_json_payload
|
||||
@ -280,9 +264,10 @@ fn generate_insert_trigger_sql(table_name: &str, pks: &[String], cols: &[String]
|
||||
cols.iter().fold(String::new(), |mut acc, col| {
|
||||
writeln!(
|
||||
&mut acc,
|
||||
"INSERT INTO {log_table} (haex_timestamp, op_type, table_name, row_pks, column_name, new_value)
|
||||
VALUES (NEW.\"{hlc_col}\", 'INSERT', '{table}', json_object({pk_payload}), '{column}', json_object('value', NEW.\"{column}\"));",
|
||||
"INSERT INTO {log_table} (id, haex_timestamp, op_type, table_name, row_pks, column_name, new_value)
|
||||
VALUES ({uuid_fn}(), NEW.\"{hlc_col}\", 'INSERT', '{table}', json_object({pk_payload}), '{column}', json_object('value', NEW.\"{column}\"));",
|
||||
log_table = TABLE_CRDT_LOGS,
|
||||
uuid_fn = UUID_FUNCTION_NAME,
|
||||
hlc_col = HLC_TIMESTAMP_COLUMN,
|
||||
table = table_name,
|
||||
pk_payload = pk_json_payload,
|
||||
@ -324,11 +309,12 @@ fn generate_update_trigger_sql(table_name: &str, pks: &[String], cols: &[String]
|
||||
for col in cols {
|
||||
writeln!(
|
||||
&mut body,
|
||||
"INSERT INTO {log_table} (haex_timestamp, op_type, table_name, row_pks, column_name, new_value, old_value)
|
||||
SELECT NEW.\"{hlc_col}\", 'UPDATE', '{table}', json_object({pk_payload}), '{column}',
|
||||
"INSERT INTO {log_table} (id, haex_timestamp, op_type, table_name, row_pks, column_name, new_value, old_value)
|
||||
SELECT {uuid_fn}(), NEW.\"{hlc_col}\", 'UPDATE', '{table}', json_object({pk_payload}), '{column}',
|
||||
json_object('value', NEW.\"{column}\"), json_object('value', OLD.\"{column}\")
|
||||
WHERE NEW.\"{column}\" IS NOT OLD.\"{column}\";",
|
||||
log_table = TABLE_CRDT_LOGS,
|
||||
uuid_fn = UUID_FUNCTION_NAME,
|
||||
hlc_col = HLC_TIMESTAMP_COLUMN,
|
||||
table = table_name,
|
||||
pk_payload = pk_json_payload,
|
||||
@ -367,10 +353,11 @@ fn generate_delete_trigger_sql(table_name: &str, pks: &[String], cols: &[String]
|
||||
for col in cols {
|
||||
writeln!(
|
||||
&mut body,
|
||||
"INSERT INTO {log_table} (haex_timestamp, op_type, table_name, row_pks, column_name, old_value)
|
||||
VALUES (OLD.\"{hlc_col}\", 'DELETE', '{table}', json_object({pk_payload}), '{column}',
|
||||
"INSERT INTO {log_table} (id, haex_timestamp, op_type, table_name, row_pks, column_name, old_value)
|
||||
VALUES ({uuid_fn}(), OLD.\"{hlc_col}\", 'DELETE', '{table}', json_object({pk_payload}), '{column}',
|
||||
json_object('value', OLD.\"{column}\"));",
|
||||
log_table = TABLE_CRDT_LOGS,
|
||||
uuid_fn = UUID_FUNCTION_NAME,
|
||||
hlc_col = HLC_TIMESTAMP_COLUMN,
|
||||
table = table_name,
|
||||
pk_payload = pk_json_payload,
|
||||
@ -381,13 +368,15 @@ fn generate_delete_trigger_sql(table_name: &str, pks: &[String], cols: &[String]
|
||||
// Nur PKs -> minimales Delete Log
|
||||
writeln!(
|
||||
&mut body,
|
||||
"INSERT INTO {log_table} (haex_timestamp, op_type, table_name, row_pks)
|
||||
VALUES (OLD.\"{hlc_col}\", 'DELETE', '{table}', json_object({pk_payload}));",
|
||||
"INSERT INTO {log_table} (id, haex_timestamp, op_type, table_name, row_pks)
|
||||
VALUES ({uuid_fn}(), OLD.\"{hlc_col}\", 'DELETE', '{table}', json_object({pk_payload}));",
|
||||
log_table = TABLE_CRDT_LOGS,
|
||||
uuid_fn = UUID_FUNCTION_NAME,
|
||||
hlc_col = HLC_TIMESTAMP_COLUMN,
|
||||
table = table_name,
|
||||
pk_payload = pk_json_payload
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let trigger_name = DELETE_TRIGGER_TPL.replace("{TABLE_NAME}", table_name);
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
// src-tauri/src/database/core.rs
|
||||
|
||||
use crate::crdt::trigger::UUID_FUNCTION_NAME;
|
||||
use crate::database::error::DatabaseError;
|
||||
use crate::database::DbConnection;
|
||||
use crate::extension::database::executor::SqlExecutor;
|
||||
use base64::{engine::general_purpose::STANDARD, Engine as _};
|
||||
use rusqlite::functions::FunctionFlags;
|
||||
use rusqlite::types::Value as SqlValue;
|
||||
use rusqlite::{
|
||||
types::{Value as RusqliteValue, ValueRef},
|
||||
@ -13,6 +15,7 @@ use serde_json::Value as JsonValue;
|
||||
use sqlparser::ast::{Expr, Query, Select, SetExpr, Statement, TableFactor, TableObject};
|
||||
use sqlparser::dialect::SQLiteDialect;
|
||||
use sqlparser::parser::Parser;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Öffnet und initialisiert eine Datenbank mit Verschlüsselung
|
||||
pub fn open_and_init_db(path: &str, key: &str, create: bool) -> Result<Connection, DatabaseError> {
|
||||
@ -34,6 +37,19 @@ pub fn open_and_init_db(path: &str, key: &str, create: bool) -> Result<Connectio
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
|
||||
// Register custom UUID function for SQLite triggers
|
||||
conn.create_scalar_function(
|
||||
UUID_FUNCTION_NAME,
|
||||
0,
|
||||
FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC,
|
||||
|_ctx| {
|
||||
Ok(Uuid::new_v4().to_string())
|
||||
},
|
||||
)
|
||||
.map_err(|e| DatabaseError::DatabaseError {
|
||||
reason: format!("Failed to register {} function: {}", UUID_FUNCTION_NAME, e),
|
||||
})?;
|
||||
|
||||
let journal_mode: String = conn
|
||||
.query_row("PRAGMA journal_mode=WAL;", [], |row| row.get(0))
|
||||
.map_err(|e| DatabaseError::PragmaError {
|
||||
@ -236,7 +252,7 @@ pub fn select_with_crdt(
|
||||
connection: &DbConnection,
|
||||
) -> Result<Vec<Vec<JsonValue>>, DatabaseError> {
|
||||
with_connection(&connection, |conn| {
|
||||
SqlExecutor::select_internal(conn, &sql, ¶ms)
|
||||
SqlExecutor::query_select(conn, &sql, ¶ms)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -16,8 +16,6 @@ pub struct HaexSettings {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub value: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub haex_tombstone: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub haex_timestamp: Option<String>,
|
||||
}
|
||||
|
||||
@ -28,8 +26,7 @@ impl HaexSettings {
|
||||
key: row.get(1)?,
|
||||
r#type: row.get(2)?,
|
||||
value: row.get(3)?,
|
||||
haex_tombstone: row.get(4)?,
|
||||
haex_timestamp: row.get(5)?,
|
||||
haex_timestamp: row.get(4)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -54,8 +51,6 @@ pub struct HaexExtensions {
|
||||
pub icon: Option<String>,
|
||||
pub signature: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub haex_tombstone: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub haex_timestamp: Option<String>,
|
||||
}
|
||||
|
||||
@ -73,8 +68,7 @@ impl HaexExtensions {
|
||||
enabled: row.get(8)?,
|
||||
icon: row.get(9)?,
|
||||
signature: row.get(10)?,
|
||||
haex_tombstone: row.get(11)?,
|
||||
haex_timestamp: row.get(12)?,
|
||||
haex_timestamp: row.get(11)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -83,8 +77,7 @@ impl HaexExtensions {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct HaexExtensionPermissions {
|
||||
pub id: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub extension_id: Option<String>,
|
||||
pub extension_id: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub resource_type: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@ -99,8 +92,6 @@ pub struct HaexExtensionPermissions {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub updated_at: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub haex_tombstone: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub haex_timestamp: Option<String>,
|
||||
}
|
||||
|
||||
@ -116,8 +107,7 @@ impl HaexExtensionPermissions {
|
||||
status: row.get(6)?,
|
||||
created_at: row.get(7)?,
|
||||
updated_at: row.get(8)?,
|
||||
haex_tombstone: row.get(9)?,
|
||||
haex_timestamp: row.get(10)?,
|
||||
haex_timestamp: row.get(9)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -200,3 +190,51 @@ impl HaexCrdtConfigs {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct HaexDesktopItems {
|
||||
pub id: String,
|
||||
pub workspace_id: String,
|
||||
pub item_type: String,
|
||||
pub reference_id: String,
|
||||
pub position_x: i64,
|
||||
pub position_y: i64,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub haex_timestamp: Option<String>,
|
||||
}
|
||||
|
||||
impl HaexDesktopItems {
|
||||
pub fn from_row(row: &rusqlite::Row) -> rusqlite::Result<Self> {
|
||||
Ok(Self {
|
||||
id: row.get(0)?,
|
||||
workspace_id: row.get(1)?,
|
||||
item_type: row.get(2)?,
|
||||
reference_id: row.get(3)?,
|
||||
position_x: row.get(4)?,
|
||||
position_y: row.get(5)?,
|
||||
haex_timestamp: row.get(6)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct HaexWorkspaces {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub position: i64,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub haex_timestamp: Option<String>,
|
||||
}
|
||||
|
||||
impl HaexWorkspaces {
|
||||
pub fn from_row(row: &rusqlite::Row) -> rusqlite::Result<Self> {
|
||||
Ok(Self {
|
||||
id: row.get(0)?,
|
||||
name: row.get(1)?,
|
||||
position: row.get(2)?,
|
||||
haex_timestamp: row.get(3)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
67
src-tauri/src/database/init.rs
Normal file
67
src-tauri/src/database/init.rs
Normal file
@ -0,0 +1,67 @@
|
||||
// src-tauri/src/database/init.rs
|
||||
// Database initialization utilities (trigger setup, etc.)
|
||||
|
||||
use crate::crdt::trigger;
|
||||
use crate::database::error::DatabaseError;
|
||||
use crate::table_names::{
|
||||
TABLE_DESKTOP_ITEMS,
|
||||
TABLE_EXTENSIONS,
|
||||
TABLE_EXTENSION_PERMISSIONS,
|
||||
TABLE_NOTIFICATIONS,
|
||||
TABLE_SETTINGS,
|
||||
TABLE_WORKSPACES,
|
||||
};
|
||||
use rusqlite::{params, Connection};
|
||||
|
||||
/// Liste aller CRDT-Tabellen die Trigger benötigen (ohne Password-Tabellen - die kommen in Extension)
|
||||
const CRDT_TABLES: &[&str] = &[
|
||||
TABLE_SETTINGS,
|
||||
TABLE_EXTENSIONS,
|
||||
TABLE_EXTENSION_PERMISSIONS,
|
||||
TABLE_NOTIFICATIONS,
|
||||
TABLE_WORKSPACES,
|
||||
TABLE_DESKTOP_ITEMS,
|
||||
];
|
||||
|
||||
/// Prüft ob Trigger bereits initialisiert wurden und erstellt sie falls nötig
|
||||
///
|
||||
/// Diese Funktion wird beim ersten Öffnen einer Template-DB aufgerufen.
|
||||
/// Sie erstellt alle CRDT-Trigger für die definierten Tabellen und markiert
|
||||
/// die Initialisierung in haex_settings.
|
||||
///
|
||||
/// Bei Migrations (ALTER TABLE) werden Trigger automatisch neu erstellt,
|
||||
/// daher ist kein Versioning nötig.
|
||||
pub fn ensure_triggers_initialized(conn: &mut Connection) -> Result<bool, DatabaseError> {
|
||||
let tx = conn.transaction()?;
|
||||
|
||||
// Check if triggers already initialized
|
||||
let check_sql = format!(
|
||||
"SELECT value FROM {} WHERE key = ? AND type = ?",
|
||||
TABLE_SETTINGS
|
||||
);
|
||||
let initialized: Option<String> = tx
|
||||
.query_row(
|
||||
&check_sql,
|
||||
params!["triggers_initialized", "system"],
|
||||
|row| row.get(0),
|
||||
)
|
||||
.ok();
|
||||
|
||||
if initialized.is_some() {
|
||||
eprintln!("DEBUG: Triggers already initialized, skipping");
|
||||
tx.commit()?; // Wichtig: Transaktion trotzdem abschließen
|
||||
return Ok(true); // true = war schon initialisiert
|
||||
}
|
||||
|
||||
eprintln!("INFO: Initializing CRDT triggers for database...");
|
||||
|
||||
// Create triggers for all CRDT tables
|
||||
for table_name in CRDT_TABLES {
|
||||
eprintln!(" - Setting up triggers for: {}", table_name);
|
||||
trigger::setup_triggers_for_table(&tx, table_name, false)?;
|
||||
}
|
||||
|
||||
tx.commit()?;
|
||||
eprintln!("INFO: ✓ CRDT triggers created successfully (flag pending)");
|
||||
Ok(false) // false = wurde gerade initialisiert
|
||||
}
|
||||
@ -3,11 +3,13 @@
|
||||
pub mod core;
|
||||
pub mod error;
|
||||
pub mod generated;
|
||||
pub mod init;
|
||||
|
||||
use crate::crdt::hlc::HlcService;
|
||||
use crate::database::core::execute_with_crdt;
|
||||
use crate::database::error::DatabaseError;
|
||||
use crate::extension::database::executor::SqlExecutor;
|
||||
use crate::table_names::TABLE_CRDT_CONFIGS;
|
||||
use crate::table_names::{TABLE_CRDT_CONFIGS, TABLE_SETTINGS};
|
||||
use crate::AppState;
|
||||
use rusqlite::Connection;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -76,7 +78,8 @@ pub fn sql_query_with_crdt(
|
||||
|
||||
core::with_connection(&state.db, |conn| {
|
||||
let tx = conn.transaction().map_err(DatabaseError::from)?;
|
||||
let result = SqlExecutor::query_internal(&tx, &hlc_service, &sql, ¶ms)?;
|
||||
let (_modified_tables, result) =
|
||||
SqlExecutor::query_internal(&tx, &hlc_service, &sql, ¶ms)?;
|
||||
tx.commit().map_err(DatabaseError::from)?;
|
||||
Ok(result)
|
||||
})
|
||||
@ -417,9 +420,12 @@ fn initialize_session(
|
||||
state: &State<'_, AppState>,
|
||||
) -> Result<(), DatabaseError> {
|
||||
// 1. Establish the raw database connection
|
||||
let conn = core::open_and_init_db(path, key, false)?;
|
||||
let mut conn = core::open_and_init_db(path, key, false)?;
|
||||
|
||||
// 2. Initialize the HLC service
|
||||
// 2. Ensure CRDT triggers are initialized (for template DB)
|
||||
let triggers_were_already_initialized = init::ensure_triggers_initialized(&mut conn)?;
|
||||
|
||||
// 3. Initialize the HLC service
|
||||
let hlc_service = HlcService::try_initialize(&conn, app_handle).map_err(|e| {
|
||||
// We convert the HlcError into a DatabaseError
|
||||
DatabaseError::ExecutionError {
|
||||
@ -429,16 +435,53 @@ fn initialize_session(
|
||||
}
|
||||
})?;
|
||||
|
||||
// 3. Store everything in the global AppState
|
||||
// 4. Store everything in the global AppState
|
||||
let mut db_guard = state.db.0.lock().map_err(|e| DatabaseError::LockError {
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
// Wichtig: Wir brauchen den db_guard gleich nicht mehr,
|
||||
// da 'execute_with_crdt' 'with_connection' aufruft, was
|
||||
// 'state.db' selbst locken muss.
|
||||
// Wir müssen den Guard freigeben, *bevor* wir 'execute_with_crdt' rufen,
|
||||
// um einen Deadlock zu verhindern.
|
||||
// Aber wir müssen die 'conn' erst hineinbewegen.
|
||||
*db_guard = Some(conn);
|
||||
drop(db_guard);
|
||||
|
||||
let mut hlc_guard = state.hlc.lock().map_err(|e| DatabaseError::LockError {
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
*hlc_guard = hlc_service;
|
||||
|
||||
// WICHTIG: hlc_guard *nicht* freigeben, da 'execute_with_crdt'
|
||||
// eine Referenz auf die Guard erwartet.
|
||||
|
||||
// 5. NEUER SCHRITT: Setze das Flag via CRDT, falls nötig
|
||||
if !triggers_were_already_initialized {
|
||||
eprintln!("INFO: Setting 'triggers_initialized' flag via CRDT...");
|
||||
|
||||
let insert_sql = format!(
|
||||
"INSERT INTO {} (id, key, type, value) VALUES (?, ?, ?, ?)",
|
||||
TABLE_SETTINGS
|
||||
);
|
||||
|
||||
// execute_with_crdt erwartet Vec<JsonValue>, kein params!-Makro
|
||||
let params_vec: Vec<JsonValue> = vec![
|
||||
JsonValue::String(uuid::Uuid::new_v4().to_string()),
|
||||
JsonValue::String("triggers_initialized".to_string()),
|
||||
JsonValue::String("system".to_string()),
|
||||
JsonValue::String("1".to_string()),
|
||||
];
|
||||
|
||||
// Jetzt können wir 'execute_with_crdt' sicher aufrufen,
|
||||
// da der AppState initialisiert ist.
|
||||
execute_with_crdt(
|
||||
insert_sql, params_vec, &state.db, // Das &DbConnection (der Mutex)
|
||||
&hlc_guard, // Die gehaltene MutexGuard
|
||||
)?;
|
||||
|
||||
eprintln!("INFO: ✓ 'triggers_initialized' flag set.");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ use crate::extension::core::manifest::{EditablePermissions, ExtensionManifest, E
|
||||
use crate::extension::core::types::{copy_directory, Extension, ExtensionSource};
|
||||
use crate::extension::core::ExtensionPermissions;
|
||||
use crate::extension::crypto::ExtensionCrypto;
|
||||
use crate::extension::database::executor::{PkRemappingContext, SqlExecutor};
|
||||
use crate::extension::database::executor::SqlExecutor;
|
||||
use crate::extension::error::ExtensionError;
|
||||
use crate::extension::permissions::manager::PermissionManager;
|
||||
use crate::extension::permissions::types::ExtensionPermission;
|
||||
@ -478,19 +478,13 @@ impl ExtensionManager {
|
||||
let hlc_service = hlc_service_guard.clone();
|
||||
drop(hlc_service_guard);
|
||||
|
||||
// Erstelle PK-Remapping Context für die gesamte Transaktion
|
||||
// Dies ermöglicht automatisches FK-Remapping wenn ON CONFLICT bei Extension auftritt
|
||||
let mut pk_context = PkRemappingContext::new();
|
||||
|
||||
// 1. Extension-Eintrag erstellen mit generierter UUID
|
||||
// WICHTIG: RETURNING wird vom CRDT-Transformer automatisch hinzugefügt
|
||||
let insert_ext_sql = format!(
|
||||
"INSERT INTO {} (id, name, version, author, entry, icon, public_key, signature, homepage, description, enabled) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING id",
|
||||
"INSERT INTO {} (id, name, version, author, entry, icon, public_key, signature, homepage, description, enabled) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
TABLE_EXTENSIONS
|
||||
);
|
||||
|
||||
let (_tables, returning_results): (HashSet<String>, Vec<Vec<JsonValue>>) =
|
||||
SqlExecutor::query_internal_typed_with_context(
|
||||
SqlExecutor::execute_internal_typed(
|
||||
&tx,
|
||||
&hlc_service,
|
||||
&insert_ext_sql,
|
||||
@ -507,26 +501,9 @@ impl ExtensionManager {
|
||||
extracted.manifest.description,
|
||||
true, // enabled
|
||||
],
|
||||
&mut pk_context,
|
||||
)?;
|
||||
|
||||
// Nutze die tatsächliche ID aus der Datenbank (wichtig bei ON CONFLICT)
|
||||
// Die haex_extensions Tabelle hat einen single-column PK namens "id"
|
||||
let actual_extension_id = returning_results
|
||||
.first() // Holt die erste Zeile (das innere Vec<JsonValue>, z.B. Some(&["uuid-string"]))
|
||||
.and_then(|row_array| row_array.first()) // Holt das erste Element daraus (z.B. Some(&JsonValue::String("uuid-string")))
|
||||
.and_then(|val| val.as_str()) // Konvertiert zu &str (z.B. Some("uuid-string"))
|
||||
.map(|s| s.to_string()) // Konvertiert zu String
|
||||
.unwrap_or_else(|| extension_id.clone()); // Fallback
|
||||
|
||||
eprintln!(
|
||||
"DEBUG: Extension UUID - Generated: {}, Actual from DB: {}",
|
||||
extension_id, actual_extension_id
|
||||
);
|
||||
|
||||
// 2. Permissions speichern (oder aktualisieren falls schon vorhanden)
|
||||
// Nutze einfaches INSERT - die CRDT-Transformation fügt automatisch ON CONFLICT hinzu
|
||||
// FK-Werte (extension_id) werden automatisch remapped wenn Extension ON CONFLICT hatte
|
||||
// 2. Permissions speichern
|
||||
let insert_perm_sql = format!(
|
||||
"INSERT INTO {} (id, extension_id, resource_type, action, target, constraints, status) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||
TABLE_EXTENSION_PERMISSIONS
|
||||
@ -536,7 +513,7 @@ impl ExtensionManager {
|
||||
use crate::database::generated::HaexExtensionPermissions;
|
||||
let db_perm: HaexExtensionPermissions = perm.into();
|
||||
|
||||
SqlExecutor::execute_internal_typed_with_context(
|
||||
SqlExecutor::execute_internal_typed(
|
||||
&tx,
|
||||
&hlc_service,
|
||||
&insert_perm_sql,
|
||||
@ -549,16 +526,15 @@ impl ExtensionManager {
|
||||
db_perm.constraints,
|
||||
db_perm.status,
|
||||
],
|
||||
&mut pk_context,
|
||||
)?;
|
||||
}
|
||||
|
||||
tx.commit().map_err(DatabaseError::from)?;
|
||||
Ok(actual_extension_id.clone())
|
||||
Ok(extension_id.clone())
|
||||
})?;
|
||||
|
||||
let extension = Extension {
|
||||
id: actual_extension_id.clone(), // Nutze die actual_extension_id aus der Transaktion
|
||||
id: extension_id.clone(),
|
||||
source: ExtensionSource::Production {
|
||||
path: extensions_dir.clone(),
|
||||
version: extracted.manifest.version.clone(),
|
||||
@ -607,8 +583,7 @@ impl ExtensionManager {
|
||||
);
|
||||
eprintln!("DEBUG: SQL Query before transformation: {}", sql);
|
||||
|
||||
// select_internal gibt jetzt Vec<Vec<JsonValue>> zurück
|
||||
let results = SqlExecutor::select_internal(conn, &sql, &[])?;
|
||||
let results = SqlExecutor::query_select(conn, &sql, &[])?;
|
||||
eprintln!("DEBUG: Query returned {} results", results.len());
|
||||
|
||||
let mut data = Vec::new();
|
||||
|
||||
@ -155,7 +155,6 @@ impl ExtensionPermissions {
|
||||
.and_then(|c| serde_json::from_value::<PermissionConstraints>(c.clone()).ok()),
|
||||
status: p.status.clone().unwrap_or(PermissionStatus::Ask),
|
||||
haex_timestamp: None,
|
||||
haex_tombstone: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,121 +1,33 @@
|
||||
// src-tauri/src/extension/database/executor.rs (neu)
|
||||
// src-tauri/src/extension/database/executor.rs
|
||||
|
||||
use crate::crdt::hlc::HlcService;
|
||||
use crate::crdt::transformer::CrdtTransformer;
|
||||
use crate::crdt::trigger;
|
||||
use crate::database::core::{convert_value_ref_to_json, parse_sql_statements, ValueConverter};
|
||||
use crate::database::error::DatabaseError;
|
||||
use rusqlite::Connection;
|
||||
use rusqlite::{params_from_iter, types::Value as SqliteValue, ToSql, Transaction};
|
||||
use serde_json::{Map, Value as JsonValue};
|
||||
use sqlparser::ast::{Insert, Statement, TableObject};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
/// Repräsentiert PK-Werte für eine Zeile (kann single oder composite key sein)
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct PkValues {
|
||||
/// column_name -> value
|
||||
values: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl PkValues {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
values: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn insert(&mut self, column: String, value: String) {
|
||||
self.values.insert(column, value);
|
||||
}
|
||||
|
||||
fn get(&self, column: &str) -> Option<&String> {
|
||||
self.values.get(column)
|
||||
}
|
||||
}
|
||||
|
||||
/// Context für PK-Remapping während einer Transaktion
|
||||
/// Trackt für jede Tabelle: welche PKs sollten eingefügt werden vs. welche sind tatsächlich in der DB
|
||||
#[derive(Debug, Default)]
|
||||
pub struct PkRemappingContext {
|
||||
/// Für jede Tabelle: Liste von (original_pk_values, actual_pk_values) Mappings
|
||||
/// Wird nur gespeichert wenn original != actual (d.h. ON CONFLICT hat PK geändert)
|
||||
mappings: HashMap<String, Vec<(PkValues, PkValues)>>,
|
||||
}
|
||||
|
||||
impl PkRemappingContext {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Fügt ein Mapping für eine Tabelle hinzu, aber nur wenn original != actual
|
||||
/// original und actual sind die PK-Werte vor und nach dem INSERT
|
||||
fn add_mapping(&mut self, table: String, original: PkValues, actual: PkValues) {
|
||||
// Nur speichern wenn tatsächlich unterschiedlich (ON CONFLICT hat stattgefunden)
|
||||
if original != actual {
|
||||
eprintln!(
|
||||
"DEBUG: PK Remapping for table '{}': {:?} -> {:?}",
|
||||
table, original.values, actual.values
|
||||
);
|
||||
self.mappings
|
||||
.entry(table)
|
||||
.or_insert_with(Vec::new)
|
||||
.push((original, actual));
|
||||
}
|
||||
}
|
||||
|
||||
/// Versucht einen FK-Wert zu remappen
|
||||
/// referenced_table: Die Tabelle auf die der FK zeigt
|
||||
/// referenced_column: Die PK-Spalte in der referenced_table
|
||||
/// value: Der FK-Wert der ersetzt werden soll
|
||||
fn remap_fk_value(
|
||||
&self,
|
||||
referenced_table: &str,
|
||||
referenced_column: &str,
|
||||
value: &str,
|
||||
) -> String {
|
||||
self.mappings
|
||||
.get(referenced_table)
|
||||
.and_then(|mappings| {
|
||||
mappings.iter().find_map(|(original, actual)| {
|
||||
if original.get(referenced_column)? == value {
|
||||
let actual_val = actual.get(referenced_column)?.clone();
|
||||
eprintln!(
|
||||
"DEBUG: FK Remapping for {}.{}: {} -> {}",
|
||||
referenced_table, referenced_column, value, actual_val
|
||||
);
|
||||
Some(actual_val)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
.unwrap_or_else(|| value.to_string())
|
||||
}
|
||||
}
|
||||
use serde_json::Value as JsonValue;
|
||||
use sqlparser::ast::Statement;
|
||||
use std::collections::HashSet;
|
||||
|
||||
/// SQL-Executor OHNE Berechtigungsprüfung - für interne Nutzung
|
||||
pub struct SqlExecutor;
|
||||
|
||||
impl SqlExecutor {
|
||||
/// Führt ein SQL Statement OHNE RETURNING aus (mit CRDT und PK-Remapping)
|
||||
/// Unterstützt automatisches FK-Remapping wenn vorherige INSERTs ON CONFLICT getriggert haben
|
||||
///
|
||||
/// Diese Variante akzeptiert &[&dyn ToSql] direkt (wie von rusqlite::params![] erzeugt)
|
||||
/// Führt ein SQL Statement OHNE RETURNING aus (mit CRDT)
|
||||
/// Returns: modified_schema_tables
|
||||
pub fn execute_internal_typed_with_context(
|
||||
pub fn execute_internal_typed(
|
||||
tx: &Transaction,
|
||||
hlc_service: &HlcService,
|
||||
sql: &str,
|
||||
params: &[&dyn ToSql],
|
||||
pk_context: &mut PkRemappingContext,
|
||||
) -> Result<HashSet<String>, DatabaseError> {
|
||||
let mut ast_vec = parse_sql_statements(sql)?;
|
||||
|
||||
if ast_vec.len() != 1 {
|
||||
return Err(DatabaseError::ExecutionError {
|
||||
sql: sql.to_string(),
|
||||
reason: "execute_internal_typed_with_context sollte nur ein einzelnes SQL-Statement erhalten"
|
||||
reason: "execute_internal_typed should only receive a single SQL statement"
|
||||
.to_string(),
|
||||
table: None,
|
||||
});
|
||||
@ -140,56 +52,15 @@ impl SqlExecutor {
|
||||
}
|
||||
|
||||
let sql_str = statement.to_string();
|
||||
eprintln!("DEBUG: Transformed SQL (execute path): {}", sql_str);
|
||||
eprintln!("DEBUG: Transformed execute SQL: {}", sql_str);
|
||||
|
||||
// Spezielle Behandlung für INSERT Statements (mit FK-Remapping, OHNE RETURNING)
|
||||
if let Statement::Insert(ref insert_stmt) = statement {
|
||||
if let TableObject::TableName(ref table_name) = insert_stmt.table {
|
||||
let table_name_str = table_name
|
||||
.to_string()
|
||||
.trim_matches('`')
|
||||
.trim_matches('"')
|
||||
.to_string();
|
||||
|
||||
// Konvertiere Params zu Vec für Manipulation
|
||||
let mut param_vec = params_to_vec(params, tx)?;
|
||||
|
||||
// Hole Foreign Key Informationen
|
||||
let fk_info = get_fk_info(tx, &table_name_str)?;
|
||||
|
||||
// Remap FK-Werte in params (falls Mappings existieren)
|
||||
remap_fk_params(insert_stmt, &mut param_vec, &fk_info, pk_context)?;
|
||||
|
||||
let param_refs: Vec<&dyn ToSql> =
|
||||
param_vec.iter().map(|v| v as &dyn ToSql).collect();
|
||||
|
||||
let mut stmt = tx
|
||||
.prepare(&sql_str)
|
||||
.map_err(|e| DatabaseError::ExecutionError {
|
||||
sql: sql_str.clone(),
|
||||
table: Some(table_name_str.clone()),
|
||||
reason: format!("Prepare failed: {}", e),
|
||||
})?;
|
||||
|
||||
let mut rows = stmt
|
||||
.query(params_from_iter(param_refs.iter()))
|
||||
.map_err(|e| DatabaseError::ExecutionError {
|
||||
sql: sql_str.clone(),
|
||||
table: Some(table_name_str.clone()),
|
||||
reason: format!("INSERT query execution failed: {}", e),
|
||||
})?;
|
||||
|
||||
let _ = rows.next()?;
|
||||
}
|
||||
} else {
|
||||
// Nicht-INSERT Statements normal ausführen
|
||||
tx.execute(&sql_str, params)
|
||||
.map_err(|e| DatabaseError::ExecutionError {
|
||||
sql: sql_str.clone(),
|
||||
table: None,
|
||||
reason: format!("Execute failed: {}", e),
|
||||
})?;
|
||||
}
|
||||
// Führe Statement aus
|
||||
tx.execute(&sql_str, params)
|
||||
.map_err(|e| DatabaseError::ExecutionError {
|
||||
sql: sql_str.clone(),
|
||||
table: None,
|
||||
reason: format!("Execute failed: {}", e),
|
||||
})?;
|
||||
|
||||
// Trigger-Logik für CREATE TABLE
|
||||
if let Statement::CreateTable(create_table_details) = statement {
|
||||
@ -200,25 +71,20 @@ impl SqlExecutor {
|
||||
Ok(modified_schema_tables)
|
||||
}
|
||||
|
||||
/// Führt ein SQL Statement MIT RETURNING aus (mit CRDT und PK-Remapping)
|
||||
/// Unterstützt automatisches FK-Remapping wenn vorherige INSERTs ON CONFLICT getriggert haben
|
||||
///
|
||||
/// Diese Variante akzeptiert &[&dyn ToSql] direkt (wie von rusqlite::params![] erzeugt)
|
||||
/// Führt ein SQL Statement MIT RETURNING aus (mit CRDT)
|
||||
/// Returns: (modified_schema_tables, returning_results)
|
||||
/// returning_results enthält ALLE RETURNING-Spalten für INSERT/UPDATE/DELETE mit RETURNING
|
||||
pub fn query_internal_typed_with_context(
|
||||
pub fn query_internal_typed(
|
||||
tx: &Transaction,
|
||||
hlc_service: &HlcService,
|
||||
sql: &str,
|
||||
params: &[&dyn ToSql],
|
||||
pk_context: &mut PkRemappingContext,
|
||||
) -> Result<(HashSet<String>, Vec<Vec<JsonValue>>), DatabaseError> {
|
||||
let mut ast_vec = parse_sql_statements(sql)?;
|
||||
|
||||
if ast_vec.len() != 1 {
|
||||
return Err(DatabaseError::ExecutionError {
|
||||
sql: sql.to_string(),
|
||||
reason: "query_internal_typed_with_context sollte nur ein einzelnes SQL-Statement erhalten"
|
||||
reason: "query_internal_typed should only receive a single SQL statement"
|
||||
.to_string(),
|
||||
table: None,
|
||||
});
|
||||
@ -245,493 +111,162 @@ impl SqlExecutor {
|
||||
let sql_str = statement.to_string();
|
||||
eprintln!("DEBUG: Transformed SQL (with RETURNING): {}", sql_str);
|
||||
|
||||
// Spezielle Behandlung für INSERT Statements (mit PK-Remapping + RETURNING)
|
||||
if let Statement::Insert(ref insert_stmt) = statement {
|
||||
if let TableObject::TableName(ref table_name) = insert_stmt.table {
|
||||
let table_name_str = table_name
|
||||
.to_string()
|
||||
.trim_matches('`')
|
||||
.trim_matches('"')
|
||||
.to_string();
|
||||
|
||||
// Konvertiere Params zu Vec für Manipulation
|
||||
let mut param_vec = params_to_vec(params, tx)?;
|
||||
|
||||
// Hole Table Schema um PKs und FKs zu identifizieren
|
||||
let table_columns =
|
||||
trigger::get_table_schema(tx, &table_name_str).map_err(|e| {
|
||||
DatabaseError::ExecutionError {
|
||||
sql: format!("PRAGMA table_info('{}')", table_name_str),
|
||||
reason: e.to_string(),
|
||||
table: Some(table_name_str.clone()),
|
||||
}
|
||||
})?;
|
||||
|
||||
let pk_columns: Vec<String> = table_columns
|
||||
.iter()
|
||||
.filter(|c| c.is_pk)
|
||||
.map(|c| c.name.clone())
|
||||
.collect();
|
||||
|
||||
// Hole Foreign Key Informationen
|
||||
let fk_info = get_fk_info(tx, &table_name_str)?;
|
||||
|
||||
// 1. Extrahiere Original PK-Werte aus params (vor FK-Remapping)
|
||||
let original_pk =
|
||||
extract_pk_values_from_params(insert_stmt, ¶m_vec, &pk_columns)?;
|
||||
|
||||
// 2. Remap FK-Werte in params (falls Mappings existieren)
|
||||
remap_fk_params(insert_stmt, &mut param_vec, &fk_info, pk_context)?;
|
||||
|
||||
// 3. Führe INSERT mit query() aus um RETURNING zu lesen
|
||||
let mut stmt = tx
|
||||
.prepare(&sql_str)
|
||||
.map_err(|e| DatabaseError::ExecutionError {
|
||||
sql: sql_str.clone(),
|
||||
table: Some(table_name_str.clone()),
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
|
||||
let column_names: Vec<String> = stmt
|
||||
.column_names()
|
||||
.into_iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
let num_columns = column_names.len();
|
||||
|
||||
let param_refs: Vec<&dyn ToSql> =
|
||||
param_vec.iter().map(|v| v as &dyn ToSql).collect();
|
||||
|
||||
let mut rows = stmt
|
||||
.query(params_from_iter(param_refs.iter()))
|
||||
.map_err(|e| DatabaseError::ExecutionError {
|
||||
sql: sql_str.clone(),
|
||||
table: Some(table_name_str.clone()),
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
|
||||
let mut result_vec: Vec<Vec<JsonValue>> = Vec::new();
|
||||
|
||||
// 4. Lese ALLE RETURNING Werte und speichere PK-Mapping
|
||||
while let Some(row) = rows.next().map_err(|e| DatabaseError::ExecutionError {
|
||||
sql: sql_str.clone(),
|
||||
table: Some(table_name_str.clone()),
|
||||
reason: e.to_string(),
|
||||
})? {
|
||||
// Extrahiere PK-Werte für PK-Remapping
|
||||
let actual_pk = extract_pk_values_from_row(&row, &pk_columns)?;
|
||||
pk_context.add_mapping(
|
||||
table_name_str.clone(),
|
||||
original_pk.clone(),
|
||||
actual_pk.clone(),
|
||||
);
|
||||
|
||||
// Extrahiere ALLE Spalten für RETURNING-Ergebnis
|
||||
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);
|
||||
}
|
||||
|
||||
return Ok((modified_schema_tables, result_vec));
|
||||
}
|
||||
}
|
||||
|
||||
// Für UPDATE/DELETE mit RETURNING: query() verwenden (kein PK-Remapping nötig)
|
||||
// Prepare und query ausführen
|
||||
let mut stmt = tx
|
||||
.prepare(&sql_str)
|
||||
.map_err(|e| DatabaseError::PrepareError {
|
||||
.map_err(|e| DatabaseError::ExecutionError {
|
||||
sql: sql_str.clone(),
|
||||
table: None,
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
|
||||
let num_columns = stmt.column_count();
|
||||
let column_names: Vec<String> = stmt
|
||||
.column_names()
|
||||
.into_iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
let num_columns = column_names.len();
|
||||
|
||||
let mut rows = stmt.query(params).map_err(|e| DatabaseError::QueryError {
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
let mut rows = stmt
|
||||
.query(params_from_iter(params.iter()))
|
||||
.map_err(|e| DatabaseError::ExecutionError {
|
||||
sql: sql_str.clone(),
|
||||
table: None,
|
||||
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),
|
||||
// Lese alle RETURNING Zeilen
|
||||
while let Some(row) = rows.next().map_err(|e| DatabaseError::ExecutionError {
|
||||
sql: sql_str.clone(),
|
||||
table: None,
|
||||
reason: e.to_string(),
|
||||
})? {
|
||||
let mut row_values: Vec<JsonValue> = Vec::with_capacity(num_columns);
|
||||
|
||||
let mut row_values: Vec<JsonValue> = Vec::new();
|
||||
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);
|
||||
let value_ref = row.get_ref(i).map_err(|e| DatabaseError::ExecutionError {
|
||||
sql: sql_str.clone(),
|
||||
table: None,
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
let json_value = convert_value_ref_to_json(value_ref)?;
|
||||
row_values.push(json_value);
|
||||
}
|
||||
result_vec.push(row_values);
|
||||
}
|
||||
|
||||
// Trigger-Logik für CREATE TABLE
|
||||
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, result_vec))
|
||||
}
|
||||
|
||||
/// Legacy-Methode ohne PK-Remapping Context
|
||||
pub fn execute_internal_typed(
|
||||
tx: &Transaction,
|
||||
hlc_service: &HlcService,
|
||||
sql: &str,
|
||||
params: &[&dyn ToSql],
|
||||
) -> Result<HashSet<String>, DatabaseError> {
|
||||
let mut context = PkRemappingContext::new();
|
||||
Self::execute_internal_typed_with_context(tx, hlc_service, sql, params, &mut context)
|
||||
}
|
||||
/// Führt SQL aus (mit CRDT-Transformation) - OHNE Permission-Check
|
||||
/// Wrapper um execute_internal_typed für JsonValue-Parameter
|
||||
/// Nutzt PK-Remapping Logik für INSERT mit ON CONFLICT
|
||||
/// Führt ein einzelnes SQL Statement OHNE Typinformationen aus (JSON params)
|
||||
pub fn execute_internal(
|
||||
tx: &Transaction,
|
||||
hlc_service: &HlcService,
|
||||
sql: &str,
|
||||
params: &[JsonValue],
|
||||
) -> Result<HashSet<String>, DatabaseError> {
|
||||
// Parameter validation
|
||||
let total_placeholders = sql.matches('?').count();
|
||||
if total_placeholders != params.len() {
|
||||
return Err(DatabaseError::ParameterMismatchError {
|
||||
expected: total_placeholders,
|
||||
provided: params.len(),
|
||||
sql: sql.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
// Convert JsonValue params to SqliteValue
|
||||
let params_converted: Vec<SqliteValue> = params
|
||||
let sql_params: Vec<SqliteValue> = params
|
||||
.iter()
|
||||
.map(ValueConverter::json_to_rusqlite_value)
|
||||
.map(|v| crate::database::core::ValueConverter::json_to_rusqlite_value(v))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
// Convert to &dyn ToSql references
|
||||
let param_refs: Vec<&dyn ToSql> =
|
||||
params_converted.iter().map(|v| v as &dyn ToSql).collect();
|
||||
|
||||
// Call execute_internal_typed (mit PK-Remapping!)
|
||||
let param_refs: Vec<&dyn ToSql> = sql_params.iter().map(|p| p as &dyn ToSql).collect();
|
||||
Self::execute_internal_typed(tx, hlc_service, sql, ¶m_refs)
|
||||
}
|
||||
|
||||
/// Führt SELECT aus (mit CRDT-Transformation) - OHNE Permission-Check
|
||||
pub fn select_internal(
|
||||
conn: &Connection,
|
||||
/// Query-Variante (mit RETURNING) OHNE Typinformationen (JSON params)
|
||||
pub fn query_internal(
|
||||
tx: &Transaction,
|
||||
hlc_service: &HlcService,
|
||||
sql: &str,
|
||||
params: &[JsonValue],
|
||||
) -> Result<Vec<Vec<JsonValue>>, DatabaseError> {
|
||||
// Parameter validation
|
||||
let total_placeholders = sql.matches('?').count();
|
||||
if total_placeholders != params.len() {
|
||||
return Err(DatabaseError::ParameterMismatchError {
|
||||
expected: total_placeholders,
|
||||
provided: params.len(),
|
||||
sql: sql.to_string(),
|
||||
) -> Result<(HashSet<String>, Vec<Vec<JsonValue>>), DatabaseError> {
|
||||
let sql_params: Vec<SqliteValue> = params
|
||||
.iter()
|
||||
.map(|v| crate::database::core::ValueConverter::json_to_rusqlite_value(v))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let param_refs: Vec<&dyn ToSql> = sql_params.iter().map(|p| p as &dyn ToSql).collect();
|
||||
Self::query_internal_typed(tx, hlc_service, sql, ¶m_refs)
|
||||
}
|
||||
|
||||
/// Führt mehrere SQL Statements als Batch aus
|
||||
pub fn execute_batch_internal(
|
||||
tx: &Transaction,
|
||||
hlc_service: &HlcService,
|
||||
sqls: &[String],
|
||||
params: &[Vec<JsonValue>],
|
||||
) -> Result<HashSet<String>, DatabaseError> {
|
||||
if sqls.len() != params.len() {
|
||||
return Err(DatabaseError::ExecutionError {
|
||||
sql: format!("{} statements but {} param sets", sqls.len(), params.len()),
|
||||
reason: "Statement count and parameter count mismatch".to_string(),
|
||||
table: None,
|
||||
});
|
||||
}
|
||||
|
||||
let mut all_modified_tables = HashSet::new();
|
||||
|
||||
for (sql, param_set) in sqls.iter().zip(params.iter()) {
|
||||
let modified_tables = Self::execute_internal(tx, hlc_service, sql, param_set)?;
|
||||
all_modified_tables.extend(modified_tables);
|
||||
}
|
||||
|
||||
Ok(all_modified_tables)
|
||||
}
|
||||
|
||||
/// Query für SELECT-Statements (read-only, kein CRDT nötig außer Filter)
|
||||
pub fn query_select(
|
||||
conn: &rusqlite::Connection,
|
||||
sql: &str,
|
||||
params: &[JsonValue],
|
||||
) -> Result<Vec<Vec<JsonValue>>, DatabaseError> {
|
||||
let mut ast_vec = parse_sql_statements(sql)?;
|
||||
|
||||
if ast_vec.is_empty() {
|
||||
return Ok(vec![]);
|
||||
if ast_vec.len() != 1 {
|
||||
return Err(DatabaseError::ExecutionError {
|
||||
sql: sql.to_string(),
|
||||
reason: "query_select should only receive a single SELECT statement".to_string(),
|
||||
table: None,
|
||||
});
|
||||
}
|
||||
|
||||
// Validate that all statements are queries
|
||||
for stmt in &ast_vec {
|
||||
if !matches!(stmt, Statement::Query(_)) {
|
||||
return Err(DatabaseError::ExecutionError {
|
||||
sql: sql.to_string(),
|
||||
reason: "Only SELECT statements are allowed".to_string(),
|
||||
table: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let sql_params = ValueConverter::convert_params(params)?;
|
||||
|
||||
// Hard Delete: Keine SELECT-Transformation mehr nötig
|
||||
let stmt_to_execute = ast_vec.pop().unwrap();
|
||||
let transformed_sql = stmt_to_execute.to_string();
|
||||
|
||||
eprintln!("DEBUG: SELECT (no transformation): {}", transformed_sql);
|
||||
|
||||
// Convert JSON params to SQLite values
|
||||
let sql_params: Vec<SqliteValue> = params
|
||||
.iter()
|
||||
.map(|v| crate::database::core::ValueConverter::json_to_rusqlite_value(v))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let mut prepared_stmt = conn.prepare(&transformed_sql)?;
|
||||
|
||||
let num_columns = prepared_stmt.column_count();
|
||||
|
||||
let mut rows = prepared_stmt
|
||||
.query(params_from_iter(&sql_params[..]))
|
||||
.map_err(|e| DatabaseError::QueryError {
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
let param_refs: Vec<&dyn ToSql> = sql_params.iter().map(|p| p as &dyn ToSql).collect();
|
||||
|
||||
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);
|
||||
let mut rows = prepared_stmt.query(params_from_iter(param_refs.iter()))?;
|
||||
|
||||
let mut result: Vec<Vec<JsonValue>> = Vec::new();
|
||||
while let Some(row) = rows.next()? {
|
||||
let mut row_values: Vec<JsonValue> = Vec::new();
|
||||
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);
|
||||
let value_ref = row.get_ref(i)?;
|
||||
let json_value = convert_value_ref_to_json(value_ref)?;
|
||||
row_values.push(json_value);
|
||||
}
|
||||
result_vec.push(row_values);
|
||||
result.push(row_values);
|
||||
}
|
||||
|
||||
Ok(result_vec)
|
||||
}
|
||||
|
||||
/// Führt SQL mit CRDT-Transformation aus und gibt RETURNING-Ergebnisse zurück
|
||||
/// Speziell für INSERT/UPDATE/DELETE mit RETURNING (Drizzle-Integration)
|
||||
/// Nutzt PK-Remapping für INSERT-Operationen
|
||||
pub fn query_internal(
|
||||
tx: &Transaction,
|
||||
hlc_service: &HlcService,
|
||||
sql: &str,
|
||||
params: &[JsonValue],
|
||||
) -> Result<Vec<Vec<JsonValue>>, DatabaseError> {
|
||||
// Parameter validation
|
||||
let total_placeholders = sql.matches('?').count();
|
||||
if total_placeholders != params.len() {
|
||||
return Err(DatabaseError::ParameterMismatchError {
|
||||
expected: total_placeholders,
|
||||
provided: params.len(),
|
||||
sql: sql.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
// Parameter konvertieren
|
||||
let params_converted: Vec<SqliteValue> = params
|
||||
.iter()
|
||||
.map(ValueConverter::json_to_rusqlite_value)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
// Convert to &dyn ToSql references
|
||||
let param_refs: Vec<&dyn ToSql> =
|
||||
params_converted.iter().map(|v| v as &dyn ToSql).collect();
|
||||
|
||||
// Call query_internal_typed_with_context (mit PK-Remapping!)
|
||||
let mut context = PkRemappingContext::new();
|
||||
let (_tables, results) = Self::query_internal_typed_with_context(
|
||||
tx,
|
||||
hlc_service,
|
||||
sql,
|
||||
¶m_refs,
|
||||
&mut context,
|
||||
)?;
|
||||
|
||||
Ok(results)
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
// =========================
|
||||
// Helper-Funktionen für FK-Remapping
|
||||
// =========================
|
||||
|
||||
/// Strukturiert FK-Informationen für einfache Lookups
|
||||
#[derive(Debug)]
|
||||
struct FkInfo {
|
||||
/// column_name -> (referenced_table, referenced_column)
|
||||
mappings: HashMap<String, (String, String)>,
|
||||
}
|
||||
|
||||
/// Hole Foreign Key Informationen für eine Tabelle
|
||||
fn get_fk_info(tx: &Transaction, table_name: &str) -> Result<FkInfo, DatabaseError> {
|
||||
// Nutze PRAGMA foreign_key_list um FK-Beziehungen zu holen
|
||||
let sql = format!("PRAGMA foreign_key_list('{}');", table_name);
|
||||
let mut stmt = tx
|
||||
.prepare(&sql)
|
||||
.map_err(|e| DatabaseError::ExecutionError {
|
||||
sql: sql.clone(),
|
||||
reason: e.to_string(),
|
||||
table: Some(table_name.to_string()),
|
||||
})?;
|
||||
|
||||
let mut mappings = HashMap::new();
|
||||
let rows = stmt
|
||||
.query_map([], |row| {
|
||||
Ok((
|
||||
row.get::<_, String>("from")?, // FK column in this table
|
||||
row.get::<_, String>("table")?, // referenced table
|
||||
row.get::<_, String>("to")?, // referenced column
|
||||
))
|
||||
})
|
||||
.map_err(|e| DatabaseError::ExecutionError {
|
||||
sql,
|
||||
reason: e.to_string(),
|
||||
table: Some(table_name.to_string()),
|
||||
})?;
|
||||
|
||||
for row in rows {
|
||||
let (from_col, ref_table, ref_col) = row.map_err(|e| DatabaseError::ExecutionError {
|
||||
sql: format!("PRAGMA foreign_key_list('{}')", table_name),
|
||||
reason: e.to_string(),
|
||||
table: Some(table_name.to_string()),
|
||||
})?;
|
||||
mappings.insert(from_col, (ref_table, ref_col));
|
||||
}
|
||||
|
||||
Ok(FkInfo { mappings })
|
||||
}
|
||||
|
||||
/// Konvertiert &[&dyn ToSql] zu Vec<SqliteValue> für Manipulation
|
||||
/// Nutzt einen Dummy-Query um die Parameter-Werte zu extrahieren
|
||||
fn params_to_vec(
|
||||
params: &[&dyn ToSql],
|
||||
tx: &Transaction,
|
||||
) -> Result<Vec<SqliteValue>, DatabaseError> {
|
||||
let mut values = Vec::new();
|
||||
|
||||
// Erstelle eine Dummy-Query mit genau so vielen Platzhaltern wie wir Parameter haben
|
||||
// z.B. "SELECT ?, ?, ?"
|
||||
if params.is_empty() {
|
||||
return Ok(values);
|
||||
}
|
||||
|
||||
let placeholders = vec!["?"; params.len()].join(", ");
|
||||
let dummy_sql = format!("SELECT {}", placeholders);
|
||||
|
||||
let mut stmt = tx
|
||||
.prepare(&dummy_sql)
|
||||
.map_err(|e| DatabaseError::ExecutionError {
|
||||
sql: dummy_sql.clone(),
|
||||
reason: format!("Failed to prepare dummy query: {}", e),
|
||||
table: None,
|
||||
})?;
|
||||
|
||||
// Führe die Query aus und extrahiere die Werte aus der Row
|
||||
let mut rows = stmt
|
||||
.query(params)
|
||||
.map_err(|e| DatabaseError::ExecutionError {
|
||||
sql: dummy_sql.clone(),
|
||||
reason: format!("Failed to execute dummy query: {}", e),
|
||||
table: None,
|
||||
})?;
|
||||
|
||||
if let Some(row) = rows.next().map_err(|e| DatabaseError::ExecutionError {
|
||||
sql: dummy_sql,
|
||||
reason: format!("Failed to read dummy query result: {}", e),
|
||||
table: None,
|
||||
})? {
|
||||
// Extrahiere alle Spalten-Werte
|
||||
for i in 0..params.len() {
|
||||
let value: SqliteValue = row.get(i).map_err(|e| DatabaseError::ExecutionError {
|
||||
sql: format!("SELECT ..."),
|
||||
reason: format!("Failed to extract value at index {}: {}", i, e),
|
||||
table: None,
|
||||
})?;
|
||||
values.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(values)
|
||||
}
|
||||
|
||||
/// Extrahiert PK-Werte aus den INSERT-Parametern
|
||||
fn extract_pk_values_from_params(
|
||||
insert_stmt: &Insert,
|
||||
params: &[SqliteValue],
|
||||
pk_columns: &[String],
|
||||
) -> Result<PkValues, DatabaseError> {
|
||||
let mut pk_values = PkValues::new();
|
||||
|
||||
// Finde die Positionen der PK-Spalten in der INSERT column list
|
||||
for pk_col in pk_columns {
|
||||
if let Some(pos) = insert_stmt.columns.iter().position(|c| &c.value == pk_col) {
|
||||
// Hole den Parameter-Wert an dieser Position
|
||||
if pos < params.len() {
|
||||
// Konvertiere SqliteValue zu String
|
||||
let value_str = value_to_string(¶ms[pos]);
|
||||
pk_values.insert(pk_col.clone(), value_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(pk_values)
|
||||
}
|
||||
|
||||
/// Remapped FK-Werte in den Parametern basierend auf dem PK-Remapping Context
|
||||
fn remap_fk_params(
|
||||
insert_stmt: &Insert,
|
||||
params: &mut Vec<SqliteValue>,
|
||||
fk_info: &FkInfo,
|
||||
pk_context: &PkRemappingContext,
|
||||
) -> Result<(), DatabaseError> {
|
||||
// Für jede FK-Spalte: prüfe ob Remapping nötig ist
|
||||
for (col_name, (ref_table, ref_col)) in &fk_info.mappings {
|
||||
// Finde Position der FK-Spalte in der INSERT column list
|
||||
if let Some(pos) = insert_stmt
|
||||
.columns
|
||||
.iter()
|
||||
.position(|c| &c.value == col_name)
|
||||
{
|
||||
if pos < params.len() {
|
||||
// Hole aktuellen FK-Wert (als String)
|
||||
let current_value = value_to_string(¶ms[pos]);
|
||||
|
||||
// Versuche zu remappen
|
||||
let new_value = pk_context.remap_fk_value(ref_table, ref_col, ¤t_value);
|
||||
|
||||
if new_value != current_value {
|
||||
// Ersetze den Parameter-Wert
|
||||
params[pos] = SqliteValue::Text(new_value);
|
||||
eprintln!(
|
||||
"DEBUG: Remapped FK {}={} to {:?}",
|
||||
col_name, current_value, params[pos]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Hilfsfunktion: Konvertiert SqliteValue zu String für Vergleiche
|
||||
fn value_to_string(value: &SqliteValue) -> String {
|
||||
match value {
|
||||
SqliteValue::Null => "NULL".to_string(),
|
||||
SqliteValue::Integer(i) => i.to_string(),
|
||||
SqliteValue::Real(r) => r.to_string(),
|
||||
SqliteValue::Text(s) => s.clone(),
|
||||
SqliteValue::Blob(b) => format!("BLOB({} bytes)", b.len()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Extrahiert PK-Werte aus einer RETURNING Row
|
||||
fn extract_pk_values_from_row(
|
||||
row: &rusqlite::Row,
|
||||
pk_columns: &[String],
|
||||
) -> Result<PkValues, DatabaseError> {
|
||||
let mut pk_values = PkValues::new();
|
||||
|
||||
for pk_col in pk_columns.iter() {
|
||||
let value: String =
|
||||
row.get(pk_col.as_str())
|
||||
.map_err(|e| DatabaseError::ExecutionError {
|
||||
sql: "RETURNING clause".to_string(),
|
||||
reason: format!("Failed to extract PK column '{}': {}", pk_col, e),
|
||||
table: None,
|
||||
})?;
|
||||
pk_values.insert(pk_col.clone(), value);
|
||||
}
|
||||
|
||||
Ok(pk_values)
|
||||
}
|
||||
|
||||
@ -165,8 +165,6 @@ pub struct ExtensionPermission {
|
||||
pub constraints: Option<PermissionConstraints>,
|
||||
pub status: PermissionStatus,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub haex_tombstone: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub haex_timestamp: Option<String>,
|
||||
}
|
||||
|
||||
@ -341,9 +339,9 @@ impl From<&ExtensionPermission> for crate::database::generated::HaexExtensionPer
|
||||
fn from(perm: &ExtensionPermission) -> Self {
|
||||
Self {
|
||||
id: perm.id.clone(),
|
||||
extension_id: Some(perm.extension_id.clone()),
|
||||
extension_id: perm.extension_id.clone(),
|
||||
resource_type: Some(perm.resource_type.as_str().to_string()),
|
||||
action: Some(perm.action.as_str()),
|
||||
action: Some(perm.action.as_str().to_string()),
|
||||
target: Some(perm.target.clone()),
|
||||
constraints: perm
|
||||
.constraints
|
||||
@ -352,7 +350,6 @@ impl From<&ExtensionPermission> for crate::database::generated::HaexExtensionPer
|
||||
status: perm.status.as_str().to_string(),
|
||||
created_at: None,
|
||||
updated_at: None,
|
||||
haex_tombstone: perm.haex_tombstone,
|
||||
haex_timestamp: perm.haex_timestamp.clone(),
|
||||
}
|
||||
}
|
||||
@ -382,13 +379,12 @@ impl From<crate::database::generated::HaexExtensionPermissions> for ExtensionPer
|
||||
|
||||
Self {
|
||||
id: db_perm.id,
|
||||
extension_id: db_perm.extension_id.unwrap_or_default(),
|
||||
extension_id: db_perm.extension_id,
|
||||
resource_type,
|
||||
action,
|
||||
target: db_perm.target.unwrap_or_default(),
|
||||
constraints,
|
||||
status,
|
||||
haex_tombstone: db_perm.haex_tombstone,
|
||||
haex_timestamp: db_perm.haex_timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,9 +100,6 @@ const vault = reactive<{
|
||||
}
|
||||
} */
|
||||
|
||||
const { syncLocaleAsync, syncThemeAsync, syncVaultNameAsync } =
|
||||
useVaultSettingsStore()
|
||||
|
||||
const check = ref(false)
|
||||
|
||||
const initVault = () => {
|
||||
@ -156,11 +153,6 @@ const onOpenDatabase = async () => {
|
||||
},
|
||||
}),
|
||||
)
|
||||
await Promise.allSettled([
|
||||
syncLocaleAsync(),
|
||||
syncThemeAsync(),
|
||||
syncVaultNameAsync(),
|
||||
])
|
||||
} catch (error) {
|
||||
open.value = false
|
||||
console.error('handleError', error, typeof error)
|
||||
|
||||
@ -50,12 +50,20 @@ const { isKnownDeviceAsync } = useDeviceStore()
|
||||
const { loadExtensionsAsync } = useExtensionsStore()
|
||||
const { setDeviceIdIfNotExistsAsync, addDeviceNameAsync } = useDeviceStore()
|
||||
const { deviceId } = storeToRefs(useDeviceStore())
|
||||
const { syncLocaleAsync, syncThemeAsync, syncVaultNameAsync } =
|
||||
useVaultSettingsStore()
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
await setDeviceIdIfNotExistsAsync()
|
||||
await loadExtensionsAsync()
|
||||
await readNotificationsAsync()
|
||||
// Sync settings first before other initialization
|
||||
await Promise.allSettled([
|
||||
syncLocaleAsync(),
|
||||
syncThemeAsync(),
|
||||
syncVaultNameAsync(),
|
||||
setDeviceIdIfNotExistsAsync(),
|
||||
loadExtensionsAsync(),
|
||||
readNotificationsAsync(),
|
||||
])
|
||||
|
||||
const knownDevice = await isKnownDeviceAsync()
|
||||
|
||||
|
||||
@ -66,7 +66,6 @@ export const useWorkspaceStore = defineStore('workspaceStore', () => {
|
||||
name: name || `Workspace ${newIndex}`,
|
||||
position: workspaces.value.length,
|
||||
haexTimestamp: '',
|
||||
haexTombstone: false,
|
||||
}
|
||||
workspaces.value.push(newWorkspace)
|
||||
currentWorkspaceIndex.value = workspaces.value.length - 1
|
||||
|
||||
@ -71,7 +71,6 @@ export const useVaultSettingsStore = defineStore('vaultSettingsStore', () => {
|
||||
where: eq(schema.haexSettings.key, VaultSettingsKeyEnum.theme),
|
||||
})
|
||||
|
||||
console.log('found currentThemeRow', currentThemeRow)
|
||||
if (currentThemeRow?.value) {
|
||||
const theme = availableThemes.value.find(
|
||||
(theme) => theme.value === currentThemeRow.value,
|
||||
@ -100,7 +99,6 @@ export const useVaultSettingsStore = defineStore('vaultSettingsStore', () => {
|
||||
where: eq(schema.haexSettings.key, VaultSettingsKeyEnum.vaultName),
|
||||
})
|
||||
|
||||
console.log('found currentVaultNameRow', currentVaultNameRow)
|
||||
if (currentVaultNameRow?.value) {
|
||||
currentVaultName.value =
|
||||
currentVaultNameRow.value || haexVault.defaultVaultName || 'HaexHub'
|
||||
|
||||
Reference in New Issue
Block a user