mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-16 14:10:52 +01:00
mobile menu
This commit is contained in:
@ -2,6 +2,11 @@ import tailwindcss from '@tailwindcss/vite'
|
||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||
|
||||
export default defineNuxtConfig({
|
||||
app: {
|
||||
pageTransition: {
|
||||
name: 'fade',
|
||||
},
|
||||
},
|
||||
modules: [
|
||||
'nuxt-zod-i18n',
|
||||
'@nuxtjs/i18n',
|
||||
@ -91,6 +96,7 @@ export default defineNuxtConfig({
|
||||
// Enables the development server to be discoverable by other devices when running on iOS physical devices
|
||||
devServer: {
|
||||
host: process.env.TAURI_DEV_HOST || 'localhost',
|
||||
port: 3003,
|
||||
},
|
||||
|
||||
vite: {
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
"@tauri-apps/plugin-os": "^2.2.1",
|
||||
"@tauri-apps/plugin-sql": "~2.2.0",
|
||||
"@tauri-apps/plugin-store": "^2.2.0",
|
||||
"@vueuse/components": "^13.3.0",
|
||||
"@vueuse/core": "^13.3.0",
|
||||
"@vueuse/nuxt": "^13.3.0",
|
||||
"drizzle-orm": "^0.43.1",
|
||||
@ -40,6 +41,8 @@
|
||||
"nuxt-snackbar": "1.3.0",
|
||||
"nuxt-zod-i18n": "^1.11.5",
|
||||
"tailwindcss": "^4.1.8",
|
||||
"tailwindcss-intersect": "^2.2.0",
|
||||
"tailwindcss-motion": "^1.1.0",
|
||||
"vue": "^3.5.16",
|
||||
"vue-router": "^4.5.1",
|
||||
"zod": "^3.25.42"
|
||||
|
||||
38
pnpm-lock.yaml
generated
38
pnpm-lock.yaml
generated
@ -53,6 +53,9 @@ importers:
|
||||
'@tauri-apps/plugin-store':
|
||||
specifier: ^2.2.0
|
||||
version: 2.2.0
|
||||
'@vueuse/components':
|
||||
specifier: ^13.3.0
|
||||
version: 13.3.0(vue@3.5.16(typescript@5.8.3))
|
||||
'@vueuse/core':
|
||||
specifier: ^13.3.0
|
||||
version: 13.3.0(vue@3.5.16(typescript@5.8.3))
|
||||
@ -80,6 +83,12 @@ importers:
|
||||
tailwindcss:
|
||||
specifier: ^4.1.8
|
||||
version: 4.1.8
|
||||
tailwindcss-intersect:
|
||||
specifier: ^2.2.0
|
||||
version: 2.2.0(tailwindcss@4.1.8)
|
||||
tailwindcss-motion:
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.0(tailwindcss@4.1.8)
|
||||
vue:
|
||||
specifier: ^3.5.16
|
||||
version: 3.5.16(typescript@5.8.3)
|
||||
@ -2133,6 +2142,11 @@ packages:
|
||||
'@vue/shared@3.5.16':
|
||||
resolution: {integrity: sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==}
|
||||
|
||||
'@vueuse/components@13.3.0':
|
||||
resolution: {integrity: sha512-ZnJiVknPtlWyeE4qwIXkDOlHM3W4bgMCxgeXj1Dec/aF/+8N+yAj+7rRdRUWUnqr8uKRin368RjG1FPKsF2erA==}
|
||||
peerDependencies:
|
||||
vue: ^3.5.0
|
||||
|
||||
'@vueuse/core@13.3.0':
|
||||
resolution: {integrity: sha512-uYRz5oEfebHCoRhK4moXFM3NSCd5vu2XMLOq/Riz5FdqZMy2RvBtazdtL3gEcmDyqkztDe9ZP/zymObMIbiYSg==}
|
||||
peerDependencies:
|
||||
@ -4782,6 +4796,16 @@ packages:
|
||||
resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
tailwindcss-intersect@2.2.0:
|
||||
resolution: {integrity: sha512-LnOQ/iU44jNQ8k3OExa7Ccv/y5NlzAN574jjDnX5gLCdlqoeUT5ADtTznF92oAdon3NZA69b+JLknahbwsqxDA==}
|
||||
peerDependencies:
|
||||
tailwindcss: '>=3.2.0 || >=4.0.0'
|
||||
|
||||
tailwindcss-motion@1.1.0:
|
||||
resolution: {integrity: sha512-0lK6rA4+367ffJdi1TtB72GlMCxJi2TP/xRiHc6An5pZSlU6WfIHhSvLxpcGilGZfBrOqc2q4woH1DEP/lCNbQ==}
|
||||
peerDependencies:
|
||||
tailwindcss: '>=3.0.0 || insiders'
|
||||
|
||||
tailwindcss@4.1.8:
|
||||
resolution: {integrity: sha512-kjeW8gjdxasbmFKpVGrGd5T4i40mV5J2Rasw48QARfYeQ8YS9x02ON9SFWax3Qf616rt4Cp3nVNIj6Hd1mP3og==}
|
||||
|
||||
@ -7469,6 +7493,12 @@ snapshots:
|
||||
|
||||
'@vue/shared@3.5.16': {}
|
||||
|
||||
'@vueuse/components@13.3.0(vue@3.5.16(typescript@5.8.3))':
|
||||
dependencies:
|
||||
'@vueuse/core': 13.3.0(vue@3.5.16(typescript@5.8.3))
|
||||
'@vueuse/shared': 13.3.0(vue@3.5.16(typescript@5.8.3))
|
||||
vue: 3.5.16(typescript@5.8.3)
|
||||
|
||||
'@vueuse/core@13.3.0(vue@3.5.16(typescript@5.8.3))':
|
||||
dependencies:
|
||||
'@types/web-bluetooth': 0.0.21
|
||||
@ -10328,6 +10358,14 @@ snapshots:
|
||||
|
||||
system-architecture@0.1.0: {}
|
||||
|
||||
tailwindcss-intersect@2.2.0(tailwindcss@4.1.8):
|
||||
dependencies:
|
||||
tailwindcss: 4.1.8
|
||||
|
||||
tailwindcss-motion@1.1.0(tailwindcss@4.1.8):
|
||||
dependencies:
|
||||
tailwindcss: 4.1.8
|
||||
|
||||
tailwindcss@4.1.8: {}
|
||||
|
||||
tapable@2.2.2: {}
|
||||
|
||||
1
src-tauri/database/migrations/0004_wooden_lockheed.sql
Normal file
1
src-tauri/database/migrations/0004_wooden_lockheed.sql
Normal file
@ -0,0 +1 @@
|
||||
ALTER TABLE `haex_notofications` RENAME TO `haex_notifications`;
|
||||
50
src-tauri/database/migrations/0005_wooden_nuke.sql
Normal file
50
src-tauri/database/migrations/0005_wooden_nuke.sql
Normal file
@ -0,0 +1,50 @@
|
||||
CREATE TABLE `haex_passwords_group_items` (
|
||||
`group_id` text,
|
||||
`item_id` text,
|
||||
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_items`(`id`) ON UPDATE no action ON DELETE no action
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `haex_passwords_groups` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`name` text,
|
||||
`icon` text,
|
||||
`order` integer,
|
||||
`color` text,
|
||||
`parent_id` text,
|
||||
FOREIGN KEY (`parent_id`) REFERENCES `haex_passwords_groups`(`id`) ON UPDATE no action ON DELETE no action
|
||||
);
|
||||
--> 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),
|
||||
FOREIGN KEY (`item_id`) REFERENCES `haex_passwords_items`(`id`) ON UPDATE no action ON DELETE no action
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `haex_passwords_items` (
|
||||
`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
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `haex_passwords_items_key_values` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`item_id` text,
|
||||
`key` text,
|
||||
`value` text,
|
||||
FOREIGN KEY (`item_id`) REFERENCES `haex_passwords_items`(`id`) ON UPDATE no action ON DELETE no action
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE `haex_notifications` ADD `source` text;
|
||||
@ -0,0 +1,5 @@
|
||||
ALTER TABLE `haex_extensions_permissions` ADD `created_at` text DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
|
||||
ALTER TABLE `haex_extensions_permissions` ADD `updated_at` integer;--> statement-breakpoint
|
||||
ALTER TABLE `haex_passwords_groups` ADD `created_at` text DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
|
||||
ALTER TABLE `haex_passwords_groups` ADD `updated_at` integer;--> statement-breakpoint
|
||||
ALTER TABLE `haex_passwords_items_key_values` ADD `updated_at` integer;
|
||||
1
src-tauri/database/migrations/0007_daffy_tusk.sql
Normal file
1
src-tauri/database/migrations/0007_daffy_tusk.sql
Normal file
@ -0,0 +1 @@
|
||||
ALTER TABLE `haex_passwords_groups` ADD `description` text;
|
||||
253
src-tauri/database/migrations/meta/0004_snapshot.json
Normal file
253
src-tauri/database/migrations/meta/0004_snapshot.json
Normal file
@ -0,0 +1,253 @@
|
||||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "7aaac460-00b5-4387-bef9-b189297cefb3",
|
||||
"prevId": "5f413421-18a5-4c1b-9c5b-99f574b10126",
|
||||
"tables": {
|
||||
"haex_extensions": {
|
||||
"name": "haex_extensions",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"author": {
|
||||
"name": "author",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"enabled": {
|
||||
"name": "enabled",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"icon": {
|
||||
"name": "icon",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"url": {
|
||||
"name": "url",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"version": {
|
||||
"name": "version",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_extensions_permissions": {
|
||||
"name": "haex_extensions_permissions",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"extension_id": {
|
||||
"name": "extension_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"resource": {
|
||||
"name": "resource",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"operation": {
|
||||
"name": "operation",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"path": {
|
||||
"name": "path",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"haex_extensions_permissions_extension_id_resource_operation_path_unique": {
|
||||
"name": "haex_extensions_permissions_extension_id_resource_operation_path_unique",
|
||||
"columns": [
|
||||
"extension_id",
|
||||
"resource",
|
||||
"operation",
|
||||
"path"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"haex_extensions_permissions_extension_id_haex_extensions_id_fk": {
|
||||
"name": "haex_extensions_permissions_extension_id_haex_extensions_id_fk",
|
||||
"tableFrom": "haex_extensions_permissions",
|
||||
"tableTo": "haex_extensions",
|
||||
"columnsFrom": [
|
||||
"extension_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_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
|
||||
},
|
||||
"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
|
||||
}
|
||||
},
|
||||
"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
|
||||
},
|
||||
"value": {
|
||||
"name": "value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
}
|
||||
},
|
||||
"views": {},
|
||||
"enums": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {
|
||||
"\"haex_notofications\"": "\"haex_notifications\""
|
||||
},
|
||||
"columns": {}
|
||||
},
|
||||
"internal": {
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
583
src-tauri/database/migrations/meta/0005_snapshot.json
Normal file
583
src-tauri/database/migrations/meta/0005_snapshot.json
Normal file
@ -0,0 +1,583 @@
|
||||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "fd079acd-3b5f-4fb7-97e2-d6641620f393",
|
||||
"prevId": "7aaac460-00b5-4387-bef9-b189297cefb3",
|
||||
"tables": {
|
||||
"haex_extensions": {
|
||||
"name": "haex_extensions",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"author": {
|
||||
"name": "author",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"enabled": {
|
||||
"name": "enabled",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"icon": {
|
||||
"name": "icon",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"url": {
|
||||
"name": "url",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"version": {
|
||||
"name": "version",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_extensions_permissions": {
|
||||
"name": "haex_extensions_permissions",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"extension_id": {
|
||||
"name": "extension_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"resource": {
|
||||
"name": "resource",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"operation": {
|
||||
"name": "operation",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"path": {
|
||||
"name": "path",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"haex_extensions_permissions_extension_id_resource_operation_path_unique": {
|
||||
"name": "haex_extensions_permissions_extension_id_resource_operation_path_unique",
|
||||
"columns": [
|
||||
"extension_id",
|
||||
"resource",
|
||||
"operation",
|
||||
"path"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"haex_extensions_permissions_extension_id_haex_extensions_id_fk": {
|
||||
"name": "haex_extensions_permissions_extension_id_haex_extensions_id_fk",
|
||||
"tableFrom": "haex_extensions_permissions",
|
||||
"tableTo": "haex_extensions",
|
||||
"columnsFrom": [
|
||||
"extension_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_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
|
||||
}
|
||||
},
|
||||
"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
|
||||
}
|
||||
},
|
||||
"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_items_id_fk": {
|
||||
"name": "haex_passwords_group_items_item_id_haex_passwords_items_id_fk",
|
||||
"tableFrom": "haex_passwords_group_items",
|
||||
"tableTo": "haex_passwords_items",
|
||||
"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
|
||||
},
|
||||
"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
|
||||
}
|
||||
},
|
||||
"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_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)"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"haex_passwords_item_history_item_id_haex_passwords_items_id_fk": {
|
||||
"name": "haex_passwords_item_history_item_id_haex_passwords_items_id_fk",
|
||||
"tableFrom": "haex_passwords_item_history",
|
||||
"tableTo": "haex_passwords_items",
|
||||
"columnsFrom": [
|
||||
"item_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_passwords_items": {
|
||||
"name": "haex_passwords_items",
|
||||
"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
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_passwords_items_key_values": {
|
||||
"name": "haex_passwords_items_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
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"haex_passwords_items_key_values_item_id_haex_passwords_items_id_fk": {
|
||||
"name": "haex_passwords_items_key_values_item_id_haex_passwords_items_id_fk",
|
||||
"tableFrom": "haex_passwords_items_key_values",
|
||||
"tableTo": "haex_passwords_items",
|
||||
"columnsFrom": [
|
||||
"item_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_settings": {
|
||||
"name": "haex_settings",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"key": {
|
||||
"name": "key",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"value": {
|
||||
"name": "value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
}
|
||||
},
|
||||
"views": {},
|
||||
"enums": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
},
|
||||
"internal": {
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
620
src-tauri/database/migrations/meta/0006_snapshot.json
Normal file
620
src-tauri/database/migrations/meta/0006_snapshot.json
Normal file
@ -0,0 +1,620 @@
|
||||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "76878f8b-9a30-4fd2-9a7b-d1a85874b1ab",
|
||||
"prevId": "fd079acd-3b5f-4fb7-97e2-d6641620f393",
|
||||
"tables": {
|
||||
"haex_extensions": {
|
||||
"name": "haex_extensions",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"author": {
|
||||
"name": "author",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"enabled": {
|
||||
"name": "enabled",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"icon": {
|
||||
"name": "icon",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"url": {
|
||||
"name": "url",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"version": {
|
||||
"name": "version",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_extensions_permissions": {
|
||||
"name": "haex_extensions_permissions",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"extension_id": {
|
||||
"name": "extension_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"resource": {
|
||||
"name": "resource",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"operation": {
|
||||
"name": "operation",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"path": {
|
||||
"name": "path",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"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
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"haex_extensions_permissions_extension_id_resource_operation_path_unique": {
|
||||
"name": "haex_extensions_permissions_extension_id_resource_operation_path_unique",
|
||||
"columns": [
|
||||
"extension_id",
|
||||
"resource",
|
||||
"operation",
|
||||
"path"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"haex_extensions_permissions_extension_id_haex_extensions_id_fk": {
|
||||
"name": "haex_extensions_permissions_extension_id_haex_extensions_id_fk",
|
||||
"tableFrom": "haex_extensions_permissions",
|
||||
"tableTo": "haex_extensions",
|
||||
"columnsFrom": [
|
||||
"extension_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_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
|
||||
}
|
||||
},
|
||||
"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
|
||||
}
|
||||
},
|
||||
"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_items_id_fk": {
|
||||
"name": "haex_passwords_group_items_item_id_haex_passwords_items_id_fk",
|
||||
"tableFrom": "haex_passwords_group_items",
|
||||
"tableTo": "haex_passwords_items",
|
||||
"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
|
||||
},
|
||||
"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
|
||||
}
|
||||
},
|
||||
"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_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)"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"haex_passwords_item_history_item_id_haex_passwords_items_id_fk": {
|
||||
"name": "haex_passwords_item_history_item_id_haex_passwords_items_id_fk",
|
||||
"tableFrom": "haex_passwords_item_history",
|
||||
"tableTo": "haex_passwords_items",
|
||||
"columnsFrom": [
|
||||
"item_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_passwords_items": {
|
||||
"name": "haex_passwords_items",
|
||||
"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
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_passwords_items_key_values": {
|
||||
"name": "haex_passwords_items_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
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"haex_passwords_items_key_values_item_id_haex_passwords_items_id_fk": {
|
||||
"name": "haex_passwords_items_key_values_item_id_haex_passwords_items_id_fk",
|
||||
"tableFrom": "haex_passwords_items_key_values",
|
||||
"tableTo": "haex_passwords_items",
|
||||
"columnsFrom": [
|
||||
"item_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_settings": {
|
||||
"name": "haex_settings",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"key": {
|
||||
"name": "key",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"value": {
|
||||
"name": "value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
}
|
||||
},
|
||||
"views": {},
|
||||
"enums": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
},
|
||||
"internal": {
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
627
src-tauri/database/migrations/meta/0007_snapshot.json
Normal file
627
src-tauri/database/migrations/meta/0007_snapshot.json
Normal file
@ -0,0 +1,627 @@
|
||||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "47f309cf-dabd-4f19-b87a-ed73d0e97781",
|
||||
"prevId": "76878f8b-9a30-4fd2-9a7b-d1a85874b1ab",
|
||||
"tables": {
|
||||
"haex_extensions": {
|
||||
"name": "haex_extensions",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"author": {
|
||||
"name": "author",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"enabled": {
|
||||
"name": "enabled",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"icon": {
|
||||
"name": "icon",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"url": {
|
||||
"name": "url",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"version": {
|
||||
"name": "version",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_extensions_permissions": {
|
||||
"name": "haex_extensions_permissions",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"extension_id": {
|
||||
"name": "extension_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"resource": {
|
||||
"name": "resource",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"operation": {
|
||||
"name": "operation",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"path": {
|
||||
"name": "path",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"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
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"haex_extensions_permissions_extension_id_resource_operation_path_unique": {
|
||||
"name": "haex_extensions_permissions_extension_id_resource_operation_path_unique",
|
||||
"columns": [
|
||||
"extension_id",
|
||||
"resource",
|
||||
"operation",
|
||||
"path"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"haex_extensions_permissions_extension_id_haex_extensions_id_fk": {
|
||||
"name": "haex_extensions_permissions_extension_id_haex_extensions_id_fk",
|
||||
"tableFrom": "haex_extensions_permissions",
|
||||
"tableTo": "haex_extensions",
|
||||
"columnsFrom": [
|
||||
"extension_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_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
|
||||
}
|
||||
},
|
||||
"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
|
||||
}
|
||||
},
|
||||
"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_items_id_fk": {
|
||||
"name": "haex_passwords_group_items_item_id_haex_passwords_items_id_fk",
|
||||
"tableFrom": "haex_passwords_group_items",
|
||||
"tableTo": "haex_passwords_items",
|
||||
"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
|
||||
}
|
||||
},
|
||||
"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_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)"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"haex_passwords_item_history_item_id_haex_passwords_items_id_fk": {
|
||||
"name": "haex_passwords_item_history_item_id_haex_passwords_items_id_fk",
|
||||
"tableFrom": "haex_passwords_item_history",
|
||||
"tableTo": "haex_passwords_items",
|
||||
"columnsFrom": [
|
||||
"item_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_passwords_items": {
|
||||
"name": "haex_passwords_items",
|
||||
"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
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_passwords_items_key_values": {
|
||||
"name": "haex_passwords_items_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
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"haex_passwords_items_key_values_item_id_haex_passwords_items_id_fk": {
|
||||
"name": "haex_passwords_items_key_values_item_id_haex_passwords_items_id_fk",
|
||||
"tableFrom": "haex_passwords_items_key_values",
|
||||
"tableTo": "haex_passwords_items",
|
||||
"columnsFrom": [
|
||||
"item_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"haex_settings": {
|
||||
"name": "haex_settings",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"key": {
|
||||
"name": "key",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"value": {
|
||||
"name": "value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
}
|
||||
},
|
||||
"views": {},
|
||||
"enums": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
},
|
||||
"internal": {
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
@ -29,6 +29,34 @@
|
||||
"when": 1748873820060,
|
||||
"tag": "0003_familiar_doctor_faustus",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 4,
|
||||
"version": "6",
|
||||
"when": 1748982377354,
|
||||
"tag": "0004_wooden_lockheed",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 5,
|
||||
"version": "6",
|
||||
"when": 1749073296353,
|
||||
"tag": "0005_wooden_nuke",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 6,
|
||||
"version": "6",
|
||||
"when": 1749128243104,
|
||||
"tag": "0006_complete_martin_li",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 7,
|
||||
"version": "6",
|
||||
"when": 1749244165094,
|
||||
"tag": "0007_daffy_tusk",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
import { sql } from 'drizzle-orm'
|
||||
import {
|
||||
integer,
|
||||
primaryKey,
|
||||
sqliteTable,
|
||||
text,
|
||||
unique,
|
||||
@ -16,10 +18,10 @@ export type SelectHaexSettings = typeof haexSettings.$inferSelect
|
||||
|
||||
export const haexExtensions = sqliteTable('haex_extensions', {
|
||||
id: text().primaryKey(),
|
||||
name: text(),
|
||||
author: text(),
|
||||
enabled: integer({ mode: 'boolean' }),
|
||||
icon: text(),
|
||||
name: text(),
|
||||
url: text(),
|
||||
version: text(),
|
||||
})
|
||||
@ -36,6 +38,10 @@ export const haexExtensionsPermissions = sqliteTable(
|
||||
resource: text({ enum: ['fs', 'http', 'db', 'shell'] }),
|
||||
operation: text({ enum: ['read', 'write', 'create'] }),
|
||||
path: text(),
|
||||
createdAt: text('created_at').default(sql`(CURRENT_TIMESTAMP)`),
|
||||
updateAt: integer('updated_at', { mode: 'timestamp' }).$onUpdate(
|
||||
() => new Date(),
|
||||
),
|
||||
},
|
||||
(table) => [
|
||||
unique().on(table.extensionId, table.resource, table.operation, table.path),
|
||||
@ -46,16 +52,105 @@ export type InsertHaexExtensionsPermissions =
|
||||
export type SelectHaexExtensionsPermissions =
|
||||
typeof haexExtensionsPermissions.$inferSelect
|
||||
|
||||
export const haexNotifications = sqliteTable('haex_notofications', {
|
||||
export const haexNotifications = sqliteTable('haex_notifications', {
|
||||
id: text().primaryKey(),
|
||||
title: text(),
|
||||
text: text(),
|
||||
type: text({ enum: ['error', 'success', 'warning', 'info'] }).notNull(),
|
||||
read: integer({ mode: 'boolean' }),
|
||||
date: text(),
|
||||
image: text(),
|
||||
alt: text(),
|
||||
date: text(),
|
||||
icon: text(),
|
||||
image: text(),
|
||||
read: integer({ mode: 'boolean' }),
|
||||
source: text(),
|
||||
text: text(),
|
||||
title: text(),
|
||||
type: text({
|
||||
enum: ['error', 'success', 'warning', 'info', 'log'],
|
||||
}).notNull(),
|
||||
})
|
||||
export type InsertHaexNotifications = typeof haexNotifications.$inferInsert
|
||||
export type SelectHaexNotifications = typeof haexNotifications.$inferSelect
|
||||
|
||||
export const haexPasswordsItems = sqliteTable('haex_passwords_items', {
|
||||
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(),
|
||||
),
|
||||
})
|
||||
export type InsertHaexPasswordsItems = typeof haexPasswordsItems.$inferInsert
|
||||
export type SelectHaexPasswordsItems = typeof haexPasswordsItems.$inferSelect
|
||||
|
||||
export const haexPasswordsItemsKeyValues = sqliteTable(
|
||||
'haex_passwords_items_key_values',
|
||||
{
|
||||
id: text().primaryKey(),
|
||||
itemId: text('item_id').references(
|
||||
(): AnySQLiteColumn => haexPasswordsItems.id,
|
||||
),
|
||||
key: text(),
|
||||
value: text(),
|
||||
updateAt: integer('updated_at', { mode: 'timestamp' }).$onUpdate(
|
||||
() => new Date(),
|
||||
),
|
||||
},
|
||||
)
|
||||
export type InsertHaexPasswordsItemsKeyValues =
|
||||
typeof haexPasswordsItemsKeyValues.$inferInsert
|
||||
export type SelectHaexPasswordsItemsKeyValues =
|
||||
typeof haexPasswordsItemsKeyValues.$inferSelect
|
||||
|
||||
export const haexPasswordsItemHistory = sqliteTable(
|
||||
'haex_passwords_item_history',
|
||||
{
|
||||
id: text().primaryKey(),
|
||||
itemId: text('item_id').references(
|
||||
(): AnySQLiteColumn => haexPasswordsItems.id,
|
||||
),
|
||||
changedProperty:
|
||||
text('changed_property').$type<keyof typeof haexPasswordsItems>(),
|
||||
oldValue: text('old_value'),
|
||||
newValue: text('new_value'),
|
||||
createdAt: text('created_at').default(sql`(CURRENT_TIMESTAMP)`),
|
||||
},
|
||||
)
|
||||
|
||||
export const haexPasswordsGroups = sqliteTable('haex_passwords_groups', {
|
||||
id: text().primaryKey(),
|
||||
name: text(),
|
||||
description: text(),
|
||||
icon: text(),
|
||||
order: integer(),
|
||||
color: text(),
|
||||
parentId: text('parent_id').references(
|
||||
(): AnySQLiteColumn => haexPasswordsGroups.id,
|
||||
),
|
||||
createdAt: text('created_at').default(sql`(CURRENT_TIMESTAMP)`),
|
||||
updateAt: integer('updated_at', { mode: 'timestamp' }).$onUpdate(
|
||||
() => new Date(),
|
||||
),
|
||||
})
|
||||
export type InsertHaexPasswordsGroups = typeof haexPasswordsGroups.$inferInsert
|
||||
export type SelectHaexPasswordsGroups = typeof haexPasswordsGroups.$inferSelect
|
||||
|
||||
export const haexPasswordsGroupItems = sqliteTable(
|
||||
'haex_passwords_group_items',
|
||||
{
|
||||
groupId: text('group_id').references(
|
||||
(): AnySQLiteColumn => haexPasswordsGroups.id,
|
||||
),
|
||||
itemId: text('item_id').references(
|
||||
(): AnySQLiteColumn => haexPasswordsItems.id,
|
||||
),
|
||||
},
|
||||
(table) => [primaryKey({ columns: [table.itemId, table.groupId] })],
|
||||
)
|
||||
export type InsertHaexPasswordsGroupItems =
|
||||
typeof haexPasswordsGroupItems.$inferInsert
|
||||
export type SelectHaexPasswordsGroupItems =
|
||||
typeof haexPasswordsGroupItems.$inferSelect
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -1,19 +1,15 @@
|
||||
// database/mod.rs
|
||||
pub mod core;
|
||||
|
||||
use rusqlite::{Connection, OpenFlags, Result as RusqliteResult};
|
||||
use rusqlite::Connection;
|
||||
use serde_json::Value as JsonValue;
|
||||
use std::fs;
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Mutex;
|
||||
use tauri::utils::resources::Resource;
|
||||
use tauri::{path::BaseDirectory, AppHandle, Manager, State, Wry};
|
||||
use tauri_plugin_fs::FsExt;
|
||||
|
||||
pub struct DbConnection(pub Mutex<Option<Connection>>);
|
||||
|
||||
// Öffentliche Funktionen für direkten Datenbankzugriff
|
||||
#[tauri::command]
|
||||
pub async fn sql_select(
|
||||
sql: String,
|
||||
@ -32,7 +28,6 @@ pub async fn sql_execute(
|
||||
core::execute(sql, params, &state).await
|
||||
}
|
||||
|
||||
// remember to call `.manage(MyState::default())`
|
||||
#[tauri::command]
|
||||
pub fn test(app_handle: AppHandle) -> Result<String, String> {
|
||||
let resource_path = app_handle
|
||||
@ -44,7 +39,7 @@ pub fn test(app_handle: AppHandle) -> Result<String, String> {
|
||||
/* std::fs::exists(String::from(resource_path.unwrap().to_string_lossy()))
|
||||
.map_err(|e| format!("Fehler: {}", e)) */
|
||||
}
|
||||
/// Erstellt eine verschlüsselte Datenbank
|
||||
|
||||
#[tauri::command]
|
||||
pub fn create_encrypted_database(
|
||||
app_handle: AppHandle,
|
||||
@ -81,6 +76,18 @@ pub fn create_encrypted_database(
|
||||
}
|
||||
}
|
||||
|
||||
let target = Path::new(&path);
|
||||
if target.exists() & target.is_file() {
|
||||
println!(
|
||||
"Datei '{}' existiert bereits. Sie wird gelöscht.",
|
||||
target.display()
|
||||
);
|
||||
|
||||
fs::remove_file(target)
|
||||
.map_err(|e| format!("Kann Vault {} nicht löschen. \n {}", target.display(), e))?;
|
||||
} else {
|
||||
println!("Datei '{}' existiert nicht.", target.display());
|
||||
}
|
||||
//core::copy_file(&resource_path, &path)?;
|
||||
|
||||
println!(
|
||||
@ -235,7 +242,6 @@ pub fn create_encrypted_database(
|
||||
))
|
||||
}
|
||||
|
||||
/// Öffnet eine verschlüsselte Datenbank
|
||||
#[tauri::command]
|
||||
pub fn open_encrypted_database(
|
||||
path: String,
|
||||
@ -254,22 +260,6 @@ pub fn open_encrypted_database(
|
||||
Ok(format!("success"))
|
||||
}
|
||||
|
||||
// Notwendige Imports an den Anfang des Moduls stellen
|
||||
//use tauri::{AppHandle, Manager, State, path::BaseDirectory, Wry};
|
||||
//use rusqlite::{Connection, OpenFlags, Result as RusqliteResult};
|
||||
//use std::fs;
|
||||
//use std::path::{Path, PathBuf};
|
||||
//use std::sync::Mutex; // Für den State
|
||||
|
||||
// Stelle sicher, dass dein DbConnection-Typ hier bekannt ist.
|
||||
// z.B. durch pub struct DbConnection(pub Mutex<Option<Connection>>);
|
||||
// oder wenn es in einem anderen Modul ist: use crate::path_to::DbConnection;
|
||||
// Für dieses Beispiel gehe ich davon aus, dass es in crate::DbConnection liegt.
|
||||
// Ersetze `crate::DbConnection` mit dem korrekten Pfad zu deiner Definition.
|
||||
//type SharedDbConnectionState = State<'_, crate::DbConnection>;
|
||||
|
||||
/// Hilfsfunktion: Lädt ein Asset und kopiert es in eine temporäre Datei.
|
||||
/// Gibt den Pfad zur temporären Datei zurück.
|
||||
fn prepare_temporary_asset_db(
|
||||
app_handle: &AppHandle<Wry>,
|
||||
asset_name: &str,
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
"identifier": "space.haex.hub",
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm dev",
|
||||
"devUrl": "http://localhost:3000",
|
||||
"devUrl": "http://localhost:3003",
|
||||
"beforeBuildCommand": "pnpm generate",
|
||||
"frontendDist": "../dist"
|
||||
},
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
@import 'tailwindcss';
|
||||
@import 'flyonui/variants.css';
|
||||
@import 'tailwindcss-intersect';
|
||||
|
||||
@plugin "tailwindcss-motion";
|
||||
@plugin "@iconify/tailwind4";
|
||||
@plugin "flyonui" {
|
||||
themes: all;
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
<li>
|
||||
<NuxtLinkLocale
|
||||
class="dropdown-item"
|
||||
:to="{ name: 'haexSettings' }"
|
||||
:to="{ name: 'settings' }"
|
||||
>
|
||||
<span class="icon-[tabler--settings]" />
|
||||
{{ t('settings') }}
|
||||
|
||||
1
src/components/haex/pass/group/index.vue
Normal file
1
src/components/haex/pass/group/index.vue
Normal file
@ -0,0 +1 @@
|
||||
<template></template>
|
||||
33
src/components/haex/pass/mobile/menu/group.vue
Normal file
33
src/components/haex/pass/mobile/menu/group.vue
Normal file
@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<button
|
||||
aria-disabled
|
||||
class="flex gap-4 w-full"
|
||||
@click="$emit('click', group)"
|
||||
>
|
||||
<Icon
|
||||
:name="groupIcon"
|
||||
size="24"
|
||||
class="shrink-0"
|
||||
/>
|
||||
<p class="w-full flex-1 text-start truncate">
|
||||
{{ group.name }}
|
||||
</p>
|
||||
|
||||
<Icon
|
||||
name="mdi:chevron-right"
|
||||
size="24"
|
||||
/>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { SelectHaexPasswordsGroups } from '~~/src-tauri/database/schemas/vault'
|
||||
|
||||
defineEmits<{ click: [group: SelectHaexPasswordsGroups] }>()
|
||||
|
||||
const { group } = defineProps<{
|
||||
group: SelectHaexPasswordsGroups
|
||||
}>()
|
||||
|
||||
const groupIcon = computed(() => group.icon ?? 'mdi:folder-outline')
|
||||
</script>
|
||||
78
src/components/haex/pass/mobile/menu/index.vue
Normal file
78
src/components/haex/pass/mobile/menu/index.vue
Normal file
@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<ul
|
||||
class="flex flex-col w-full h-full gap-y-2 *:first:rounded-t-md *:last:rounded-b-md"
|
||||
ref="listRef"
|
||||
>
|
||||
<li
|
||||
v-for="(group, index) in groupItems.groups"
|
||||
class="bg-base-100 rounded-lg hover:bg-base-100/45 origin-to intersect:motion-preset-slide-down intersect:motion-ease-spring-bouncier intersect:motion-delay ease-in-out shadow"
|
||||
:class="{
|
||||
'bg-base-300/15 outline outline-accent hover:bg-base-300/15':
|
||||
selectedItems.has(group.id),
|
||||
}"
|
||||
:style="{ '--motion-delay': `${50 * index}ms` }"
|
||||
:key="group.id"
|
||||
v-on-long-press="[
|
||||
onLongPressCallbackHook,
|
||||
{
|
||||
delay: 1000,
|
||||
},
|
||||
]"
|
||||
>
|
||||
<HaexPassMobileMenuGroup
|
||||
:group
|
||||
@click="onClickGroupAsync"
|
||||
class="px-4 py-2"
|
||||
/>
|
||||
</li>
|
||||
<li
|
||||
v-for="item in groupItems.items"
|
||||
:key="item.id"
|
||||
>
|
||||
<HaexPassMobileMenuItem :item />
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type {
|
||||
SelectHaexPasswordsGroups,
|
||||
SelectHaexPasswordsItems,
|
||||
} from '~~/src-tauri/database/schemas/vault'
|
||||
|
||||
import { vOnLongPress } from '@vueuse/components'
|
||||
|
||||
defineProps<{
|
||||
groupItems: {
|
||||
items: SelectHaexPasswordsItems[]
|
||||
groups: SelectHaexPasswordsGroups[]
|
||||
}
|
||||
}>()
|
||||
|
||||
const selectedItems = ref<Set<string>>(new Set())
|
||||
const longPressedHook = shallowRef(false)
|
||||
|
||||
const onLongPressCallbackHook = (_: PointerEvent) => {
|
||||
longPressedHook.value = true
|
||||
}
|
||||
|
||||
const localePath = useLocalePath()
|
||||
const onClickGroupAsync = async (group: SelectHaexPasswordsGroups) => {
|
||||
if (longPressedHook.value) {
|
||||
if (selectedItems.value.has(group.id)) {
|
||||
selectedItems.value.delete(group.id)
|
||||
} else {
|
||||
selectedItems.value.add(group.id)
|
||||
}
|
||||
if (!selectedItems.value.size) longPressedHook.value = false
|
||||
} else {
|
||||
await navigateTo(localePath({ name: 'passwordGroupEdit' }))
|
||||
}
|
||||
}
|
||||
|
||||
const listRef = useTemplateRef('listRef')
|
||||
onClickOutside(listRef, () => {
|
||||
selectedItems.value.clear()
|
||||
longPressedHook.value = false
|
||||
})
|
||||
</script>
|
||||
13
src/components/haex/pass/mobile/menu/item.vue
Normal file
13
src/components/haex/pass/mobile/menu/item.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<NuxtLinkLocale>
|
||||
{{ item.title }}
|
||||
</NuxtLinkLocale>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { SelectHaexPasswordsItems } from '~~/src-tauri/database/schemas/vault'
|
||||
|
||||
defineProps<{
|
||||
item: SelectHaexPasswordsItems
|
||||
}>()
|
||||
</script>
|
||||
5
src/components/haex/pass/mobile/menu/types.d.ts
vendored
Normal file
5
src/components/haex/pass/mobile/menu/types.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
export interface IPassMenuItem {
|
||||
name: string
|
||||
type: 'group' | 'item'
|
||||
id: string
|
||||
}
|
||||
@ -1,77 +1,96 @@
|
||||
<template>
|
||||
|
||||
|
||||
<aside :id ref="sidebarRef" class=" flex sm:shadow-none w-full md:max-w-64 bg-red-200" tabindex="-1">
|
||||
<div class="drawer-body w-full ">
|
||||
<aside
|
||||
:id
|
||||
ref="sidebarRef"
|
||||
class="flex sm:shadow-none w-full md:max-w-64"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div class="drawer-body w-full">
|
||||
<ul class="menu space-y-0.5 p-0 rounded-none md:rounded">
|
||||
<li>
|
||||
<a href="#">
|
||||
<span class="icon-[tabler--home] size-5"/>
|
||||
<span class="icon-[tabler--home] size-5" />
|
||||
Home
|
||||
</a>
|
||||
</li>
|
||||
<li class="space-y-0.5">
|
||||
<a id="menu-app" class="collapse-toggle collapse-open:bg-base-content/10" data-collapse="#menu-app-collapse">
|
||||
<span class="icon-[tabler--apps] size-5"/>
|
||||
<a
|
||||
id="menu-app"
|
||||
class="collapse-toggle collapse-open:bg-base-content/10"
|
||||
data-collapse="#menu-app-collapse"
|
||||
>
|
||||
<span class="icon-[tabler--apps] size-5" />
|
||||
Apps
|
||||
<span
|
||||
class="icon-[tabler--chevron-down] collapse-open:rotate-180 size-4 transition-all duration-300"/>
|
||||
class="icon-[tabler--chevron-down] collapse-open:rotate-180 size-4 transition-all duration-300"
|
||||
/>
|
||||
</a>
|
||||
<ul
|
||||
id="menu-app-collapse"
|
||||
id="menu-app-collapse"
|
||||
class="collapse hidden w-auto space-y-0.5 overflow-hidden transition-[height] duration-300"
|
||||
aria-labelledby="menu-app">
|
||||
aria-labelledby="menu-app"
|
||||
>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span class="icon-[tabler--message] size-5"/>
|
||||
<span class="icon-[tabler--message] size-5" />
|
||||
Chat
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span class="icon-[tabler--calendar] size-5"/>
|
||||
<span class="icon-[tabler--calendar] size-5" />
|
||||
Calendar
|
||||
</a>
|
||||
</li>
|
||||
<li class="space-y-0.5">
|
||||
<a
|
||||
id="sub-menu-academy" class="collapse-toggle collapse-open:bg-base-content/10"
|
||||
data-collapse="#sub-menu-academy-collapse">
|
||||
<span class="icon-[tabler--book] size-5"/>
|
||||
id="sub-menu-academy"
|
||||
class="collapse-toggle collapse-open:bg-base-content/10"
|
||||
data-collapse="#sub-menu-academy-collapse"
|
||||
>
|
||||
<span class="icon-[tabler--book] size-5" />
|
||||
Academy
|
||||
<span class="icon-[tabler--chevron-down] collapse-open:rotate-180 size-4"/>
|
||||
<span
|
||||
class="icon-[tabler--chevron-down] collapse-open:rotate-180 size-4"
|
||||
/>
|
||||
</a>
|
||||
<ul
|
||||
id="sub-menu-academy-collapse"
|
||||
id="sub-menu-academy-collapse"
|
||||
class="collapse hidden w-auto space-y-0.5 overflow-hidden transition-[height] duration-300"
|
||||
aria-labelledby="sub-menu-academy">
|
||||
aria-labelledby="sub-menu-academy"
|
||||
>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span class="icon-[tabler--books] size-5"/>
|
||||
<span class="icon-[tabler--books] size-5" />
|
||||
Courses
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span class="icon-[tabler--list-details] size-5"/>
|
||||
<span class="icon-[tabler--list-details] size-5" />
|
||||
Course details
|
||||
</a>
|
||||
</li>
|
||||
<li class="space-y-0.5">
|
||||
<a
|
||||
id="sub-menu-academy-stats" class="collapse-toggle collapse-open:bg-base-content/10"
|
||||
data-collapse="#sub-menu-academy-stats-collapse">
|
||||
<span class="icon-[tabler--chart-bar] size-5"/>
|
||||
id="sub-menu-academy-stats"
|
||||
class="collapse-toggle collapse-open:bg-base-content/10"
|
||||
data-collapse="#sub-menu-academy-stats-collapse"
|
||||
>
|
||||
<span class="icon-[tabler--chart-bar] size-5" />
|
||||
Stats
|
||||
<span class="icon-[tabler--chevron-down] collapse-open:rotate-180 size-4"/>
|
||||
<span
|
||||
class="icon-[tabler--chevron-down] collapse-open:rotate-180 size-4"
|
||||
/>
|
||||
</a>
|
||||
<ul
|
||||
id="sub-menu-academy-stats-collapse"
|
||||
id="sub-menu-academy-stats-collapse"
|
||||
class="collapse hidden w-auto space-y-0.5 overflow-hidden transition-[height] duration-300"
|
||||
aria-labelledby="sub-menu-academy-stats">
|
||||
aria-labelledby="sub-menu-academy-stats"
|
||||
>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span class="icon-[tabler--chart-donut] size-5"/>
|
||||
<span class="icon-[tabler--chart-donut] size-5" />
|
||||
Goals
|
||||
</a>
|
||||
</li>
|
||||
@ -83,33 +102,37 @@ id="sub-menu-academy-stats-collapse"
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span class="icon-[tabler--settings] size-5"/>
|
||||
<span class="icon-[tabler--settings] size-5" />
|
||||
Settings
|
||||
</a>
|
||||
</li>
|
||||
<div class="divider text-base-content/50 py-6 after:border-0">Account</div>
|
||||
<div class="divider text-base-content/50 py-6 after:border-0">
|
||||
Account
|
||||
</div>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span class="icon-[tabler--login] size-5"/>
|
||||
<span class="icon-[tabler--login] size-5" />
|
||||
Sign In
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span class="icon-[tabler--logout-2] size-5"/>
|
||||
<span class="icon-[tabler--logout-2] size-5" />
|
||||
Sign Out
|
||||
</a>
|
||||
</li>
|
||||
<div class="divider text-base-content/50 py-6 after:border-0">Miscellaneous</div>
|
||||
<div class="divider text-base-content/50 py-6 after:border-0">
|
||||
Miscellaneous
|
||||
</div>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span class="icon-[tabler--users-group] size-5"/>
|
||||
<span class="icon-[tabler--users-group] size-5" />
|
||||
Support
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span class="icon-[tabler--files] size-5"/>
|
||||
<span class="icon-[tabler--files] size-5" />
|
||||
Documentation
|
||||
</a>
|
||||
</li>
|
||||
@ -119,47 +142,47 @@ id="sub-menu-academy-stats-collapse"
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { HSOverlay } from "flyonui/flyonui";
|
||||
import type { HSOverlay } from 'flyonui/flyonui'
|
||||
|
||||
defineProps<{ title?: string; label?: string }>();
|
||||
defineProps<{ title?: string; label?: string }>()
|
||||
|
||||
defineEmits(["open", "close"]);
|
||||
defineEmits(['open', 'close'])
|
||||
|
||||
const id = useId();
|
||||
const id = useId()
|
||||
|
||||
const open = defineModel<boolean>("open", { default: true });
|
||||
const open = defineModel<boolean>('open', { default: true })
|
||||
|
||||
const { t } = useI18n();
|
||||
const { t } = useI18n()
|
||||
|
||||
const sidebarRef = useTemplateRef("sidebarRef");
|
||||
const sidebarRef = useTemplateRef('sidebarRef')
|
||||
|
||||
const modal = ref<HSOverlay>();
|
||||
const modal = ref<HSOverlay>()
|
||||
|
||||
watch(open, async () => {
|
||||
if (open.value) {
|
||||
await modal.value?.open();
|
||||
await modal.value?.open()
|
||||
} else {
|
||||
await modal.value?.close(true);
|
||||
await modal.value?.close(true)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
if (!sidebarRef.value) return;
|
||||
if (!sidebarRef.value) return
|
||||
|
||||
modal.value = new window.HSOverlay(sidebarRef.value, {
|
||||
isClosePrev: true,
|
||||
});
|
||||
})
|
||||
|
||||
modal.value.on("close", () => {
|
||||
open.value = false;
|
||||
});
|
||||
});
|
||||
modal.value.on('close', () => {
|
||||
open.value = false
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<i18n lang="yaml">
|
||||
de:
|
||||
close: Schließen
|
||||
|
||||
en:
|
||||
close: Close
|
||||
</i18n>
|
||||
de:
|
||||
close: Schließen
|
||||
|
||||
en:
|
||||
close: Close
|
||||
</i18n>
|
||||
|
||||
@ -1,11 +1,30 @@
|
||||
<template>
|
||||
<li
|
||||
class="hover:text-primary rounded" :class="{ ['bg-base-200 text-base-content']: isActive }"
|
||||
@click="triggerNavigate">
|
||||
<UiTooltip :tooltip="tooltip ?? name" direction="right-start">
|
||||
<NuxtLinkLocale ref="linkRef" :to class="flex items-center justify-center cursor-pointer tooltip-toogle">
|
||||
<div v-if="iconType === 'svg'" class="shrink-0 size-5" v-html="icon" />
|
||||
<Icon v-else :name="icon" size="1.5em" />
|
||||
class="rounded"
|
||||
:class="{
|
||||
['bg-base-300/35 ']: isActive,
|
||||
}"
|
||||
@click="triggerNavigate"
|
||||
>
|
||||
<UiTooltip
|
||||
:tooltip="tooltip ?? name"
|
||||
direction="right-start"
|
||||
>
|
||||
<NuxtLinkLocale
|
||||
ref="linkRef"
|
||||
:to
|
||||
class="flex items-center justify-center cursor-pointer tooltip-toogle"
|
||||
>
|
||||
<div
|
||||
v-if="iconType === 'svg'"
|
||||
class="shrink-0 size-5"
|
||||
v-html="icon"
|
||||
/>
|
||||
<Icon
|
||||
v-else
|
||||
:name="icon"
|
||||
size="1.5em"
|
||||
/>
|
||||
</NuxtLinkLocale>
|
||||
</UiTooltip>
|
||||
</li>
|
||||
@ -26,7 +45,15 @@ const isActive = computed(() => {
|
||||
props.id
|
||||
)
|
||||
} else {
|
||||
return props.to?.name === router.currentRoute.value.meta.name
|
||||
return (
|
||||
props.to?.name === router.currentRoute.value.meta.name ||
|
||||
router
|
||||
.getRoutes()
|
||||
.find((route) => route.meta.name === props.to?.name)
|
||||
?.children.some(
|
||||
(route) => route.meta?.name === router.currentRoute.value.meta.name,
|
||||
)
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
105
src/components/ui/button/action.vue
Normal file
105
src/components/ui/button/action.vue
Normal file
@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div class="z-10">
|
||||
<div
|
||||
class="dropdown relative inline-flex [--placement:top] [--strategy:absolute]"
|
||||
>
|
||||
<button
|
||||
:id
|
||||
class="dropdown-toggle btn btn-primary btn-lg btn-square dropdown-open:rotate-45 transition-transform"
|
||||
aria-haspopup="menu"
|
||||
aria-expanded="false"
|
||||
aria-label="Menu"
|
||||
>
|
||||
<Icon
|
||||
:name="icon"
|
||||
size="46"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<ul
|
||||
class="dropdown-menu dropdown-open:opacity-100 hidden min-w-60 bg-transparent shadow-none"
|
||||
data-dropdown-transition
|
||||
role="menu"
|
||||
aria-orientation="vertical"
|
||||
:aria-labelledby="id"
|
||||
>
|
||||
<li
|
||||
v-for="link in menu"
|
||||
class="dropdown-item hover:bg-transparent px-0"
|
||||
>
|
||||
<NuxtLinkLocale
|
||||
v-if="link.to"
|
||||
:to="link.to"
|
||||
class="btn btn-primary flex items-center no-underline rounded-lg flex-nowrap w-full"
|
||||
>
|
||||
<Icon
|
||||
v-if="link.icon"
|
||||
:name="link.icon"
|
||||
class="me-3"
|
||||
/>
|
||||
{{ link.label }}
|
||||
</NuxtLinkLocale>
|
||||
|
||||
<button
|
||||
v-else
|
||||
@click="link.action"
|
||||
class="link hover:link-primary flex items-center no-underline w-full"
|
||||
>
|
||||
<Icon
|
||||
v-if="link.icon"
|
||||
:name="link.icon"
|
||||
class="me-3"
|
||||
/>
|
||||
{{ link.label }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { IActionMenuItem } from './types'
|
||||
|
||||
defineProps({
|
||||
menu: {
|
||||
type: Array as PropType<IActionMenuItem[]>,
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: 'mdi:plus',
|
||||
},
|
||||
})
|
||||
|
||||
const id = useId()
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
@keyframes fadeInStagger {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(15px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 2. Die Listenelemente sind standardmäßig unsichtbar, damit sie nicht aufblitzen */
|
||||
.stagger-menu li {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* 3. Wenn das Menü geöffnet wird, weise die Animation zu */
|
||||
:global(.dropdown-open) .stagger-menu li {
|
||||
animation-name: fadeInStagger;
|
||||
animation-duration: 0.4s;
|
||||
animation-timing-function: ease-out;
|
||||
|
||||
/* SEHR WICHTIG: Sorgt dafür, dass die Elemente nach der Animation sichtbar bleiben (den Zustand von 'to' beibehalten) */
|
||||
animation-fill-mode: forwards;
|
||||
|
||||
/* Die individuelle animation-delay wird per :style im Template gesetzt. */
|
||||
}
|
||||
</style>
|
||||
8
src/components/ui/button/types.d.ts
vendored
Normal file
8
src/components/ui/button/types.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
import type { RouteLocationRaw } from 'vue-router'
|
||||
|
||||
export interface IActionMenuItem {
|
||||
label: string
|
||||
icon?: string
|
||||
action?: () => Promise<unknown>
|
||||
to?: RouteLocationRaw
|
||||
}
|
||||
63
src/components/ui/card/index.vue
Normal file
63
src/components/ui/card/index.vue
Normal file
@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="card">
|
||||
<slot name="image" />
|
||||
|
||||
<div class="card-header">
|
||||
<slot name="header">
|
||||
<div
|
||||
v-if="$slots.title || title"
|
||||
class="flex items-center gap-2"
|
||||
>
|
||||
<Icon
|
||||
v-if="icon"
|
||||
:name="icon"
|
||||
size="28"
|
||||
/>
|
||||
<h5
|
||||
v-if="title"
|
||||
class="card-title mb-0"
|
||||
>
|
||||
{{ title }}
|
||||
</h5>
|
||||
<slot
|
||||
v-else
|
||||
name="title"
|
||||
/>
|
||||
</div>
|
||||
<div class="text-base-content/45">{{ subtitle }}</div>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<div class="card-body px-2 sm:px-6">
|
||||
<slot />
|
||||
<div
|
||||
v-if="$slots.action"
|
||||
class="card-actions"
|
||||
>
|
||||
<slot name="action" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const emit = defineEmits(['close', 'submit'])
|
||||
|
||||
defineProps<{ title?: string; subtitle?: string; icon?: string }>()
|
||||
|
||||
const { escape, enter } = useMagicKeys()
|
||||
|
||||
watchEffect(async () => {
|
||||
if (escape.value) {
|
||||
await nextTick()
|
||||
emit('close')
|
||||
}
|
||||
})
|
||||
|
||||
watchEffect(async () => {
|
||||
if (enter.value) {
|
||||
await nextTick()
|
||||
emit('submit')
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@ -16,13 +16,13 @@
|
||||
<template #buttons>
|
||||
<slot name="buttons">
|
||||
<UiButton
|
||||
class="btn-error btn-outline"
|
||||
class="btn-error btn-outline w-full sm:w-auto"
|
||||
@click="onAbort"
|
||||
>
|
||||
<Icon name="mdi:close" /> {{ abortLabel ?? t('abort') }}
|
||||
</UiButton>
|
||||
<UiButton
|
||||
class="btn-primary"
|
||||
class="btn-primary w-full sm:w-auto"
|
||||
@click="onConfirm"
|
||||
>
|
||||
<Icon name="mdi:check" /> {{ confirmLabel ?? t('confirm') }}
|
||||
|
||||
@ -17,18 +17,18 @@
|
||||
<div
|
||||
:id
|
||||
ref="modalRef"
|
||||
class="overlay modal overlay-open:opacity-100 hidden overlay-open:duration-300 modal-middle"
|
||||
class="overlay modal overlay-open:opacity-100 hidden overlay-open:duration-300 sm:modal-middle p-0 xs:p-2"
|
||||
role="dialog"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="overlay-animation-target overlay-open:mt-4 overlay-open:duration-500 mt-12 transition-all ease-out modal-dialog overlay-open:opacity-100"
|
||||
class="overlay-animation-target overlay-open:duration-300 transition-all ease-out modal-dialog overlay-open:opacity-100 pointer-events-auto overflow-y-auto"
|
||||
>
|
||||
<div class="modal-content gap-2">
|
||||
<div class="modal-content justify-between h-full max-h-none">
|
||||
<div class="modal-header">
|
||||
<div
|
||||
v-if="title || $slots.title"
|
||||
class="modal-title"
|
||||
class="modal-title py-4 break-all"
|
||||
>
|
||||
<slot name="title">
|
||||
{{ title }}
|
||||
@ -53,7 +53,7 @@
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<div class="modal-footer flex-wrap">
|
||||
<div class="modal-footer flex-col sm:flex-row">
|
||||
<slot name="buttons" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,32 +1,67 @@
|
||||
<template>
|
||||
<div>
|
||||
<fieldset class="join w-full pt-1.5 " v-bind="$attrs">
|
||||
<fieldset
|
||||
class="join w-full"
|
||||
:class="{ 'pt-1.5': label }"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<slot name="prepend" />
|
||||
|
||||
<div class="input join-item">
|
||||
<Icon v-if="prependIcon" :name="prependIcon" class="my-auto shrink-0" />
|
||||
<Icon
|
||||
v-if="prependIcon"
|
||||
:name="prependIcon"
|
||||
class="my-auto shrink-0"
|
||||
/>
|
||||
|
||||
<div class="input-floating grow">
|
||||
<input
|
||||
:id ref="inputRef" v-model="input" :name="name ?? id" :placeholder="placeholder || label" :type
|
||||
:autofocus class="ps-3" :readonly="read_only" >
|
||||
<label class="input-floating-label" :for="id">{{ label }}</label>
|
||||
:id
|
||||
ref="inputRef"
|
||||
v-model="input"
|
||||
:name="name ?? id"
|
||||
:placeholder="placeholder || label"
|
||||
:type
|
||||
:autofocus
|
||||
class="ps-3"
|
||||
:readonly="read_only"
|
||||
/>
|
||||
<label
|
||||
class="input-floating-label"
|
||||
:for="id"
|
||||
>{{ label }}</label
|
||||
>
|
||||
</div>
|
||||
|
||||
<Icon v-if="appendIcon" :name="appendIcon" class="my-auto shrink-0" />
|
||||
<Icon
|
||||
v-if="appendIcon"
|
||||
:name="appendIcon"
|
||||
class="my-auto shrink-0"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<slot name="append" class="h-auto" />
|
||||
<slot
|
||||
name="append"
|
||||
class="h-auto"
|
||||
/>
|
||||
|
||||
<UiButton
|
||||
v-if="withCopyButton" class="btn-outline btn-accent btn-square join-item h-auto"
|
||||
@click="copy(`${input}`)">
|
||||
v-if="withCopyButton"
|
||||
class="btn-outline btn-accent btn-square join-item h-auto"
|
||||
@click="copy(`${input}`)"
|
||||
>
|
||||
<Icon :name="copied ? 'mdi:check' : 'mdi:content-copy'" />
|
||||
</UiButton>
|
||||
</fieldset>
|
||||
|
||||
<span v-show="errors" class="flex flex-col px-2 pt-0.5">
|
||||
<span v-for="error in errors" class="label-text-alt text-error">
|
||||
<span
|
||||
v-show="errors"
|
||||
class="flex flex-col px-2 pt-0.5"
|
||||
>
|
||||
<span
|
||||
v-for="error in errors"
|
||||
class="label-text-alt text-error"
|
||||
>
|
||||
{{ error }}
|
||||
</span>
|
||||
</span>
|
||||
@ -34,57 +69,57 @@ v-if="withCopyButton" class="btn-outline btn-accent btn-square join-item h-auto"
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ZodSchema } from "zod";
|
||||
import type { ZodSchema } from 'zod'
|
||||
|
||||
const inputRef = useTemplateRef("inputRef");
|
||||
defineExpose({ inputRef });
|
||||
const inputRef = useTemplateRef('inputRef')
|
||||
defineExpose({ inputRef })
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "",
|
||||
default: '',
|
||||
},
|
||||
type: {
|
||||
type: String as PropType<
|
||||
| "button"
|
||||
| "checkbox"
|
||||
| "color"
|
||||
| "date"
|
||||
| "datetime-local"
|
||||
| "email"
|
||||
| "file"
|
||||
| "hidden"
|
||||
| "image"
|
||||
| "month"
|
||||
| "number"
|
||||
| "password"
|
||||
| "radio"
|
||||
| "range"
|
||||
| "reset"
|
||||
| "search"
|
||||
| "submit"
|
||||
| "tel"
|
||||
| "text"
|
||||
| "time"
|
||||
| "url"
|
||||
| "week"
|
||||
| 'button'
|
||||
| 'checkbox'
|
||||
| 'color'
|
||||
| 'date'
|
||||
| 'datetime-local'
|
||||
| 'email'
|
||||
| 'file'
|
||||
| 'hidden'
|
||||
| 'image'
|
||||
| 'month'
|
||||
| 'number'
|
||||
| 'password'
|
||||
| 'radio'
|
||||
| 'range'
|
||||
| 'reset'
|
||||
| 'search'
|
||||
| 'submit'
|
||||
| 'tel'
|
||||
| 'text'
|
||||
| 'time'
|
||||
| 'url'
|
||||
| 'week'
|
||||
>,
|
||||
default: "text",
|
||||
default: 'text',
|
||||
},
|
||||
label: String,
|
||||
name: String,
|
||||
prependIcon: {
|
||||
type: String,
|
||||
default: "",
|
||||
default: '',
|
||||
},
|
||||
prependLabel: String,
|
||||
appendIcon: {
|
||||
type: String,
|
||||
default: "",
|
||||
default: '',
|
||||
},
|
||||
appendLabel: String,
|
||||
rules: Object as PropType<ZodSchema>,
|
||||
@ -92,44 +127,44 @@ const props = defineProps({
|
||||
withCopyButton: Boolean,
|
||||
autofocus: Boolean,
|
||||
read_only: Boolean,
|
||||
});
|
||||
})
|
||||
|
||||
const input = defineModel<string | number | undefined | null>({
|
||||
default: "",
|
||||
default: '',
|
||||
required: true,
|
||||
});
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (props.autofocus && inputRef.value) inputRef.value.focus();
|
||||
});
|
||||
if (props.autofocus && inputRef.value) inputRef.value.focus()
|
||||
})
|
||||
|
||||
const errors = defineModel<string[] | undefined>("errors");
|
||||
const errors = defineModel<string[] | undefined>('errors')
|
||||
|
||||
const id = useId();
|
||||
const id = useId()
|
||||
|
||||
watch(input, () => checkInput());
|
||||
watch(input, () => checkInput())
|
||||
|
||||
watch(
|
||||
() => props.checkInput,
|
||||
() => {
|
||||
checkInput();
|
||||
}
|
||||
);
|
||||
checkInput()
|
||||
},
|
||||
)
|
||||
|
||||
const emit = defineEmits(["error"]);
|
||||
const emit = defineEmits(['error'])
|
||||
|
||||
const checkInput = () => {
|
||||
if (props.rules) {
|
||||
const result = props.rules.safeParse(input.value);
|
||||
const result = props.rules.safeParse(input.value)
|
||||
//console.log('check result', result.error, props.rules);
|
||||
if (!result.success) {
|
||||
errors.value = result.error.errors.map((error) => error.message);
|
||||
emit("error", errors.value);
|
||||
errors.value = result.error.errors.map((error) => error.message)
|
||||
emit('error', errors.value)
|
||||
} else {
|
||||
errors.value = [];
|
||||
errors.value = []
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const { copy, copied } = useClipboard();
|
||||
</script>
|
||||
const { copy, copied } = useClipboard()
|
||||
</script>
|
||||
|
||||
54
src/components/ui/select/color.vue
Normal file
54
src/components/ui/select/color.vue
Normal file
@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div class="flex items-center gap-4">
|
||||
<label
|
||||
:for="id"
|
||||
class="font-medium"
|
||||
>
|
||||
{{ t('label') }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
:id
|
||||
:readonly="read_only"
|
||||
:disabled="read_only"
|
||||
:title="t('title')"
|
||||
class="p-0 cursor-pointer disabled:opacity-50 disabled:pointer-events-none w-14 h-10"
|
||||
type="color"
|
||||
v-model="model"
|
||||
/>
|
||||
|
||||
<button
|
||||
@click="model = null"
|
||||
class="btn btn-sm text-sm"
|
||||
:class="{ 'btn-disabled': read_only }"
|
||||
>
|
||||
{{ t('reset') }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const id = useId()
|
||||
const { t } = useI18n()
|
||||
|
||||
const model = defineModel()
|
||||
|
||||
defineProps({
|
||||
read_only: Boolean,
|
||||
})
|
||||
</script>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"de": {
|
||||
"label": "Farbauswahl",
|
||||
"title": "Wähle eine Farbe aus",
|
||||
"reset": "zurücksetzen"
|
||||
},
|
||||
"en": {
|
||||
"label": "Color Picker",
|
||||
"title": "Choose a color",
|
||||
"reset": "Reset"
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
37
src/components/ui/select/icon.vue
Normal file
37
src/components/ui/select/icon.vue
Normal file
@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<UiSelect
|
||||
v-model="icon"
|
||||
:options="icons"
|
||||
label="Icon Picker"
|
||||
>
|
||||
<template #value="{ value }">
|
||||
<Icon
|
||||
:name="value"
|
||||
v-if="value"
|
||||
/>
|
||||
</template>
|
||||
<template #option="{ option }">
|
||||
<Icon :name="option ?? ''" />
|
||||
</template>
|
||||
</UiSelect>
|
||||
|
||||
<UiDropdown :items="icons">
|
||||
<template #activator> {{ icons.find((_icon) => _icon === icon) }}</template>
|
||||
<template #item="{ item }">
|
||||
<Icon :name="`mdi:${item}`" />
|
||||
{{ item }}
|
||||
</template>
|
||||
</UiDropdown>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const icons = [
|
||||
'streamline:money-bank-institution-money-saving-bank-payment-finance',
|
||||
'material-symbols:star-outline-rounded',
|
||||
'pepicons-pop:smartphone-home-button',
|
||||
'majesticons:desktop-computer-line',
|
||||
'mdi:folder',
|
||||
]
|
||||
|
||||
const icon = defineModel<string | undefined | null>({ default: '' })
|
||||
</script>
|
||||
84
src/components/ui/select/index.vue
Normal file
84
src/components/ui/select/index.vue
Normal file
@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div
|
||||
ref="activator"
|
||||
class="relative advance-select flex w-full input-group"
|
||||
>
|
||||
<button
|
||||
:id
|
||||
class="advance-select-toogle flex justify-between grow p-3"
|
||||
@click.prevent="toogleMenu"
|
||||
:disabled="read_only"
|
||||
>
|
||||
<slot
|
||||
name="value"
|
||||
:value
|
||||
class=""
|
||||
>
|
||||
<span>{{ value }}</span>
|
||||
</slot>
|
||||
</button>
|
||||
<button
|
||||
@click.prevent="toogleMenu"
|
||||
class="flex items-center p-2 hover:shadow rounded-md hover:bg-primary hover:text-base-content"
|
||||
:disabled="read_only"
|
||||
>
|
||||
<i class="i-[material-symbols--keyboard-arrow-down] size-4" />
|
||||
</button>
|
||||
<!-- <div data-select-dropdown="" class="absolute advance-select-menu max-h-44 top-full opened" role="listbox" tabindex="-1" aria-orientation="vertical" style="margin-top: 10px;"><div data-value="dark" data-title-value="Dunkel" tabindex="0" class="cursor-pointer advance-select-option selected:active" data-id="0"><div><div class="flex items-center"> <div class="me-2" data-icon=""><icon name="undefined" class="flex-shrink-0 size-4 text-base-content mt-1 max-w-full"></icon></div> <div class="font-semibold text-base-content" data-title="">Dunkel</div> </div> <div class="mt-1.5 text-sm text-base-content/80" data-description=""></div> </div></div><div data-value="light" data-title-value="Hell" tabindex="1" class="cursor-pointer advance-select-option selected:active" data-id="1"><div><div class="flex items-center"> <div class="me-2" data-icon=""><icon name="undefined" class="flex-shrink-0 size-4 text-base-content mt-1 max-w-full"></icon></div> <div class="font-semibold text-base-content" data-title="">Hell</div> </div> <div class="mt-1.5 text-sm text-base-content/80" data-description=""></div> </div></div><div data-value="soft" data-title-value="Soft" tabindex="2" class="cursor-pointer advance-select-option selected:active selected" data-id="2"><div><div class="flex items-center"> <div class="me-2" data-icon=""><icon name="undefined" class="flex-shrink-0 size-4 text-base-content mt-1 max-w-full"></icon></div> <div class="font-semibold text-base-content" data-title="">Soft</div> </div> <div class="mt-1.5 text-sm text-base-content/80" data-description=""></div> </div></div></div>
|
||||
class="absolute advance-select-menu max-h-44 top-full opened" -->
|
||||
<!-- Dropdown menu -->
|
||||
<ul
|
||||
data-select-dropdown
|
||||
classaaa="advance-select-menu bg-white divide-y divide-slate-100 rounded-lg shadow dark:bg-slate-700 absolute top-12"
|
||||
:class="{ hidden: !show }"
|
||||
class="absolute advance-select-menu max-h-44 top-full opened"
|
||||
role="listbox"
|
||||
tabindex="-1"
|
||||
aria-orientation="vertical"
|
||||
>
|
||||
<!-- <ul
|
||||
class=""
|
||||
:aria-labelledby="id"
|
||||
> -->
|
||||
<li
|
||||
v-for="(option, index) in options"
|
||||
:key="index"
|
||||
class="advance-select-option selected:active font-semibold text-base-content"
|
||||
@click=";(value = option), (show = false)"
|
||||
>
|
||||
<slot
|
||||
name="option"
|
||||
:option
|
||||
>
|
||||
{{ option }}
|
||||
</slot>
|
||||
</li>
|
||||
<!-- </ul> -->
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" generic="T">
|
||||
import { onClickOutside } from '@vueuse/core'
|
||||
|
||||
const id = useId()
|
||||
|
||||
defineProps({
|
||||
label: String,
|
||||
options: {
|
||||
type: Array as PropType<T[]>,
|
||||
default: () => [],
|
||||
},
|
||||
read_only: Boolean,
|
||||
})
|
||||
const value = defineModel<T>()
|
||||
|
||||
const show = ref(false)
|
||||
const toogleMenu = () => {
|
||||
show.value = !show.value
|
||||
}
|
||||
|
||||
const activator = ref(null)
|
||||
|
||||
onClickOutside(activator, () => (show.value = false))
|
||||
</script>
|
||||
@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<p class="bg-gradient-to-r from-primary to-accent bg-clip-text text-transparent font-black">
|
||||
<p
|
||||
class="bg-gradient-to-r from-primary to-accent bg-clip-text text-transparent font-black"
|
||||
>
|
||||
<slot />
|
||||
</p>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<UiDialog
|
||||
v-model:open="open"
|
||||
:title="t('title')"
|
||||
class="btn btn-primary btn-outline shadow-md md:btn-lg"
|
||||
class="btn btn-primary btn-outline shadow-md btn-lg"
|
||||
@click="open = true"
|
||||
>
|
||||
<template #trigger>
|
||||
@ -34,14 +34,14 @@
|
||||
|
||||
<template #buttons>
|
||||
<UiButton
|
||||
class="btn-error"
|
||||
class="btn-error w-full sm:w-auto"
|
||||
@click="onClose"
|
||||
>
|
||||
{{ t('abort') }}
|
||||
</UiButton>
|
||||
|
||||
<UiButton
|
||||
class="btn-primary"
|
||||
class="btn-primary w-full sm:w-auto"
|
||||
@click="onCreateAsync"
|
||||
>
|
||||
{{ t('create') }}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UiDialogConfirm
|
||||
v-model:open="open"
|
||||
class="btn btn-primary btn-outline shadow-md md:btn-lg"
|
||||
class="btn btn-primary btn-outline shadow-md btn-lg"
|
||||
:confirm-label="t('open')"
|
||||
:abort-label="t('abort')"
|
||||
@abort="onAbort"
|
||||
@ -12,13 +12,14 @@
|
||||
<i18n-t
|
||||
keypath="title"
|
||||
tag="p"
|
||||
class="flex gap-2"
|
||||
class="flex gap-x-2 flex-wrap"
|
||||
>
|
||||
<template #haexvault>
|
||||
<UiTextGradient>HaexVault</UiTextGradient>
|
||||
</template>
|
||||
</i18n-t>
|
||||
<p class="text-sm">{{ database.path }}</p>
|
||||
|
||||
<div class="text-sm">{{ props.path ?? database.path }}</div>
|
||||
</template>
|
||||
|
||||
<template #trigger>
|
||||
@ -63,11 +64,6 @@ const database = reactive<{
|
||||
type: 'password',
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.path,
|
||||
() => (database.path = props.path),
|
||||
)
|
||||
|
||||
const initDatabase = () => {
|
||||
database.name = ''
|
||||
database.password = ''
|
||||
|
||||
@ -65,7 +65,7 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex flex-col items-center w-full min-h-14 gap-2 py-1"
|
||||
class="flex items-center w-full min-h-14 gap-2 py-1"
|
||||
:style="{ color }"
|
||||
>
|
||||
<Icon
|
||||
@ -78,7 +78,7 @@
|
||||
v-show="read_only"
|
||||
class="overflow-hidden whitespace-nowrap"
|
||||
>
|
||||
{{ title }}
|
||||
a{{ title }}
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
@ -91,6 +91,7 @@
|
||||
class="fixed bottom-2 left-0 w-full flex items-center justify-between px-4 md:hidden"
|
||||
>
|
||||
<div class="transition-all duration-500">
|
||||
aa
|
||||
<button
|
||||
class="btn btn-square btn-error btn-outline"
|
||||
@click="onClose"
|
||||
|
||||
@ -3,59 +3,61 @@
|
||||
<slot name="image" />
|
||||
|
||||
<div class="card-header">
|
||||
<div v-if="$slots.title || title">
|
||||
<Icon v-if="icon" :name="icon" />
|
||||
<h5 v-if="title" class="card-title mb-0">
|
||||
{{ title }}
|
||||
</h5>
|
||||
<slot v-else name="title" />
|
||||
</div>
|
||||
<div class="text-base-content/50">Your journey starts here</div>
|
||||
<slot name="header">
|
||||
<div
|
||||
v-if="$slots.title || title"
|
||||
class="flex items-center gap-2"
|
||||
>
|
||||
<Icon
|
||||
v-if="icon"
|
||||
:name="icon"
|
||||
size="28"
|
||||
/>
|
||||
<h5
|
||||
v-if="title"
|
||||
class="card-title mb-0"
|
||||
>
|
||||
{{ title }}
|
||||
</h5>
|
||||
<slot
|
||||
v-else
|
||||
name="title"
|
||||
/>
|
||||
</div>
|
||||
<div class="text-base-content/50">{{ subtitle }}</div>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="card-body px-2 sm:px-6">
|
||||
<slot />
|
||||
aaaaaaaaa
|
||||
<div v-if="$slots.action" class="card-actions">
|
||||
<div
|
||||
v-if="$slots.action"
|
||||
class="card-actions"
|
||||
>
|
||||
<slot name="action" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="bg-base-100 w-full mx-auto shadow h-full overflow-hidden pt-[7.5rem]">
|
||||
<div
|
||||
class="fixed top-0 right-0 z-10 transition-all duration-700 w-full font-semibold text-lg h-[7.5rem]"
|
||||
>
|
||||
<div
|
||||
class="justify-center items-center flex flex-wrap border-b rounded-b border-secondary h-full"
|
||||
>
|
||||
<slot name="header" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="h-full overflow-scroll bg-base-200">
|
||||
<slot />
|
||||
</div>
|
||||
</div> -->
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const emit = defineEmits(["close", "submit"]);
|
||||
const emit = defineEmits(['close', 'submit'])
|
||||
|
||||
defineProps<{ title?: string; icon?: string }>();
|
||||
defineProps<{ title?: string; subtitle?: string; icon?: string }>()
|
||||
|
||||
const { escape, enter } = useMagicKeys();
|
||||
const { escape, enter } = useMagicKeys()
|
||||
|
||||
watchEffect(async () => {
|
||||
if (escape.value) {
|
||||
await nextTick();
|
||||
emit("close");
|
||||
await nextTick()
|
||||
emit('close')
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
watchEffect(async () => {
|
||||
if (enter.value) {
|
||||
await nextTick();
|
||||
emit("submit");
|
||||
await nextTick()
|
||||
emit('submit')
|
||||
}
|
||||
});
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -17,7 +17,6 @@
|
||||
v-model.trim="vaultGroup.name"
|
||||
:label="t('vaultGroup.name')"
|
||||
:placeholder="t('vaultGroup.name')"
|
||||
:rules="vaultGroupSchema.name"
|
||||
:with-copy-button="read_only"
|
||||
:read_only
|
||||
autofocus
|
||||
@ -29,18 +28,17 @@
|
||||
:read_only
|
||||
:label="t('vaultGroup.description')"
|
||||
:placeholder="t('vaultGroup.description')"
|
||||
:rules="vaultGroupSchema.description"
|
||||
:with-copy-button="read_only"
|
||||
/>
|
||||
|
||||
<UiColorPicker
|
||||
<UiSelectColor
|
||||
v-model="vaultGroup.color"
|
||||
:read_only
|
||||
:label="t('vaultGroup.color')"
|
||||
:placeholder="t('vaultGroup.color')"
|
||||
/>
|
||||
|
||||
<UiIconPicker
|
||||
<UiSelectIcon
|
||||
v-model="vaultGroup.icon"
|
||||
:read_only
|
||||
:label="t('vaultGroup.icon')"
|
||||
@ -51,29 +49,26 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { RouteLocationNormalizedLoadedGeneric } from 'vue-router';
|
||||
import {
|
||||
vaultGroupSchema,
|
||||
type SelectVaultGroup,
|
||||
} from '~/database/schemas/vault';
|
||||
import type { RouteLocationNormalizedLoadedGeneric } from 'vue-router'
|
||||
import type { SelectHaexPasswordsGroups } from '~~/src-tauri/database/schemas/vault'
|
||||
|
||||
const { t } = useI18n();
|
||||
const showConfirmation = ref(false);
|
||||
const vaultGroup = defineModel<SelectVaultGroup>({ required: true });
|
||||
const read_only = defineModel<boolean>('read_only');
|
||||
const { t } = useI18n()
|
||||
const showConfirmation = ref(false)
|
||||
const vaultGroup = defineModel<SelectHaexPasswordsGroups>({ required: true })
|
||||
const read_only = defineModel<boolean>('read_only')
|
||||
const props = defineProps({
|
||||
originally: Object as PropType<SelectVaultGroup>,
|
||||
});
|
||||
originally: Object as PropType<SelectHaexPasswordsGroups>,
|
||||
})
|
||||
|
||||
defineEmits<{
|
||||
submit: [to?: RouteLocationNormalizedLoadedGeneric];
|
||||
close: [void];
|
||||
back: [void];
|
||||
reject: [to?: RouteLocationNormalizedLoadedGeneric];
|
||||
}>();
|
||||
submit: [to?: RouteLocationNormalizedLoadedGeneric]
|
||||
close: [void]
|
||||
back: [void]
|
||||
reject: [to?: RouteLocationNormalizedLoadedGeneric]
|
||||
}>()
|
||||
|
||||
const hasChanges = computed(() => {
|
||||
console.log('group has changes', props.originally, vaultGroup.value);
|
||||
console.log('group has changes', props.originally, vaultGroup.value)
|
||||
if (!props.originally) {
|
||||
if (
|
||||
vaultGroup.value.color?.length ||
|
||||
@ -81,13 +76,13 @@ const hasChanges = computed(() => {
|
||||
vaultGroup.value.icon?.length ||
|
||||
vaultGroup.value.name?.length
|
||||
) {
|
||||
return true;
|
||||
return true
|
||||
} else {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
}
|
||||
return JSON.stringify(props.originally) !== JSON.stringify(vaultGroup.value);
|
||||
});
|
||||
return JSON.stringify(props.originally) !== JSON.stringify(vaultGroup.value)
|
||||
})
|
||||
|
||||
/* const onClose = () => {
|
||||
if (props.originally) vaultGroup.value = { ...props.originally };
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col min-w-min">
|
||||
<div class="h-full w-full flex flex-col overflow-hidden">
|
||||
<nav
|
||||
class="navbar bg-base-100 rounded-b max-sm:shadow border-b border-base-content/25 sm:z-20 relative px-2"
|
||||
class="navbar bg-base-100 rounded-b max-sm:shadow border-b border-base-content/25 sm:z-20 relative px-2 py-0 sm:py-2"
|
||||
>
|
||||
<UiTooltip :tooltip="isVisible ? t('sidebar.close') : t('sidebar.show')">
|
||||
<button
|
||||
@ -39,24 +39,26 @@
|
||||
<div
|
||||
class="dropdown relative inline-flex [--auto-close:inside] [--offset:20] [--placement:bottom-end]"
|
||||
>
|
||||
<button
|
||||
id="dropdown-scrollable"
|
||||
type="button"
|
||||
class="dropdown-toggle btn btn-text btn-circle dropdown-open:bg-base-content/10 size-10"
|
||||
aria-haspopup="menu"
|
||||
aria-expanded="false"
|
||||
aria-label="Dropdown"
|
||||
>
|
||||
<div class="indicator">
|
||||
<span
|
||||
v-show="notifications.length"
|
||||
class="indicator-item bg-error size-2 rounded-full text-sm"
|
||||
/>
|
||||
<span
|
||||
class="icon-[tabler--bell] text-base-content size-[1.375rem]"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
<UiTooltip :tooltip="t('notifications.label')">
|
||||
<button
|
||||
id="dropdown-scrollable"
|
||||
type="button"
|
||||
class="dropdown-toggle btn btn-text btn-circle dropdown-open:bg-base-content/10 size-10"
|
||||
aria-haspopup="menu"
|
||||
aria-expanded="false"
|
||||
aria-label="Dropdown"
|
||||
>
|
||||
<div class="indicator">
|
||||
<span
|
||||
v-show="notifications.length"
|
||||
class="indicator-item bg-error size-2 rounded-full text-sm"
|
||||
/>
|
||||
<span
|
||||
class="icon-[tabler--bell] text-base-content size-[1.375rem]"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
</UiTooltip>
|
||||
<div
|
||||
class="dropdown-menu dropdown-open:opacity-100 hidden"
|
||||
role="menu"
|
||||
@ -69,7 +71,7 @@
|
||||
</h6>
|
||||
</div>
|
||||
<div
|
||||
class="vertical-scrollbar horizontal-scrollbar rounded-scrollbar text-base-content/80 max-h-56 overflow-auto max-md:max-w-60"
|
||||
class="vertical-scrollbar horizontal-scrollbar rounded-scrollbar text-base-content/80 max-h-56 overflow-auto max-w-full"
|
||||
>
|
||||
<div
|
||||
v-for="notification in notifications"
|
||||
@ -99,13 +101,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a
|
||||
href="#"
|
||||
<NuxtLinkLocale
|
||||
:to="{ name: 'notifications' }"
|
||||
class="dropdown-footer justify-center gap-1"
|
||||
>
|
||||
<span class="icon-[tabler--eye] size-4" />
|
||||
{{ t('notifications.view_all') }}
|
||||
</a>
|
||||
</NuxtLinkLocale>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -113,7 +115,7 @@
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="flex h-full overflow-hidden">
|
||||
<div class="flex h-full w-full overflow-hidden">
|
||||
<aside
|
||||
id="sidebar"
|
||||
class="sm:shadow-none transition-all h-full overflow-hidden border-r border-base-300"
|
||||
@ -138,8 +140,8 @@
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main class="w-full h-full overflow-scroll">
|
||||
<NuxtPage :transition="{ name: 'fade' }" />
|
||||
<main class="w-full h-full overflow-auto">
|
||||
<NuxtPage />
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="w-screen h-screen overflow-scroll bg-base-200">
|
||||
<div class="w-screen h-screen bg-base-200 min-w-52">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="items-center justify-center min-h-full flex w-full relative">
|
||||
<div class="items-center justify-center h-full flex w-full relative">
|
||||
<div class="fixed top-2 right-2">
|
||||
<UiDropdownLocale @select="setLocale" />
|
||||
</div>
|
||||
@ -78,7 +78,6 @@
|
||||
import { UiLogoHaexhub } from '#components'
|
||||
import { openUrl } from '@tauri-apps/plugin-opener'
|
||||
|
||||
const refresh = () => location.reload()
|
||||
definePageMeta({
|
||||
name: 'vaultOpen',
|
||||
})
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="w-full h-full">
|
||||
<div class="h-full w-full">
|
||||
<NuxtLayout name="app">
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
|
||||
@ -109,10 +109,12 @@ const loadExtensionManifestAsync = async () => {
|
||||
} catch (error) {
|
||||
console.error('Fehler loadExtensionManifestAsync:', error)
|
||||
add({ type: 'error', text: JSON.stringify(error) })
|
||||
await addNotificationAsync({ text: JSON.stringify(error), type: 'error' })
|
||||
}
|
||||
}
|
||||
|
||||
const { add } = useSnackbar()
|
||||
const { addNotificationAsync } = useNotificationStore()
|
||||
|
||||
const prepareInstallExtensionAsyn = async () => {
|
||||
try {
|
||||
@ -132,6 +134,7 @@ const prepareInstallExtensionAsyn = async () => {
|
||||
}
|
||||
} catch (error) {
|
||||
add({ type: 'error', text: JSON.stringify(error) })
|
||||
await addNotificationAsync({ text: JSON.stringify(error), type: 'error' })
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,9 +150,17 @@ const addExtensionAsync = async () => {
|
||||
}),
|
||||
text: t('extension.success.text'),
|
||||
})
|
||||
await addNotificationAsync({
|
||||
text: t('extension.success.text'),
|
||||
type: 'success',
|
||||
title: t('extension.success.title', {
|
||||
extension: extension.manifest?.name,
|
||||
}),
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Fehler addExtensionAsync:', error)
|
||||
add({ type: 'error', text: JSON.stringify(error) })
|
||||
await addNotificationAsync({ text: JSON.stringify(error), type: 'error' })
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,12 +193,26 @@ const removeExtensionAsync = async () => {
|
||||
extensionName: extensionToBeRemoved.value.name,
|
||||
}),
|
||||
})
|
||||
await addNotificationAsync({
|
||||
text: t('extension.remove.success.text', {
|
||||
extensionName: extensionToBeRemoved.value.name,
|
||||
}),
|
||||
type: 'success',
|
||||
title: t('extension.remove.success.title', {
|
||||
extensionName: extensionToBeRemoved.value.name,
|
||||
}),
|
||||
})
|
||||
} catch (error) {
|
||||
add({
|
||||
type: 'error',
|
||||
title: t('extension.remove.error.title'),
|
||||
text: t('extension.remove.error.text', { error: JSON.stringify(error) }),
|
||||
})
|
||||
await addNotificationAsync({
|
||||
type: 'error',
|
||||
title: t('extension.remove.error.title'),
|
||||
text: t('extension.remove.error.text', { error: JSON.stringify(error) }),
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
<template>
|
||||
<div class="flex">
|
||||
<HaexPassSidebar />
|
||||
<div>
|
||||
<NuxtPage />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -1,9 +0,0 @@
|
||||
<template>
|
||||
<div>passwords</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
name: "haexpassOverview"
|
||||
})
|
||||
</script>
|
||||
148
src/pages/vault/[vaultId]/notifications.vue
Normal file
148
src/pages/vault/[vaultId]/notifications.vue
Normal file
@ -0,0 +1,148 @@
|
||||
<template>
|
||||
<div class="">
|
||||
<div class="p-6">
|
||||
<UiButton
|
||||
class="btn-error"
|
||||
@click="onDeleteNotificationsAsync"
|
||||
>
|
||||
{{ t('delete') }}
|
||||
</UiButton>
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<input
|
||||
v-model="selectAll"
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-primary checkbox-sm"
|
||||
aria-label="notification"
|
||||
/>
|
||||
</th>
|
||||
<th>{{ t('title') }}</th>
|
||||
<th>{{ t('text') }}</th>
|
||||
<th>{{ t('date') }}</th>
|
||||
<th>{{ t('type') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="notification in notifications"
|
||||
:key="notification.id"
|
||||
>
|
||||
<td>
|
||||
<label>
|
||||
<input
|
||||
v-model="selectedNotificationIds"
|
||||
:name="notification.id"
|
||||
:value="notification.id"
|
||||
aria-label="notification"
|
||||
class="checkbox checkbox-primary checkbox-sm"
|
||||
type="checkbox"
|
||||
/>
|
||||
</label>
|
||||
</td>
|
||||
<td>{{ notification.title }}</td>
|
||||
<td>{{ notification.text }}</td>
|
||||
<td>
|
||||
{{
|
||||
notification.date
|
||||
? new Date(notification.date).toLocaleDateString(locale, {
|
||||
dateStyle: 'short',
|
||||
})
|
||||
: ''
|
||||
}}
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
class="badge badge-soft text-xs"
|
||||
:class="badgeClass[notification.type]"
|
||||
>
|
||||
{{ t(`types.${notification.type}`) }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { SelectHaexNotifications } from '~~/src-tauri/database/schemas/vault'
|
||||
|
||||
definePageMeta({
|
||||
name: 'notifications',
|
||||
})
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
|
||||
const notifications = ref<SelectHaexNotifications[]>([])
|
||||
|
||||
const { deleteNotificationsAsync, syncNotificationsAsync } =
|
||||
useNotificationStore()
|
||||
|
||||
onMounted(async () => {
|
||||
await syncNotificationsAsync()
|
||||
})
|
||||
|
||||
const selectedNotificationIds = ref<string[]>([])
|
||||
const selectAll = computed({
|
||||
get() {
|
||||
return (
|
||||
notifications.value.length > 0 &&
|
||||
notifications.value.length === selectedNotificationIds.value.length
|
||||
)
|
||||
},
|
||||
set(value: boolean) {
|
||||
selectedNotificationIds.value = value
|
||||
? [...notifications.value.map((notification) => notification.id)]
|
||||
: []
|
||||
},
|
||||
})
|
||||
|
||||
const { add } = useSnackbar()
|
||||
|
||||
const onDeleteNotificationsAsync = async () => {
|
||||
try {
|
||||
console.log('onDeleteNotificationsAsync', selectedNotificationIds.value)
|
||||
await deleteNotificationsAsync(selectedNotificationIds.value)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
add({ type: 'error', text: JSON.stringify(error) })
|
||||
}
|
||||
}
|
||||
|
||||
const badgeClass: Record<SelectHaexNotifications['type'], string> = {
|
||||
error: 'badge-error',
|
||||
info: 'badge-info',
|
||||
success: 'badge-success',
|
||||
warning: 'badge-warning',
|
||||
log: 'badge-accent',
|
||||
}
|
||||
</script>
|
||||
|
||||
<i18n lang="yaml">
|
||||
de:
|
||||
title: Titel
|
||||
text: Text
|
||||
type: Typ
|
||||
date: Datum
|
||||
delete: Benachrichtigungen löschen
|
||||
types:
|
||||
error: Fehler
|
||||
info: Info
|
||||
success: Erfolg
|
||||
warning: Warnung
|
||||
|
||||
en:
|
||||
title: Title
|
||||
text: Text
|
||||
type: Type
|
||||
date: Date
|
||||
delete: Delete Notifications
|
||||
types:
|
||||
error: Error
|
||||
info: Info
|
||||
success: Success
|
||||
warning: Warning
|
||||
</i18n>
|
||||
13
src/pages/vault/[vaultId]/passwords.vue
Normal file
13
src/pages/vault/[vaultId]/passwords.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div class="p-4">
|
||||
<NuxtPage />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
name: 'passwords',
|
||||
})
|
||||
|
||||
const { currentGroupId } = storeToRefs(usePasswordGroupStore())
|
||||
</script>
|
||||
147
src/pages/vault/[vaultId]/passwords/[[groupId]]/create.vue
Normal file
147
src/pages/vault/[vaultId]/passwords/[[groupId]]/create.vue
Normal file
@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<div>
|
||||
<VaultCard
|
||||
:title="t('title')"
|
||||
icon="mdi:folder-plus-outline"
|
||||
@close="onClose"
|
||||
>
|
||||
<div
|
||||
class="flex flex-col gap-4 w-full p-4"
|
||||
@keyup.enter="onCreate"
|
||||
>
|
||||
<UiInput
|
||||
:check-input="check"
|
||||
:label="t('name.label')"
|
||||
:placeholder="t('name.label')"
|
||||
autofocus
|
||||
v-model:errors="errors.name"
|
||||
v-model="vaultGroup.name"
|
||||
/>
|
||||
|
||||
<UiInput
|
||||
v-model="vaultGroup.description"
|
||||
:check-input="check"
|
||||
:label="t('description.label')"
|
||||
:placeholder="t('description.label')"
|
||||
/>
|
||||
|
||||
<UiSelectColor v-model="vaultGroup.color" />
|
||||
|
||||
{{ vaultGroup.icon }}
|
||||
<UiSelectIcon v-model="vaultGroup.icon" />
|
||||
|
||||
<div class="flex flex-wrap justify-end gap-4">
|
||||
<button
|
||||
class="btn btn-error btn-outline flex-1 flex-nowrap"
|
||||
@click="onClose"
|
||||
type="button"
|
||||
>
|
||||
{{ t('abort') }}
|
||||
<Icon name="mdi:close" />
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-primary flex-1 flex-nowrap"
|
||||
type="button"
|
||||
@click="onCreate"
|
||||
>
|
||||
{{ t('create') }}
|
||||
<Icon name="mdi:check" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</VaultCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { InsertHaexPasswordsGroups } from '~~/src-tauri/database/schemas/vault'
|
||||
|
||||
definePageMeta({
|
||||
name: 'passwordGroupCreate',
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const check = ref(false)
|
||||
|
||||
const { currentGroupId } = storeToRefs(usePasswordGroupStore())
|
||||
const vaultGroup = ref<InsertHaexPasswordsGroups>({
|
||||
name: '',
|
||||
description: '',
|
||||
id: '',
|
||||
color: null,
|
||||
icon: null,
|
||||
order: null,
|
||||
parentId: currentGroupId.value,
|
||||
})
|
||||
|
||||
const errors = ref({
|
||||
name: [],
|
||||
description: [],
|
||||
})
|
||||
|
||||
const onClose = () => {
|
||||
useRouter().back()
|
||||
}
|
||||
|
||||
const onCreate = async () => {
|
||||
try {
|
||||
check.value = true
|
||||
|
||||
if (errors.value.name.length || errors.value.description.length) return
|
||||
|
||||
const { addGroupAsync } = usePasswordGroupStore()
|
||||
|
||||
const newGroup = await addGroupAsync(vaultGroup.value)
|
||||
|
||||
console.log('newGroup', newGroup)
|
||||
if (!newGroup.id) {
|
||||
return
|
||||
}
|
||||
//console.log('created group with id', newGroup?.id)
|
||||
|
||||
//currentGroupId.value = newGroup?.id
|
||||
await navigateTo(
|
||||
useLocalePath()({
|
||||
name: 'passwordGroupItems',
|
||||
params: {
|
||||
groupId: newGroup.id,
|
||||
},
|
||||
query: {
|
||||
...useRoute().query,
|
||||
},
|
||||
}),
|
||||
)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"de": {
|
||||
"title": "Neue Gruppe anlegen",
|
||||
"abort": "Abbrechen",
|
||||
"create": "Anlegen",
|
||||
"name": {
|
||||
"label": "Name"
|
||||
},
|
||||
"description": {
|
||||
"label": "Beschreibung"
|
||||
}
|
||||
},
|
||||
|
||||
"en": {
|
||||
"title": "Create new Group",
|
||||
"abort": "Abort",
|
||||
"create": "Create",
|
||||
"name": {
|
||||
"label": "Name"
|
||||
},
|
||||
"description": {
|
||||
"label": "Description"
|
||||
}
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
150
src/pages/vault/[vaultId]/passwords/[[groupId]]/edit.vue
Normal file
150
src/pages/vault/[vaultId]/passwords/[[groupId]]/edit.vue
Normal file
@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<div>
|
||||
<VaultGroup
|
||||
@submit="onSaveAsync"
|
||||
@back="onBackAsync"
|
||||
@reject="onRejectAsync"
|
||||
@close="onCloseAsync"
|
||||
v-model="vaultGroup"
|
||||
v-model:read_only="read_only"
|
||||
:originally
|
||||
>
|
||||
<!-- <template #bottom="{ onSubmit, onClose }">
|
||||
<button
|
||||
class="btn btn-error flex-1 flex-nowrap"
|
||||
@click="onClose"
|
||||
type="button"
|
||||
>
|
||||
{{ t('abort') }}
|
||||
<Icon name="mdi:close" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="btn btn-primary flex-1 flex-nowrap"
|
||||
type="button"
|
||||
@click="onSubmit"
|
||||
>
|
||||
{{ t('save') }}
|
||||
<Icon name="mdi:check" />
|
||||
</button>
|
||||
</template> -->
|
||||
</VaultGroup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { RouteLocationNormalizedLoadedGeneric } from 'vue-router'
|
||||
import type { SelectHaexPasswordsGroups } from '~~/src-tauri/database/schemas/vault'
|
||||
|
||||
definePageMeta({
|
||||
name: 'passwordGroupEdit',
|
||||
})
|
||||
|
||||
const { read_only } = storeToRefs(useVaultStore())
|
||||
|
||||
const vaultGroup = ref<SelectHaexPasswordsGroups>({
|
||||
color: '',
|
||||
description: '',
|
||||
icon: '',
|
||||
id: '',
|
||||
name: '',
|
||||
order: null,
|
||||
parentId: '',
|
||||
createdAt: null,
|
||||
updateAt: null,
|
||||
})
|
||||
|
||||
const originally = ref<SelectHaexPasswordsGroups>()
|
||||
|
||||
const onCloseAsync = async () => {
|
||||
if (read_only.value) return navigateToGroupItemsAsync(vaultGroup.value.id)
|
||||
else read_only.value = true
|
||||
}
|
||||
/* {
|
||||
await navigateTo(
|
||||
useLocaleRoute()({
|
||||
name: 'vaultGroupEntries',
|
||||
params: {
|
||||
...useRouter().currentRoute.value.params,
|
||||
},
|
||||
query: {
|
||||
...useRouter().currentRoute.value.query,
|
||||
},
|
||||
})
|
||||
);
|
||||
}; */
|
||||
|
||||
const { currentGroupId } = storeToRefs(usePasswordGroupStore())
|
||||
const { readGroupAsync, navigateToGroupItemsAsync } = usePasswordGroupStore()
|
||||
|
||||
const getGroupAsync = async () => {
|
||||
if (!currentGroupId.value) return
|
||||
|
||||
const group = await readGroupAsync(currentGroupId.value)
|
||||
console.log('found group', group)
|
||||
if (group) {
|
||||
vaultGroup.value = group
|
||||
originally.value = { ...group }
|
||||
}
|
||||
}
|
||||
watch(currentGroupId, async () => getGroupAsync(), { immediate: true })
|
||||
|
||||
const { add } = useSnackbar()
|
||||
|
||||
const onSaveAsync = async (to?: RouteLocationNormalizedLoadedGeneric) => {
|
||||
try {
|
||||
const { updateAsync } = usePasswordGroupStore()
|
||||
await updateAsync(vaultGroup.value)
|
||||
await getGroupAsync()
|
||||
read_only.value = true
|
||||
if (to) {
|
||||
return navigateTo(to)
|
||||
}
|
||||
} catch (error) {
|
||||
add({
|
||||
type: 'error',
|
||||
text: JSON.stringify(error),
|
||||
})
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
const onBackAsync = async () => {
|
||||
if (originally.value) vaultGroup.value = { ...originally.value }
|
||||
await navigateToGroupItemsAsync(vaultGroup.value.id)
|
||||
}
|
||||
|
||||
const onRejectAsync = async (to?: RouteLocationNormalizedLoadedGeneric) => {
|
||||
if (originally.value) vaultGroup.value = { ...originally.value }
|
||||
if (to) return navigateTo(to)
|
||||
else return onBackAsync
|
||||
}
|
||||
</script>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"de": {
|
||||
"title": "Gruppe anpassen",
|
||||
"abort": "Abbrechen",
|
||||
"save": "Speichern",
|
||||
"name": {
|
||||
"label": "Name"
|
||||
},
|
||||
"description": {
|
||||
"label": "Beschreibung"
|
||||
}
|
||||
},
|
||||
|
||||
"en": {
|
||||
"title": "Edit Group",
|
||||
"abort": "Abort",
|
||||
"save": "Save",
|
||||
"name": {
|
||||
"label": "Name"
|
||||
},
|
||||
"description": {
|
||||
"label": "Description"
|
||||
}
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
40
src/pages/vault/[vaultId]/passwords/[[groupId]]/index.vue
Normal file
40
src/pages/vault/[vaultId]/passwords/[[groupId]]/index.vue
Normal file
@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div class="relative h-full">
|
||||
<div>
|
||||
<HaexPassMobileMenu :group-items="currentGroupItems" />
|
||||
|
||||
<div
|
||||
class="fixed bottom-4 flex justify-center w-full transition-all pointer-events-none"
|
||||
:class="[isVisible ? 'left-15' : 'left-0']"
|
||||
>
|
||||
<!-- <UiButton class="btn btn-primary btn-lg btn-square rotate-45">
|
||||
<Icon name="mdi:plus" />
|
||||
</UiButton> -->
|
||||
<UiButtonAction
|
||||
class="pointer-events-auto"
|
||||
:menu
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { SelectHaexPasswordsItems } from '~~/src-tauri/database/schemas/vault'
|
||||
|
||||
definePageMeta({
|
||||
name: 'passwordGroupItems',
|
||||
})
|
||||
|
||||
const { menu } = storeToRefs(usePasswordsActionMenuStore())
|
||||
const items = ref<SelectHaexPasswordsItems[]>([])
|
||||
|
||||
const { readGroupItemsAsync } = usePasswordGroupStore()
|
||||
const { currentGroupItems } = storeToRefs(usePasswordGroupStore())
|
||||
const { isVisible } = storeToRefs(useSidebarStore())
|
||||
|
||||
console.log('currentGroupItems', currentGroupItems.value)
|
||||
const test = () => console.log('currentGroupItems', currentGroupItems.value)
|
||||
|
||||
onMounted(async () => {})
|
||||
</script>
|
||||
@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<div>item</div>
|
||||
</template>
|
||||
@ -0,0 +1 @@
|
||||
<template><div>create item</div></template>
|
||||
@ -29,14 +29,10 @@
|
||||
|
||||
<div class="p-2">{{ t('notifications.label') }}</div>
|
||||
<div class="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="switch switch-primary"
|
||||
:checked="isNotificationAllowed"
|
||||
readonly
|
||||
/>
|
||||
{{ isNotificationAllowed }}
|
||||
<UiButton @click="requestNotificationPermissionAsync">
|
||||
<UiButton
|
||||
class="btn-primary"
|
||||
@click="requestNotificationPermissionAsync"
|
||||
>
|
||||
{{ t('notifications.requestPermission') }}
|
||||
</UiButton>
|
||||
</div>
|
||||
@ -49,7 +45,7 @@ import type { Locale } from 'vue-i18n'
|
||||
import { haexSettings } from '~~/src-tauri/database/schemas/vault'
|
||||
|
||||
definePageMeta({
|
||||
name: 'haexSettings',
|
||||
name: 'settings',
|
||||
})
|
||||
|
||||
const { t, setLocale } = useI18n()
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import 'flyonui/flyonui'
|
||||
import { Observer } from 'tailwindcss-intersect'
|
||||
|
||||
Observer.start()
|
||||
import type {
|
||||
HSOverlay,
|
||||
IStaticMethods,
|
||||
|
||||
@ -1,91 +0,0 @@
|
||||
export interface ResourceRequestDetails {
|
||||
url: string
|
||||
resourceType: string
|
||||
tabId?: string
|
||||
frameId?: number
|
||||
}
|
||||
|
||||
export interface ResourceRequestResult {
|
||||
cancel: boolean
|
||||
redirectUrl?: string
|
||||
}
|
||||
|
||||
export interface ContentScript {
|
||||
code: string
|
||||
matches?: string[]
|
||||
runAt?: 'document_start' | 'document_end' | 'document_idle'
|
||||
}
|
||||
|
||||
export interface Extension {
|
||||
id: string
|
||||
name: string
|
||||
version: string
|
||||
description?: string
|
||||
processNavigation?: (url: string) => boolean
|
||||
processResourceRequest?: (
|
||||
details: ResourceRequestDetails,
|
||||
) => ResourceRequestResult
|
||||
contentScripts?: ContentScript[]
|
||||
}
|
||||
|
||||
export const useBrowserExtensionStore = defineStore(
|
||||
'useBrowserExtensionStore',
|
||||
() => {
|
||||
const extensions = ref<Extension[]>([])
|
||||
const isInitialized = ref<boolean>(false)
|
||||
|
||||
return {
|
||||
extensions,
|
||||
isInitialized,
|
||||
initializeAsync,
|
||||
processNavigation,
|
||||
injectContentScripts,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
const initializeAsync = async () => {
|
||||
const { isInitialized } = storeToRefs(useBrowserExtensionStore())
|
||||
return
|
||||
if (isInitialized.value) return
|
||||
|
||||
// Lade Erweiterungen aus dem Erweiterungsverzeichnis
|
||||
try {
|
||||
const extensions = await loadExtensionsAsync()
|
||||
for (const extension of extensions) {
|
||||
registerExtension(extension)
|
||||
}
|
||||
|
||||
isInitialized.value = true
|
||||
console.log(`${extensions.length} Erweiterungen geladen`)
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Erweiterungen:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const loadExtensionsAsync = async (): Promise<Extension[]> => {
|
||||
// In einer realen Implementierung würden Sie hier Erweiterungen aus einem Verzeichnis laden
|
||||
// Für dieses Beispiel verwenden wir hartcodierte Erweiterungen
|
||||
/* const adBlocker = (await import('./ad-blocker')).default;
|
||||
const trackerBlocker = (await import('./tracker-blocker')).default; */
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
const registerExtension = (extension: Extension): boolean => {
|
||||
const { extensions } = storeToRefs(useBrowserExtensionStore())
|
||||
if (!extension.id || !extension.name) {
|
||||
console.error('Ungültige Erweiterung:', extension)
|
||||
return false
|
||||
}
|
||||
|
||||
console.log(`Erweiterung registriert: ${extension.name}`)
|
||||
extensions.value.push(extension)
|
||||
return true
|
||||
}
|
||||
|
||||
const processNavigation = () => {
|
||||
return true
|
||||
}
|
||||
|
||||
const injectContentScripts = () => {}
|
||||
8
src/stores/passwords/actionMenu/de.json
Normal file
8
src/stores/passwords/actionMenu/de.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"group": {
|
||||
"create": "Gruppe erstellen"
|
||||
},
|
||||
"entry": {
|
||||
"create": "Eintrag erstellen"
|
||||
}
|
||||
}
|
||||
8
src/stores/passwords/actionMenu/en.json
Normal file
8
src/stores/passwords/actionMenu/en.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"group": {
|
||||
"create": "Create Group"
|
||||
},
|
||||
"entry": {
|
||||
"create": "Create Entry"
|
||||
}
|
||||
}
|
||||
44
src/stores/passwords/actionMenu/index.ts
Normal file
44
src/stores/passwords/actionMenu/index.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import type { IActionMenuItem } from '~/components/ui/button/types'
|
||||
import de from './de.json'
|
||||
import en from './en.json'
|
||||
|
||||
export const usePasswordsActionMenuStore = defineStore(
|
||||
'passwordsActionMenuStore',
|
||||
() => {
|
||||
const { t } = useI18n({
|
||||
messages: {
|
||||
de: { passwordActionMenu: de },
|
||||
en: { passwordActionMenu: en },
|
||||
},
|
||||
})
|
||||
|
||||
const menu = computed<IActionMenuItem[]>(() => [
|
||||
{
|
||||
label: t('passwordActionMenu.group.create'),
|
||||
icon: 'mdi:folder-plus-outline',
|
||||
to: {
|
||||
name: 'passwordGroupCreate',
|
||||
params: { groupId: usePasswordGroupStore().currentGroupId },
|
||||
query: {
|
||||
...useRouter().currentRoute.value.query,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('passwordActionMenu.entry.create'),
|
||||
icon: 'mdi:key-plus',
|
||||
to: {
|
||||
name: 'passwordItemCreate',
|
||||
params: { groupId: usePasswordGroupStore().currentGroupId },
|
||||
query: {
|
||||
...useRouter().currentRoute.value.query,
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
return {
|
||||
menu,
|
||||
}
|
||||
},
|
||||
)
|
||||
188
src/stores/passwords/groups.ts
Normal file
188
src/stores/passwords/groups.ts
Normal file
@ -0,0 +1,188 @@
|
||||
import { and, eq, isNull, sql, type SQLWrapper } from 'drizzle-orm'
|
||||
import {
|
||||
haexPasswordsGroupItems,
|
||||
haexPasswordsGroups,
|
||||
haexPasswordsItems,
|
||||
type InsertHaexPasswordsGroups,
|
||||
type InsertHaexPasswordsItems,
|
||||
type SelectHaexPasswordsGroups,
|
||||
type SelectHaexPasswordsItems,
|
||||
} from '~~/src-tauri/database/schemas/vault'
|
||||
|
||||
export const usePasswordGroupStore = defineStore('passwordGroupStore', () => {
|
||||
const groups = ref<SelectHaexPasswordsGroups[]>([])
|
||||
|
||||
const currentGroupId = computed<string | null>({
|
||||
get: () =>
|
||||
getSingleRouteParam(useRouter().currentRoute.value.params.groupId) ||
|
||||
null,
|
||||
set: (newGroupId) => {
|
||||
console.log('set groupId', newGroupId)
|
||||
useRouter().currentRoute.value.params.groupId = newGroupId ?? ''
|
||||
},
|
||||
})
|
||||
|
||||
const currentGroup = ref()
|
||||
|
||||
const currentGroupItems = reactive<{
|
||||
items: SelectHaexPasswordsItems[]
|
||||
groups: SelectHaexPasswordsGroups[]
|
||||
}>({
|
||||
items: [],
|
||||
groups: [],
|
||||
})
|
||||
|
||||
const syncGroupItemsAsync = async (currentGroupId: string | null) => {
|
||||
const { addNotificationAsync } = useNotificationStore()
|
||||
const { readByGroupIdAsync } = usePasswordItemStore()
|
||||
/* const { currentGroup, groups, currentGroupItems } = storeToRefs(
|
||||
usePasswordGroupStore(),
|
||||
) */
|
||||
groups.value = await readGroupsAsync()
|
||||
currentGroup.value = groups.value?.find(
|
||||
(group) => group.id === currentGroupId,
|
||||
)
|
||||
try {
|
||||
currentGroupItems.groups =
|
||||
(await getByParentIdAsync(currentGroupId)) ?? []
|
||||
currentGroupItems.items = (await readByGroupIdAsync(currentGroupId)) ?? []
|
||||
console.log('search current group', groups.value, currentGroup.value)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
currentGroupItems.groups = []
|
||||
currentGroupItems.items = []
|
||||
await addNotificationAsync({
|
||||
type: 'log',
|
||||
text: JSON.stringify(error),
|
||||
})
|
||||
}
|
||||
}
|
||||
watch(
|
||||
currentGroupId,
|
||||
async () => {
|
||||
syncGroupItemsAsync(currentGroupId.value)
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
return {
|
||||
addGroupAsync,
|
||||
currentGroup,
|
||||
currentGroupId,
|
||||
currentGroupItems,
|
||||
groups,
|
||||
navigateToGroupAsync,
|
||||
navigateToGroupItemsAsync,
|
||||
readGroupAsync,
|
||||
readGroupItemsAsync,
|
||||
readGroupsAsync,
|
||||
updateAsync,
|
||||
}
|
||||
})
|
||||
|
||||
const addGroupAsync = async (group: Partial<InsertHaexPasswordsGroups>) => {
|
||||
const { currentVault } = useVaultStore()
|
||||
|
||||
const newGroup: InsertHaexPasswordsGroups = {
|
||||
id: crypto.randomUUID(),
|
||||
parentId: group.parentId,
|
||||
color: group.color,
|
||||
icon: group.icon,
|
||||
name: group.name,
|
||||
order: group.order,
|
||||
}
|
||||
await currentVault.drizzle.insert(haexPasswordsGroups).values(newGroup)
|
||||
return newGroup
|
||||
}
|
||||
|
||||
const readGroupAsync = async (groupId: string) => {
|
||||
const { currentVault } = useVaultStore()
|
||||
|
||||
return (
|
||||
await currentVault.drizzle
|
||||
.select()
|
||||
.from(haexPasswordsGroups)
|
||||
.where(eq(haexPasswordsGroups.id, groupId))
|
||||
).at(0)
|
||||
}
|
||||
|
||||
const readGroupsAsync = async (filter?: { parentId?: string | null }) => {
|
||||
const { currentVault } = storeToRefs(useVaultStore())
|
||||
if (filter?.parentId) {
|
||||
return await currentVault.value.drizzle
|
||||
.select()
|
||||
.from(haexPasswordsGroups)
|
||||
.where(eq(haexPasswordsGroups.id, filter.parentId))
|
||||
} else {
|
||||
return await currentVault.value.drizzle
|
||||
.select()
|
||||
.from(haexPasswordsGroups)
|
||||
.where(isNull(haexPasswordsGroups.parentId))
|
||||
.orderBy(sql`${haexPasswordsGroups.order} nulls last`)
|
||||
}
|
||||
}
|
||||
|
||||
const readGroupItemsAsync = async (id?: string | null) => {
|
||||
const { currentVault } = useVaultStore()
|
||||
|
||||
currentVault.drizzle.select().from(haexPasswordsGroupItems)
|
||||
}
|
||||
|
||||
const getByParentIdAsync = async (parentId?: string | null) => {
|
||||
try {
|
||||
const { currentVault } = useVaultStore()
|
||||
|
||||
if (parentId) {
|
||||
const groups = await currentVault.drizzle
|
||||
.select()
|
||||
.from(haexPasswordsGroups)
|
||||
.where(eq(haexPasswordsGroups.parentId, parentId))
|
||||
.orderBy(sql`${haexPasswordsGroups.order} nulls last`)
|
||||
|
||||
console.log('found groups', groups)
|
||||
return groups
|
||||
} else {
|
||||
const groups = await currentVault.drizzle
|
||||
.select()
|
||||
.from(haexPasswordsGroups)
|
||||
.where(isNull(haexPasswordsGroups.parentId))
|
||||
.orderBy(sql`${haexPasswordsGroups.order} nulls last`)
|
||||
|
||||
console.log('found groups', groups)
|
||||
return groups
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
const navigateToGroupAsync = (groupId?: string | null) =>
|
||||
navigateTo(
|
||||
useLocaleRoute()({
|
||||
name: 'passwordGroupEdit',
|
||||
params: {
|
||||
vaultId: useRouter().currentRoute.value.params.vaultId,
|
||||
groupId,
|
||||
},
|
||||
query: {
|
||||
...useRouter().currentRoute.value.query,
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
const updateAsync = async () => {}
|
||||
|
||||
const navigateToGroupItemsAsync = (groupId: string) => {
|
||||
navigateTo(
|
||||
useLocaleRoute()({
|
||||
name: 'passwordGroupItems',
|
||||
params: {
|
||||
vaultId: useRouter().currentRoute.value.params.vaultId,
|
||||
groupId,
|
||||
},
|
||||
query: {
|
||||
...useRouter().currentRoute.value.query,
|
||||
},
|
||||
}),
|
||||
)
|
||||
}
|
||||
28
src/stores/passwords/history.ts
Normal file
28
src/stores/passwords/history.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { haexPasswordsItemHistory } from '~~/src-tauri/database/schemas/vault'
|
||||
|
||||
export const usePasswordHistoryStore = defineStore(
|
||||
'passwordHistoryStore',
|
||||
() => {
|
||||
return { getAsync }
|
||||
},
|
||||
)
|
||||
|
||||
const getAsync = async (itemId: string | null) => {
|
||||
if (!itemId) return null
|
||||
|
||||
try {
|
||||
const { currentVault } = useVaultStore()
|
||||
|
||||
const history = await currentVault.drizzle
|
||||
.select()
|
||||
.from(haexPasswordsItemHistory)
|
||||
.where(eq(haexPasswordsItemHistory.itemId, itemId))
|
||||
|
||||
console.log('found history ', history)
|
||||
return history
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
120
src/stores/passwords/items.ts
Normal file
120
src/stores/passwords/items.ts
Normal file
@ -0,0 +1,120 @@
|
||||
import { eq, isNull } from 'drizzle-orm'
|
||||
import {
|
||||
haexPasswordsGroupItems,
|
||||
haexPasswordsGroups,
|
||||
haexPasswordsItems,
|
||||
type InsertHaexPasswordsItems,
|
||||
type InsertHaexPasswordsItemsKeyValues,
|
||||
} from '~~/src-tauri/database/schemas/vault'
|
||||
|
||||
export const usePasswordItemStore = defineStore('passwordItemStore', () => {
|
||||
const currentItemId = computed({
|
||||
get: () =>
|
||||
getSingleRouteParam(useRouter().currentRoute.value.params.itemId),
|
||||
set: (entryId) => {
|
||||
console.log('set entryId', entryId)
|
||||
useRouter().currentRoute.value.params.entryId = entryId ?? ''
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
currentItemId,
|
||||
addAsync,
|
||||
readByGroupIdAsync,
|
||||
readAsync,
|
||||
readKeyValuesAsync,
|
||||
}
|
||||
})
|
||||
|
||||
const addAsync = async (
|
||||
item: InsertHaexPasswordsItems,
|
||||
keyValues: InsertHaexPasswordsItemsKeyValues,
|
||||
) => {
|
||||
const { currentVault } = useVaultStore()
|
||||
/* const { currentGroupId } = useVaultGroupStore();
|
||||
|
||||
entry.id = crypto.randomUUID();
|
||||
entry.createdAt = null;
|
||||
entry.updateAt = null;
|
||||
console.log('store create entry', entry, currentGroupId);
|
||||
await currentVault?.drizzle.transaction(async (tx) => {
|
||||
await tx.insert(vaultEntry).values(entry);
|
||||
await tx
|
||||
.insert(vaultGroupEntry)
|
||||
.values({ entryId: entry.id, groupId: currentGroupId });
|
||||
});
|
||||
|
||||
return entry.id; */
|
||||
}
|
||||
|
||||
const readByGroupIdAsync = async (groupId?: string | null) => {
|
||||
try {
|
||||
const { currentVault } = useVaultStore()
|
||||
|
||||
console.log('get entries by groupId', groupId || null)
|
||||
|
||||
if (groupId) {
|
||||
const entries = await currentVault.drizzle
|
||||
.select()
|
||||
.from(haexPasswordsGroupItems)
|
||||
.innerJoin(
|
||||
haexPasswordsItems,
|
||||
eq(haexPasswordsItems.id, haexPasswordsGroupItems.itemId),
|
||||
)
|
||||
.where(eq(haexPasswordsGroupItems.groupId, groupId))
|
||||
|
||||
console.log('found entries by groupId', entries)
|
||||
return entries.map((entry) => entry.haex_passwords_items)
|
||||
} else {
|
||||
const entries = await currentVault.drizzle
|
||||
.select()
|
||||
.from(haexPasswordsGroupItems)
|
||||
.innerJoin(
|
||||
haexPasswordsItems,
|
||||
eq(haexPasswordsItems.id, haexPasswordsGroupItems.itemId),
|
||||
)
|
||||
.where(isNull(haexPasswordsGroupItems.groupId))
|
||||
|
||||
console.log('found entries', entries)
|
||||
return entries.map((entry) => entry.haex_passwords_items)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
const readAsync = async (itemId: string | null) => {
|
||||
if (!itemId) return null
|
||||
|
||||
try {
|
||||
const { currentVault } = useVaultStore()
|
||||
|
||||
const details =
|
||||
await currentVault.drizzle.query.haexPasswordsItems.findFirst({
|
||||
where: eq(haexPasswordsItems.id, itemId),
|
||||
})
|
||||
|
||||
if (!details) return {}
|
||||
|
||||
const history = (await usePasswordHistoryStore().getAsync(itemId)) ?? []
|
||||
const keyValues = (await readKeyValuesAsync(itemId)) ?? []
|
||||
|
||||
console.log('found item by id', { details, history, keyValues })
|
||||
return { details, history, keyValues }
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const readKeyValuesAsync = async (itemId: string | null) => {
|
||||
if (!itemId) return null
|
||||
const { currentVault } = useVaultStore()
|
||||
|
||||
const keyValues =
|
||||
await currentVault.drizzle.query.haexPasswordsItemsKeyValues.findMany({
|
||||
where: eq(haexPasswordsItems.id, itemId),
|
||||
})
|
||||
return keyValues
|
||||
}
|
||||
@ -17,7 +17,7 @@ export const useSidebarStore = defineStore('sidebarStore', () => {
|
||||
id: 'haex-pass',
|
||||
name: 'HaexPass',
|
||||
icon: 'mdi:safe',
|
||||
to: { name: 'haexpassOverview' },
|
||||
to: { name: 'passwords' },
|
||||
},
|
||||
{
|
||||
id: 'haex-extensions',
|
||||
|
||||
@ -93,6 +93,7 @@ export const useVaultStore = defineStore('vaultStore', () => {
|
||||
})
|
||||
console.log('select', rows)
|
||||
} else {
|
||||
console.log('sql_execute', sql, params)
|
||||
// Otherwise, use the execute method
|
||||
rows = await invoke<unknown[]>('sql_execute', {
|
||||
sql,
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { and, eq, or, type SQLWrapper } from 'drizzle-orm'
|
||||
import {
|
||||
haexNotifications,
|
||||
type InsertHaexNotifications,
|
||||
} from '~~/src-tauri/database/schemas/vault'
|
||||
import {
|
||||
channels,
|
||||
isPermissionGranted,
|
||||
requestPermission,
|
||||
sendNotification,
|
||||
@ -18,7 +17,7 @@ export interface IHaexNotification {
|
||||
image?: string | null
|
||||
alt?: string | null
|
||||
date: string | null
|
||||
type?: 'error' | 'success' | 'warning' | 'info' | null
|
||||
type?: 'error' | 'success' | 'warning' | 'info' | 'log' | null
|
||||
}
|
||||
|
||||
export const useNotificationStore = defineStore('notificationStore', () => {
|
||||
@ -29,15 +28,8 @@ export const useNotificationStore = defineStore('notificationStore', () => {
|
||||
const permission = await requestPermission()
|
||||
console.log('got permission', permission)
|
||||
isNotificationAllowed.value = permission === 'granted'
|
||||
sendNotification({
|
||||
title: 'Tauri',
|
||||
body: 'Tauri is awesome!',
|
||||
icon: 'dialog-information',
|
||||
})
|
||||
/* const existingChannels = await channels()
|
||||
console.log('existingChannels', existingChannels) */
|
||||
}
|
||||
const test = async () => console.log('test')
|
||||
|
||||
const checkNotificationAsync = async () => {
|
||||
isNotificationAllowed.value = await isPermissionGranted()
|
||||
return isNotificationAllowed.value
|
||||
@ -45,13 +37,24 @@ export const useNotificationStore = defineStore('notificationStore', () => {
|
||||
|
||||
const notifications = ref<IHaexNotification[]>([])
|
||||
|
||||
const readNotificationsAsync = async (read: boolean = false) => {
|
||||
const readNotificationsAsync = async (filter?: SQLWrapper[]) => {
|
||||
const { currentVault } = storeToRefs(useVaultStore())
|
||||
notifications.value = await currentVault.value.drizzle
|
||||
.select()
|
||||
.from(haexNotifications)
|
||||
.where(eq(haexNotifications.read, read))
|
||||
console.log('readNotificationsAsync', notifications.value)
|
||||
|
||||
console.log('readNotificationsAsync', filter)
|
||||
if (filter) {
|
||||
return await currentVault.value.drizzle
|
||||
.select()
|
||||
.from(haexNotifications)
|
||||
.where(and(...filter))
|
||||
} else {
|
||||
return await currentVault.value.drizzle.select().from(haexNotifications)
|
||||
}
|
||||
}
|
||||
|
||||
const syncNotificationsAsync = async () => {
|
||||
notifications.value = await readNotificationsAsync([
|
||||
eq(haexNotifications.read, false),
|
||||
])
|
||||
}
|
||||
|
||||
const addNotificationAsync = async (
|
||||
@ -61,21 +64,22 @@ export const useNotificationStore = defineStore('notificationStore', () => {
|
||||
try {
|
||||
const _notification: InsertHaexNotifications = {
|
||||
id: crypto.randomUUID(),
|
||||
type: notification.type || 'info',
|
||||
alt: notification.alt,
|
||||
date: new Date().toUTCString(),
|
||||
date: notification.date || new Date().toUTCString(),
|
||||
icon: notification.icon,
|
||||
image: notification.image,
|
||||
read: notification.read || false,
|
||||
text: notification.text ?? '',
|
||||
title: notification.title ?? '',
|
||||
source: notification.source,
|
||||
text: notification.text,
|
||||
title: notification.title,
|
||||
type: notification.type || 'info',
|
||||
}
|
||||
|
||||
await currentVault.value.drizzle
|
||||
.insert(haexNotifications)
|
||||
.values(_notification)
|
||||
|
||||
await readNotificationsAsync()
|
||||
await syncNotificationsAsync()
|
||||
|
||||
if (!isNotificationAllowed.value) {
|
||||
const permission = await requestPermission()
|
||||
@ -88,16 +92,29 @@ export const useNotificationStore = defineStore('notificationStore', () => {
|
||||
body: _notification.text!,
|
||||
})
|
||||
}
|
||||
} catch (error) {}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
const deleteNotificationsAsync = async (notificationIds: string[]) => {
|
||||
const { currentVault } = storeToRefs(useVaultStore())
|
||||
const filter = notificationIds.map((id) => eq(haexNotifications.id, id))
|
||||
|
||||
console.log('deleteNotificationsAsync', notificationIds)
|
||||
return currentVault.value.drizzle
|
||||
.delete(haexNotifications)
|
||||
.where(or(...filter))
|
||||
}
|
||||
|
||||
return {
|
||||
notifications,
|
||||
isNotificationAllowed,
|
||||
checkNotificationAsync,
|
||||
addNotificationAsync,
|
||||
checkNotificationAsync,
|
||||
deleteNotificationsAsync,
|
||||
isNotificationAllowed,
|
||||
notifications,
|
||||
readNotificationsAsync,
|
||||
requestNotificationPermissionAsync,
|
||||
test,
|
||||
syncNotificationsAsync,
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user