encryption of sqlite working

This commit is contained in:
Martin Drechsel
2025-04-28 12:18:39 +02:00
parent 2c5ec6b281
commit 410a885d21
31 changed files with 3664 additions and 1766 deletions

27
.gitignore vendored Normal file
View File

@ -0,0 +1,27 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.nuxt
.output

10
.prettierrc.toml Normal file
View File

@ -0,0 +1,10 @@
# .prettierrc.toml
useTabs = false
tabWidth = 2
printWidth = 100
endOfLine = "lf"
# Not supported yet
# trailingComma = "es5"
# embeddedLanguageFormatting = "auto"

7
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"recommendations": [
"Vue.volar",
"tauri-apps.tauri-vscode",
"rust-lang.rust-analyzer"
]
}

View File

@ -1,71 +1,71 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
compatibilityDate: '2024-11-01',
compatibilityDate: "2024-11-01",
modules: [
'nuxt-zod-i18n',
'@nuxtjs/i18n',
'@nuxtjs/tailwindcss',
'@pinia/nuxt',
'@vueuse/nuxt',
'@nuxt/icon',
'nuxt-snackbar',
"nuxt-zod-i18n",
"@nuxtjs/i18n",
"@nuxtjs/tailwindcss",
"@pinia/nuxt",
"@vueuse/nuxt",
"@nuxt/icon",
"nuxt-snackbar",
],
imports: {
dirs: ['composables/**', 'stores/**', 'components/**', 'pages/**'],
dirs: ["composables/**", "stores/**", "components/**", "pages/**"],
},
i18n: {
strategy: 'prefix_and_default',
defaultLocale: 'de',
vueI18n: '../src/i18n/i18n.config.ts',
strategy: "prefix_and_default",
defaultLocale: "de",
vueI18n: "../src/i18n/i18n.config.ts",
locales: [
{ code: 'de', language: 'de-DE', isCatchallLocale: true },
{ code: 'en', language: 'en-EN' },
{ code: "de", language: "de-DE", isCatchallLocale: true },
{ code: "en", language: "en-EN" },
],
detectBrowserLanguage: {
useCookie: true,
cookieKey: 'i18n_redirected',
redirectOn: 'root', // recommended
cookieKey: "i18n_redirected",
redirectOn: "root", // recommended
},
types: 'composition',
types: "composition",
bundle: {
optimizeTranslationDirective: false,
},
},
/* zodI18n: {
zodI18n: {
localeCodesMapping: {
'en-GB': 'en',
'de-DE': 'de',
"en-GB": "en",
"de-DE": "de",
},
},
}, */
runtimeConfig: {
public: {
haexVault: {
lastVaultFileName: 'lastVaults.json',
lastVaultFileName: "lastVaults.json",
//defaultDatabase: 'src/database/default.db',
},
},
},
devtools: { enabled: true },
srcDir: './src',
srcDir: "./src",
// Enable SSG
ssr: false,
// Enables the development server to be discoverable by other devices when running on iOS physical devices
devServer: { host: process.env.TAURI_DEV_HOST || 'localhost' },
devServer: { host: process.env.TAURI_DEV_HOST || "localhost", port: 3003 },
vite: {
// Better support for Tauri CLI output
clearScreen: false,
// Enable environment variables
// Additional environment variables can be found at
// https://v2.tauri.app/reference/environment-variables/
envPrefix: ['VITE_', 'TAURI_'],
envPrefix: ["VITE_", "TAURI_"],
server: {
// Tauri requires a consistent port
strictPort: true,

View File

@ -14,41 +14,41 @@
"drizzle:migrate": "drizzle-kit migrate"
},
"dependencies": {
"@libsql/client": "^0.15.1",
"@libsql/client": "^0.15.4",
"@nuxt/icon": "1.11.0",
"@nuxtjs/i18n": "^9.4.0",
"@nuxtjs/i18n": "^9.5.3",
"@pinia/nuxt": "^0.10.1",
"@tauri-apps/api": "^2.4.0",
"@tauri-apps/plugin-dialog": "^2.2.0",
"@tauri-apps/plugin-fs": "^2.2.0",
"@tauri-apps/plugin-http": "~2.4.2",
"@tauri-apps/api": "^2.5.0",
"@tauri-apps/plugin-dialog": "^2.2.1",
"@tauri-apps/plugin-fs": "^2.2.1",
"@tauri-apps/plugin-http": "~2.4.3",
"@tauri-apps/plugin-opener": "^2.2.6",
"@tauri-apps/plugin-os": "^2.2.1",
"@tauri-apps/plugin-sql": "~2.2.0",
"@tauri-apps/plugin-store": "^2.2.0",
"@vueuse/core": "^13.0.0",
"@vueuse/nuxt": "^13.0.0",
"@vueuse/core": "^13.1.0",
"@vueuse/nuxt": "^13.1.0",
"drizzle-orm": "^0.41.0",
"nuxt": "^3.16.1",
"nuxt": "^3.17.0",
"nuxt-snackbar": "1.3.0",
"nuxt-zod-i18n": "^1.11.5",
"vue": "^3.5.13",
"zod": "^3.24.2"
"zod": "^3.24.3"
},
"devDependencies": {
"@egoist/tailwindcss-icons": "^1.9.0",
"@iconify/json": "^2.2.321",
"@iconify/json": "^2.2.332",
"@iconify/tailwind": "^1.2.0",
"@nuxtjs/tailwindcss": "^6.13.2",
"@tauri-apps/cli": "^2.4.0",
"@nuxtjs/tailwindcss": "^6.14.0",
"@tauri-apps/cli": "^2.5.0",
"@vitejs/plugin-vue": "^5.2.3",
"drizzle-kit": "^0.30.6",
"flyonui": "^1.3.1",
"typescript": "~5.6.3",
"vite": "^6.2.3",
"vue-tsc": "^2.2.8"
"vite": "^6.3.3",
"vue-tsc": "^2.2.10"
},
"packageManager": "pnpm@10.5.2+sha512.da9dc28cd3ff40d0592188235ab25d3202add8a207afbedc682220e4a0029ffbff4562102b9e6e46b4e3f9e8bd53e6d05de48544b0c57d4b0179e22c76d1199b",
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39",
"pnpm": {
"ignoredBuiltDependencies": [
"@parcel/watcher",

3798
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

168
src-tauri/Cargo.lock generated
View File

@ -2107,9 +2107,9 @@ dependencies = [
[[package]]
name = "libsqlite3-sys"
version = "0.32.0"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbb8270bb4060bd76c6e96f20c52d80620f1d82a3470885694e41e0f81ef6fe7"
checksum = "947e6816f7825b2b45027c2c32e7085da9934defa535de4a6a46b10a4d5257fa"
dependencies = [
"cc",
"pkg-config",
@ -3340,9 +3340,9 @@ dependencies = [
[[package]]
name = "rusqlite"
version = "0.34.0"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e34486da88d8e051c7c0e23c3f15fd806ea8546260aa2fec247e97242ec143"
checksum = "a22715a5d6deef63c637207afbe68d0c72c3f8d0022d7cf9714c442d6157606b"
dependencies = [
"bitflags 2.9.0",
"fallible-iterator",
@ -3960,9 +3960,9 @@ dependencies = [
[[package]]
name = "tao"
version = "0.32.8"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63c8b1020610b9138dd7b1e06cf259ae91aa05c30f3bd0d6b42a03997b92dec1"
checksum = "1e59c1f38e657351a2e822eadf40d6a2ad4627b9c25557bc1180ec1b3295ef82"
dependencies = [
"bitflags 2.9.0",
"core-foundation 0.10.0",
@ -3991,8 +3991,8 @@ dependencies = [
"tao-macros",
"unicode-segmentation",
"url",
"windows",
"windows-core 0.60.1",
"windows 0.61.1",
"windows-core 0.61.0",
"windows-version",
"x11-dl",
]
@ -4016,9 +4016,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]]
name = "tauri"
version = "2.4.0"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "511dd38065a5d3b36c33cdba4362b99a40a5103bebcd4aebb930717e7c8ba292"
checksum = "e7b0bc1aec81bda6bc455ea98fcaed26b3c98c1648c627ad6ff1c704e8bf8cbc"
dependencies = [
"anyhow",
"bytes",
@ -4039,6 +4039,7 @@ dependencies = [
"objc2 0.6.0",
"objc2-app-kit",
"objc2-foundation 0.3.0",
"objc2-ui-kit",
"percent-encoding",
"plist",
"raw-window-handle",
@ -4061,14 +4062,14 @@ dependencies = [
"webkit2gtk",
"webview2-com",
"window-vibrancy",
"windows",
"windows 0.61.1",
]
[[package]]
name = "tauri-build"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffa8732a66f90903f5a585215f3cf1e87988d0359bc88c18a502efe7572c1de"
checksum = "d7a0350f0df1db385ca5c02888a83e0e66655c245b7443db8b78a70da7d7f8fc"
dependencies = [
"anyhow",
"cargo_toml",
@ -4088,9 +4089,9 @@ dependencies = [
[[package]]
name = "tauri-codegen"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c266a247f14d63f40c6282c2653a8bac5cc3d482ca562a003a88513653ea817a"
checksum = "f93f035551bf7b11b3f51ad9bc231ebbe5e085565527991c16cf326aa38cdf47"
dependencies = [
"base64 0.22.1",
"brotli",
@ -4115,9 +4116,9 @@ dependencies = [
[[package]]
name = "tauri-macros"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f47a1cf94b3bd6c4dc37dce1a43fc96120ff29a91757f0ab3cf713c7ad846e7c"
checksum = "8db4df25e2d9d45de0c4c910da61cd5500190da14ae4830749fee3466dddd112"
dependencies = [
"heck 0.5.0",
"proc-macro2",
@ -4225,7 +4226,7 @@ dependencies = [
"tauri-plugin",
"thiserror 2.0.12",
"url",
"windows",
"windows 0.60.0",
"zbus",
]
@ -4265,29 +4266,31 @@ dependencies = [
[[package]]
name = "tauri-runtime"
version = "2.5.0"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e9c7bce5153f1ca7bc45eba37349b31ba50e975e28edc8b5766c5ec02b0b63a"
checksum = "00f004905d549854069e6774533d742b03cacfd6f03deb08940a8677586cbe39"
dependencies = [
"cookie",
"dpi",
"gtk",
"http",
"jni",
"objc2 0.6.0",
"objc2-ui-kit",
"raw-window-handle",
"serde",
"serde_json",
"tauri-utils",
"thiserror 2.0.12",
"url",
"windows",
"windows 0.61.1",
]
[[package]]
name = "tauri-runtime-wry"
version = "2.5.0"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "087188020fd6facb8578fe9b38e81fa0fe5fb85744c73da51a299f94a530a1e3"
checksum = "f85d056f4d4b014fe874814034f3416d57114b617a493a4fe552580851a3f3a2"
dependencies = [
"gtk",
"http",
@ -4306,15 +4309,15 @@ dependencies = [
"url",
"webkit2gtk",
"webview2-com",
"windows",
"windows 0.61.1",
"wry",
]
[[package]]
name = "tauri-utils"
version = "2.3.0"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82dcced4014e59af9790cc22f5d271df3be09ecd6728ec68861642553c8d01b7"
checksum = "b2900399c239a471bcff7f15c4399eb1a8c4fe511ba2853e07c996d771a5e0a4"
dependencies = [
"anyhow",
"brotli",
@ -5049,15 +5052,15 @@ dependencies = [
[[package]]
name = "webview2-com"
version = "0.36.0"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0d606f600e5272b514dbb66539dd068211cc20155be8d3958201b4b5bd79ed3"
checksum = "b542b5cfbd9618c46c2784e4d41ba218c336ac70d44c55e47b251033e7d85601"
dependencies = [
"webview2-com-macros",
"webview2-com-sys",
"windows",
"windows-core 0.60.1",
"windows-implement",
"windows 0.61.1",
"windows-core 0.61.0",
"windows-implement 0.60.0",
"windows-interface",
]
@ -5074,13 +5077,13 @@ dependencies = [
[[package]]
name = "webview2-com-sys"
version = "0.36.0"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfb27fccd3c27f68e9a6af1bcf48c2d82534b8675b83608a4d81446d095a17ac"
checksum = "8ae2d11c4a686e4409659d7891791254cf9286d3cfe0eef54df1523533d22295"
dependencies = [
"thiserror 2.0.12",
"windows",
"windows-core 0.60.1",
"windows 0.61.1",
"windows-core 0.61.0",
]
[[package]]
@ -5135,11 +5138,24 @@ version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddf874e74c7a99773e62b1c671427abf01a425e77c3d3fb9fb1e4883ea934529"
dependencies = [
"windows-collections",
"windows-collections 0.1.1",
"windows-core 0.60.1",
"windows-future",
"windows-future 0.1.1",
"windows-link",
"windows-numerics",
"windows-numerics 0.1.1",
]
[[package]]
name = "windows"
version = "0.61.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
dependencies = [
"windows-collections 0.2.0",
"windows-core 0.61.0",
"windows-future 0.2.0",
"windows-link",
"windows-numerics 0.2.0",
]
[[package]]
@ -5151,6 +5167,15 @@ dependencies = [
"windows-core 0.60.1",
]
[[package]]
name = "windows-collections"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
dependencies = [
"windows-core 0.61.0",
]
[[package]]
name = "windows-core"
version = "0.52.0"
@ -5166,11 +5191,24 @@ version = "0.60.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca21a92a9cae9bf4ccae5cf8368dce0837100ddf6e6d57936749e85f152f6247"
dependencies = [
"windows-implement",
"windows-implement 0.59.0",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings",
"windows-strings 0.3.1",
]
[[package]]
name = "windows-core"
version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
dependencies = [
"windows-implement 0.60.0",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings 0.4.0",
]
[[package]]
@ -5183,6 +5221,16 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-future"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32"
dependencies = [
"windows-core 0.61.0",
"windows-link",
]
[[package]]
name = "windows-implement"
version = "0.59.0"
@ -5194,6 +5242,17 @@ dependencies = [
"syn 2.0.100",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "windows-interface"
version = "0.59.1"
@ -5221,6 +5280,16 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-numerics"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
dependencies = [
"windows-core 0.61.0",
"windows-link",
]
[[package]]
name = "windows-registry"
version = "0.4.0"
@ -5228,7 +5297,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3"
dependencies = [
"windows-result",
"windows-strings",
"windows-strings 0.3.1",
"windows-targets 0.53.0",
]
@ -5250,6 +5319,15 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
@ -5588,9 +5666,9 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
[[package]]
name = "wry"
version = "0.50.5"
version = "0.51.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b19b78efae8b853c6c817e8752fc1dbf9cab8a8ffe9c30f399bd750ccf0f0730"
checksum = "c886a0a9d2a94fd90cfa1d929629b79cfefb1546e2c7430c63a47f0664c0e4e2"
dependencies = [
"base64 0.22.1",
"block2 0.6.0",
@ -5624,8 +5702,8 @@ dependencies = [
"webkit2gtk",
"webkit2gtk-sys",
"webview2-com",
"windows",
"windows-core 0.60.1",
"windows 0.61.1",
"windows-core 0.61.0",
"windows-version",
"x11-dl",
]

View File

@ -15,23 +15,24 @@ name = "haex_hub_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies]
tauri-build = { version = "2", features = [] }
tauri-build = { version = "2.2", features = [] }
[dependencies]
rusqlite = { version = "0.34.0", features = [
rusqlite = { version = "0.35.0", features = [
"load_extension",
"bundled-sqlcipher",
] }
#libsqlite3-sys = { version = "0.32", features = ["bundled-sqlcipher"] }
tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread"] }
#libsqlite3-sys = { version = "0.28.0", features = ["bundled-sqlcipher"] }
#libsqlite3-sys = { version = "0.28", features = ["bundled-sqlcipher"] }
#sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "sqlite"] }
tokio = { version = "1.44", features = ["macros", "rt-multi-thread"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sqlparser = { version = "0.55.0", features = [] }
tauri = { version = "2", features = [] }
tauri-plugin-dialog = "2"
tauri-plugin-fs = "2"
tauri-plugin-opener = "2"
tauri = { version = "2.5", features = [] }
tauri-plugin-dialog = "2.2"
tauri-plugin-fs = "2.2.0"
tauri-plugin-opener = "2.2"
tauri-plugin-os = "2"
tauri-plugin-store = "2"
tauri-plugin-http = "2"
tauri-plugin-http = "2.4"
#tauri-plugin-sql = { version = "2", features = ["sqlite"] }

View File

@ -5,17 +5,21 @@ import {
type AnySQLiteColumn,
unique,
numeric,
} from 'drizzle-orm/sqlite-core';
} from "drizzle-orm/sqlite-core";
export const haexSettings = sqliteTable('haex_settings', {
export function generateCreateStatements() {
const tables = [haexSettings, haexExtensions, testTable, haexExtensionsPermissions];
}
export const haexSettings = sqliteTable("haex_settings", {
id: text().primaryKey(),
key: text(),
value_text: text(),
value_json: text({ mode: 'json' }),
value_json: text({ mode: "json" }),
value_number: numeric(),
});
export const haexExtensions = sqliteTable('haex_extensions', {
export const haexExtensions = sqliteTable("haex_extensions", {
id: text().primaryKey(),
author: text(),
enabled: integer(),
@ -24,21 +28,24 @@ export const haexExtensions = sqliteTable('haex_extensions', {
version: text(),
});
export const testTable = sqliteTable("testTable", {
id: text().primaryKey(),
author: text(),
test: text(),
});
export const haexExtensionsPermissions = sqliteTable(
'haex_extensions_permissions',
"haex_extensions_permissions",
{
id: text().primaryKey(),
extensionId: text('extension_id').references(
(): AnySQLiteColumn => haexExtensions.id
),
resource: text({ enum: ['fs', 'http', 'database'] }),
operation: text({ enum: ['read', 'write', 'create'] }),
extensionId: text("extension_id").references((): AnySQLiteColumn => haexExtensions.id),
resource: text({ enum: ["fs", "http", "database"] }),
operation: text({ enum: ["read", "write", "create"] }),
path: text(),
},
(table) => [
unique().on(table.extensionId, table.resource, table.operation, table.path),
]
(table) => [unique().on(table.extensionId, table.resource, table.operation, table.path)]
);
console.log("table", haexExtensionsPermissions.getSQL());
export type InsertHaexSettings = typeof haexSettings.$inferInsert;
export type SelectHaexSettings = typeof haexSettings.$inferSelect;
@ -46,7 +53,5 @@ export type SelectHaexSettings = typeof haexSettings.$inferSelect;
export type InsertHaexExtensions = typeof haexExtensions.$inferInsert;
export type SelectHaexExtensions = typeof haexExtensions.$inferSelect;
export type InsertHaexExtensionsPermissions =
typeof haexExtensionsPermissions.$inferInsert;
export type SelectHaexExtensionsPermissions =
typeof haexExtensionsPermissions.$inferSelect;
export type InsertHaexExtensionsPermissions = typeof haexExtensionsPermissions.$inferInsert;
export type SelectHaexExtensionsPermissions = typeof haexExtensionsPermissions.$inferSelect;

View File

@ -1,7 +1,7 @@
// database/mod.rs
pub mod core;
use rusqlite::Connection;
use rusqlite::{Connection, OpenFlags};
use std::path::Path;
use std::sync::Mutex;
use tauri::{path::BaseDirectory, AppHandle, Manager, State};
@ -58,7 +58,60 @@ pub fn create_encrypted_database(
}
}
// Kopieren der Ressourcen-Datenbank zum Zielpfad
// Neue Datenbank erstellen
let conn = Connection::open_with_flags(
&path,
OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE,
)
.map_err(|e| format!("Fehler beim Erstellen der Datenbank: {}", e.to_string()))?;
// Datenbank mit dem angegebenen Passwort verschlüsseln
conn.pragma_update(None, "key", &key)
.map_err(|e| format!("Fehler beim Verschlüsseln der Datenbank: {}", e.to_string()))?;
println!("Datenbank verschlüsselt mit key {}", &key);
// Überprüfen, ob die Datenbank korrekt verschlüsselt wurde
let validation_result: Result<i32, _> = conn.query_row("SELECT 1", [], |row| row.get(0));
if let Err(e) = validation_result {
return Err(format!(
"Fehler beim Testen der verschlüsselten Datenbank: {}",
e.to_string()
));
}
// 2. VERSUCHEN, EINE SQLCIPHER-SPEZIFISCHE OPERATION AUSZUFÜHREN
println!("Prüfe SQLCipher-Aktivität mit 'PRAGMA cipher_version;'...");
match conn.query_row("PRAGMA cipher_version;", [], |row| {
let version: String = row.get(0)?;
Ok(version)
}) {
Ok(version) => {
println!("SQLCipher ist aktiv! Version: {}", version);
// Fahre mit normalen Operationen fort
println!("Erstelle Tabelle 'benutzer'...");
conn.execute(
"CREATE TABLE benutzer (id INTEGER PRIMARY KEY, name TEXT NOT NULL)",
[],
)
.map_err(|e| format!("Fehler beim Verschlüsseln der Datenbank: {}", e.to_string()))?;
println!("Füge Benutzer 'Bob' hinzu...");
conn.execute("INSERT INTO benutzer (name) VALUES ('Bob')", [])
.map_err(|e| {
format!("Fehler beim Verschlüsseln der Datenbank: {}", e.to_string())
})?;
println!("Benutzer hinzugefügt.");
}
Err(e) => {
eprintln!("FEHLER: SQLCipher scheint NICHT aktiv zu sein!");
eprintln!("Der Befehl 'PRAGMA cipher_version;' schlug fehl: {}", e);
eprintln!("Die Datenbank wurde wahrscheinlich NICHT verschlüsselt.");
// Optional: Hier die Verbindung schließen oder weitere Aktionen unterlassen
// return Err(e); // Beende das Programm mit dem Fehler
}
}
/* // Kopieren der Ressourcen-Datenbank zum Zielpfad
core::copy_file(&resource_path, &path)?;
// Öffnen der kopierten Datenbank ohne Verschlüsselung
@ -90,16 +143,17 @@ pub fn create_encrypted_database(
e.to_string()
));
}
*/
// Aktualisieren der Datenbankverbindung im State
let mut db = state
.0
.lock()
.map_err(|e| format!("Mutex-Fehler: {}", e.to_string()))?;
*db = Some(encrypted_conn);
*db = Some(conn);
Ok(format!(
"Verschlüsselte CRDT-Datenbank erstellt unter: {} (kopiert aus Ressource)",
path
"Verschlüsselte CRDT-Datenbank erstellt unter: {} and password",
key
))
}

View File

@ -1,7 +1,7 @@
mod permissions;
use crate::database;
use crate::database::DbConnection;
//use crate::models::ExtensionState;
use crate::models::ExtensionState;
use tauri::{AppHandle, State};
// Extension-bezogene Funktionen mit extension_-Präfix
@ -17,8 +17,8 @@ pub fn extension_load(
app.state::<ExtensionState>()
.add_extension(manifest_path.clone(), manifest.clone());
Ok(manifest)
} */
}
*/
/// Führt SQL-Leseoperationen mit Berechtigungsprüfung aus
#[tauri::command]
pub async fn extension_sql_select(

View File

@ -1,10 +1,10 @@
mod browser;
//mod browser;
mod database;
mod extension;
mod models;
use database::DbConnection;
//use models::ExtensionState;
use models::ExtensionState;
use std::sync::Mutex;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
@ -12,12 +12,13 @@ pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_http::init())
.manage(DbConnection(Mutex::new(None)))
//.manage(ExtensionState::default())
.manage(ExtensionState::default())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_store::Builder::new().build())
//.plugin(tauri_plugin_sql::Builder::new().build())
.invoke_handler(tauri::generate_handler![
database::create_encrypted_database,
database::open_encrypted_database,
@ -25,7 +26,7 @@ pub fn run() {
database::sql_select,
extension::database::extension_sql_execute,
extension::database::extension_sql_select,
browser::create_tab
//browser::create_tab
])
.run(tauri::generate_context!())
.expect("error while running tauri application");

View File

@ -1,6 +1,6 @@
// models.rs
use serde::{Deserialize, Serialize};
//use std::sync::Mutex;
use std::sync::Mutex;
#[derive(Serialize, Deserialize, Clone)]
pub struct ExtensionManifest {
@ -23,7 +23,7 @@ pub struct DatabasePermissions {
pub create: Option<Vec<String>>,
}
/* #[derive(Default)]
#[derive(Default)]
pub struct ExtensionState {
pub extensions: Mutex<std::collections::HashMap<String, ExtensionManifest>>,
}
@ -38,7 +38,7 @@ impl ExtensionState {
let extensions = self.extensions.lock().unwrap();
extensions.values().find(|p| p.name == addon_id).cloned()
}
} */
}
#[derive(Debug, Serialize, Deserialize)]
pub struct DbExtensionPermission {

View File

@ -5,7 +5,7 @@
"identifier": "space.haex.hub",
"build": {
"beforeDevCommand": "pnpm dev",
"devUrl": "http://localhost:3001",
"devUrl": "http://localhost:3003",
"beforeBuildCommand": "pnpm generate",
"frontendDist": "../dist"
},

View File

@ -3,45 +3,22 @@
class="flex shrink-0 transition-[width] ease-in duration-300 z-30 h-full overflow-hidden fixed sm:relative left-0 shadow border-r border-base-300"
>
<div class="sm:flex flex-col w-14 bg-base-200 shrink-0 h-full hidden">
<img
src="/logo.svg"
class="bg-primary p-3 size-16"
/>
<img src="/logo.svg" class="bg-primary p-3 size-16" />
<div class="flex flex-col justify-between h-full overflow-y-scroll z-10">
<div class="flex flex-col space-y-2 text-base-content/90">
<template v-for="item in menu.top">
<UiSidebarLink
v-if="item.to"
:to="item.to ?? ''"
:icon="item.icon"
:label="$t(item.label)"
/>
<UiSidebarLink v-if="item.to" v-bind="item" />
<UiSidebarButton
v-else
:icon="item.icon"
:label="$t(item.label)"
@click="item.click"
/>
<UiSidebarButton v-else :icon="item.icon" :label="$t(item.label)" @click="item.click" />
</template>
</div>
<div class="flex flex-col space-y-2 text-base-content/90">
<template v-for="item in menu.bottom">
<UiSidebarLink
v-if="item.to"
:to="item.to ?? ''"
:icon="item.icon"
:label="$t(item.label)"
/>
<UiSidebarLink v-if="item.to" v-bind="item" />
<UiSidebarButton
v-else
:icon="item.icon"
:label="$t(item.label)"
@click="item.click"
/>
<UiSidebarButton v-else :icon="item.icon" :label="$t(item.label)" @click="item.click" />
</template>
<!-- <UiSidebarLink
v-for="item in menu.bottom"

View File

@ -1,50 +1,37 @@
<template>
<li
@click="triggerNavigate"
class="hover:text-primary"
>
<UiTooltip
:tooltip="tooltip ?? name"
direction="right-end"
>
<NuxtLinkLocale
class="hover:text-primary rounded"
:class="{ ['bg-base-300']: isActive }"
:to="{
name: type === 'browser' ? 'haexBrowser' : 'haexExtension',
params: type === 'browser' ? {} : { extensionId: id },
}"
>
<UiTooltip :tooltip="tooltip ?? name" direction="right-start">
<NuxtLinkLocale
:to
class="flex items-center justify-center cursor-pointer tooltip-toogle"
ref="link"
>
<Icon
:name="icon"
class="shrink-0 size-6"
/>
<Icon :name="icon" class="shrink-0 size-6" />
</NuxtLinkLocale>
</UiTooltip>
</li>
</template>
<script setup lang="ts">
import { type ISidebarItem } from '#imports';
import { type ISidebarItem } from "#imports";
const props = defineProps<ISidebarItem>();
const router = useRouter();
const isActive = computed(() => {
if (props.type === 'browser') {
return router.currentRoute.value.name === 'haexBrowser';
} else if (props.type === 'extension') {
return (
router.currentRoute.value.name === 'haexExtension' &&
getSingleRouteParam(router.currentRoute.value.params.extensionId) ===
props.id
);
if (props.to?.name === "haexExtension") {
return getSingleRouteParam(router.currentRoute.value.params.extensionId) === props.id;
} else {
return props.to?.name === router.currentRoute.value.meta.name;
}
});
const link = useTemplateRef('link');
const link = useTemplateRef("link");
const triggerNavigate = () => link.value?.$el.click();

View File

@ -1,22 +1,16 @@
<template>
<UiDialog
:title="t('title')"
v-model:open="open"
>
<UiDialog :title="t('title')" v-model:open="open">
<template #trigger="{ id }">
<button
class="btn btn-primary btn-outline shadow-md md:btn-lg shrink-0 flex-1 whitespace-nowrap flex-nowrap"
@click="open = true"
>
<Icon name="mdi:plus" />
{{ t('database.create') }}
{{ t("database.create") }}
</button>
</template>
<form
class="flex flex-col gap-4"
@submit="onCreateAsync"
>
<form class="flex flex-col gap-4" @submit="onCreateAsync">
<!-- @keyup.enter="onCreateAsync" -->
<UiInput
:check-input="check"
@ -37,27 +31,22 @@
</form>
<template #buttons>
<UiButton
class="btn-error"
@click="onClose"
>
{{ t('abort') }}
<UiButton class="btn-error" @click="onClose">
{{ t("abort") }}
</UiButton>
<UiButton
class="btn-primary"
@click="onCreateAsync"
>
{{ t('create') }}
<UiButton class="btn-primary" @click="onCreateAsync">
{{ t("create") }}
</UiButton>
</template>
</UiDialog>
</template>
<script setup lang="ts">
import { save } from '@tauri-apps/plugin-dialog';
import { useVaultStore } from '~/stores/vault';
import { vaultDatabaseSchema } from './schema';
import { save } from "@tauri-apps/plugin-dialog";
import { useVaultStore } from "~/stores/vault";
import { vaultDatabaseSchema } from "./schema";
import Database from "@tauri-apps/plugin-sql";
const check = ref(false);
const open = ref();
@ -68,63 +57,55 @@ const database = reactive<{
name: string;
password: string;
path: string | null;
type: 'password' | 'text';
type: "password" | "text";
}>({
name: '',
password: '',
path: '',
type: 'password',
name: "",
password: "",
path: "",
type: "password",
});
const initDatabase = () => {
database.name = t('database.name');
database.password = '';
database.path = '';
database.type = 'password';
database.name = t("database.name");
database.password = "";
database.path = "";
database.type = "password";
};
initDatabase();
const { add } = useSnackbar();
const { createAsync } = useVaultStore();
//const { show } = storeToRefs(useSidebarStore());
const onCreateAsync = async () => {
check.value = true;
const nameCheck = vaultDatabaseSchema.name.safeParse(database.name);
const passwordCheck = vaultDatabaseSchema.password.safeParse(
database.password
);
const passwordCheck = vaultDatabaseSchema.password.safeParse(database.password);
console.log(
'checks',
database.name,
nameCheck,
database.password,
passwordCheck
);
console.log("checks", database.name, nameCheck, database.password, passwordCheck);
if (!nameCheck.success || !passwordCheck.success) return;
open.value = false;
try {
database.path = await save({ defaultPath: `${database.name}.db` });
database.path = await save({
defaultPath: database.name.endsWith(".db") ? database.name : `${database.name}.db`,
});
console.log("data", database);
console.log('data', database);
if (database.path && database.password) {
const vaultId = await createAsync({
path: database.path,
password: database.password,
});
//show.value = true;
await navigateTo(
useLocaleRoute()({ name: 'vault', params: { vaultId } })
);
console.log("vaultId", vaultId);
await navigateTo(useLocaleRoute()({ name: "vaultOverview", params: { vaultId } }));
}
} catch (error) {
console.error(error);
add({ type: 'error', text: JSON.stringify(error) });
add({ type: "error", text: JSON.stringify(error) });
}
};

View File

@ -7,7 +7,7 @@
@click="onLoadDatabase"
>
<Icon name="mdi:folder-open-outline" />
{{ t('database.open') }}
{{ t("database.open") }}
</button>
</template>
@ -21,31 +21,24 @@
/>
<template #buttons>
<UiButton
class="btn-error"
@click="onClose"
>
{{ t('abort') }}
<UiButton class="btn-error" @click="onClose">
{{ t("abort") }}
</UiButton>
<UiButton
type="submit"
class="btn-primary"
@click="onOpenDatabase"
>
{{ t('open') }}
<UiButton type="submit" class="btn-primary" @click="onOpenDatabase">
{{ t("open") }}
</UiButton>
</template>
</UiDialog>
</template>
<script setup lang="ts">
import { open } from '@tauri-apps/plugin-dialog';
import { vaultDatabaseSchema } from './schema';
import { open } from "@tauri-apps/plugin-dialog";
import { vaultDatabaseSchema } from "./schema";
const { t } = useI18n();
const isOpen = defineModel('isOpen', { type: Boolean });
const isOpen = defineModel("isOpen", { type: Boolean });
const props = defineProps({
path: String,
@ -57,19 +50,19 @@ const database = reactive<{
name: string;
password: string;
path: string | null;
type: 'password' | 'text';
type: "password" | "text";
}>({
name: '',
password: '',
path: '',
type: 'password',
name: "",
password: "",
path: "",
type: "password",
});
const initDatabase = () => {
database.name = '';
database.password = '';
database.path = '';
database.type = 'password';
database.name = "";
database.password = "";
database.path = "";
database.type = "password";
};
initDatabase();
@ -78,7 +71,7 @@ const { add } = useSnackbar();
const handleError = (error: unknown) => {
isOpen.value = false;
add({ type: 'error', text: JSON.stringify(error) });
add({ type: "error", text: JSON.stringify(error) });
//console.error(error);
};
@ -92,8 +85,8 @@ const onLoadDatabase = async () => {
directory: false,
filters: [
{
name: 'HaexVault',
extensions: ['db'],
name: "HaexVault",
extensions: ["db"],
},
],
});
@ -112,16 +105,14 @@ const onOpenDatabase = async () => {
check.value = true;
const path = database.path || props.path;
const pathCheck = vaultDatabaseSchema.path.safeParse(path);
const passwordCheck = vaultDatabaseSchema.password.safeParse(
database.password
);
const passwordCheck = vaultDatabaseSchema.password.safeParse(database.password);
if (!pathCheck.success || !passwordCheck.success || !path) {
add({ type: 'error', text: 'params falsch' });
add({ type: "error", text: "params falsch" });
return;
}
//console.log('try to open', path);
console.log("try to open", path);
const vaultId = await openAsync({
path,
@ -129,23 +120,23 @@ const onOpenDatabase = async () => {
});
if (!vaultId) {
add({ type: 'error', text: 'Vault konnte nicht geöffnet werden' });
add({ type: "error", text: "Vault konnte nicht geöffnet werden" });
return;
}
onClose();
/* await navigateTo(
await navigateTo(
localePath({
name: 'vaultGroup',
name: "vaultOverview",
params: {
vaultId,
},
query: {
showSidebar: 'true',
showSidebar: "true",
},
})
); */
);
} catch (error) {
console.log(error);
handleError(error);

View File

@ -1,27 +1,34 @@
<template>
<div class="w-full h-full flex flex-col">
<nav
class="navbar bg-base-100 max-sm:rounded-box max-sm:shadow sm:border-b border-base-content/25 sm:z-20 relative"
class="navbar bg-base-100 max-sm:rounded-box max-sm:shadow sm:border-b border-base-content/25 sm:z-20 relative px-2"
>
<button
type="button"
class="btn btn-text max-sm:btn-square sm:hidden me-2"
class="btn btn-text btn-square me-2"
aria-haspopup="dialog"
aria-expanded="false"
aria-controls="sidebar"
data-overlay="#sidebar"
>
<Icon name="mage:dash-menu" size="28" />
</button>
<!-- <button
type="button"
class="btn btn-text max-sm:btn-square me-2"
aria-haspopup="dialog"
aria-expanded="false"
aria-controls="sidebar"
data-overlay="#sidebar"
>
<span class="icon-[tabler--menu-2] size-5"></span>
</button>
</button> -->
<div class="flex flex-1 items-center">
<a
class="link text-base-content link-neutral text-xl font-semibold no-underline"
href="#"
>
<a class="link text-base-content link-neutral text-xl font-semibold no-underline" href="#">
<UiTextGradient>Haex Hub</UiTextGradient>
</a>
</div>
<div class="navbar-end flex items-center gap-4">
<div class="navbar-end flex items-center gap-4 me-4">
<div
class="dropdown relative inline-flex [--auto-close:inside] [--offset:8] [--placement:bottom-end]"
>
@ -38,9 +45,7 @@
v-show="notifications.length"
class="indicator-item bg-error size-2 rounded-full text-sm"
></span>
<span
class="icon-[tabler--bell] text-base-content size-[1.375rem]"
></span>
<span class="icon-[tabler--bell] text-base-content size-[1.375rem]"></span>
</div>
</button>
<div
@ -51,16 +56,13 @@
>
<div class="dropdown-header justify-center">
<h6 class="text-base-content text-base">
{{ t('notifications.label') }}
{{ t("notifications.label") }}
</h6>
</div>
<div
class="vertical-scrollbar horizontal-scrollbar rounded-scrollbar text-base-content/80 max-h-56 overflow-auto max-md:max-w-60"
>
<div
class="dropdown-item"
v-for="notification in notifications"
>
<div class="dropdown-item" v-for="notification in notifications">
<div class="avatar">
<div class="w-10 rounded-full">
<img
@ -68,10 +70,7 @@
:src="notification.image"
:alt="notification.alt ?? 'notification avatar'"
/>
<Icon
v-else-if="notification.icon"
:name="notification.icon"
/>
<Icon v-else-if="notification.icon" :name="notification.icon" />
</div>
</div>
<div class="w-60">
@ -84,12 +83,9 @@
</div>
</div>
</div>
<a
href="#"
class="dropdown-footer justify-center gap-1"
>
<a href="#" class="dropdown-footer justify-center gap-1">
<span class="icon-[tabler--eye] size-4"></span>
{{ t('notifications.view_all') }}
{{ t("notifications.view_all") }}
</a>
</div>
</div>
@ -106,10 +102,7 @@
>
<div class="avatar">
<div class="size-9.5 rounded-full">
<img
src="https://cdn.flyonui.com/fy-assets/avatar/avatar-1.png"
alt="avatar 1"
/>
<img src="https://cdn.flyonui.com/fy-assets/avatar/avatar-1.png" alt="avatar 1" />
</div>
</div>
</button>
@ -122,62 +115,42 @@
<li class="dropdown-header gap-2">
<div class="avatar">
<div class="w-10 rounded-full">
<img
src="https://cdn.flyonui.com/fy-assets/avatar/avatar-1.png"
alt="avatar"
/>
<img src="https://cdn.flyonui.com/fy-assets/avatar/avatar-1.png" alt="avatar" />
</div>
</div>
<div>
<h6 class="text-base-content text-base font-semibold">
John Doe
</h6>
<h6 class="text-base-content text-base font-semibold">John Doe</h6>
<small class="text-base-content/50">Admin</small>
</div>
</li>
<li>
<a
class="dropdown-item"
href="#"
>
<a class="dropdown-item" href="#">
<span class="icon-[tabler--user]"></span>
My Profile
</a>
</li>
<li>
<a
class="dropdown-item"
href="#"
>
<a class="dropdown-item" href="#">
<span class="icon-[tabler--settings]"></span>
Settings
</a>
</li>
<li>
<a
class="dropdown-item"
href="#"
>
<a class="dropdown-item" href="#">
<span class="icon-[tabler--receipt-rupee]"></span>
Billing
</a>
</li>
<li>
<a
class="dropdown-item"
href="#"
>
<a class="dropdown-item" href="#">
<span class="icon-[tabler--help-triangle]"></span>
FAQs
</a>
</li>
<li class="dropdown-footer gap-2">
<button
class="btn btn-error btn-soft btn-block"
@click="onVaultCloseAsync"
>
<button class="btn btn-error btn-soft btn-block" @click="onVaultCloseAsync">
<span class="icon-[tabler--logout]"></span>
{{ t('vault.close') }}
{{ t("vault.close") }}
</button>
</li>
</ul>
@ -187,7 +160,7 @@
<aside
id="sidebar"
class="overlay sm:shadow-none overlay-open:translate-x-0 drawer drawer-start hidden sm:absolute max-w-14 sm:flex sm:translate-x-0 sm:pt-16 z-10"
class="overlay sm:shadow-none overlay-open:translate-x-0 drawer drawer-start hidden sm:absolute max-w-14 sm:flex sm:translate-x-0 sm:pt-12 z-10"
role="dialog"
tabindex="-1"
>
@ -198,11 +171,7 @@
> -->
<UiSidebarLink
v-bind="item"
v-for="item in menu"
:key="item.id"
/>
<UiSidebarLink v-bind="item" v-for="item in menu" :key="item.id" />
<!-- <UiTooltip
:tooltip="item.tooltip || item.name"
direction="right-start"
@ -232,7 +201,7 @@
<div class="overflow-hidden transition-all relative w-full">
<div class="h-full overflow-scroll sm:pl-14">
<NuxtPage />
<slot />
</div>
</div>
@ -249,10 +218,13 @@ const { notifications } = storeToRefs(useNotificationStore());
const { menu } = storeToRefs(useSidebarStore());
const { isActive } = useExtensionsStore();
const { closeAsync } = useVaultStore();
const onExtensionSelectAsync = async (id: string) => {};
const onVaultCloseAsync = async () => {};
const onVaultCloseAsync = async () => {
await closeAsync();
await navigateTo(useLocalePath()({ name: "vaultOpen" }));
};
</script>
<i18n lang="yaml">

View File

@ -1,17 +1,11 @@
<template>
<div class="items-center justify-center min-h-full flex w-full">
<div class="flex flex-col justify-center items-center gap-4 max-w-3xl">
<img
src="/logo.svg"
class="bg-primary p-3 size-16 rounded-full"
alt="HaexVault Logo"
/>
<img src="/logo.svg" class="bg-primary p-3 size-16 rounded-full" alt="HaexVault Logo" />
<span
class="flex flex-wrap font-bold text-pretty text-xl gap-2 justify-center"
>
<span class="flex flex-wrap font-bold text-pretty text-xl gap-2 justify-center">
<p class="whitespace-nowrap">
{{ t('welcome') }}
{{ t("welcome") }}
</p>
<UiTextGradient>Haex Hub</UiTextGradient>
</span>
@ -19,17 +13,14 @@
<div class="flex flex-col md:flex-row gap-4 w-full">
<VaultButtonCreate />
<!-- <VaultButtonOpen
v-model:isOpen="passwordPromptOpen"
:path="vaultPath"
/> -->
<NuxtLinkLocale
<VaultButtonOpen v-model:isOpen="passwordPromptOpen" :path="vaultPath" />
<!-- <NuxtLinkLocale
:to="{
name: 'haexBrowser',
params: { vaultId: 'test' },
}"
>test link</NuxtLinkLocale
>
> -->
<!-- <button @click="test">test</button>
<NuxtLinkLocale
:to="{ name: 'vaultGroup', params: { vaultId: 'test' } }"
@ -47,12 +38,9 @@
/> -->
</div>
<div
v-show="lastVaults.length"
class="w-full"
>
<div v-show="lastVaults.length" class="w-full">
<div class="font-thin text-sm justify-start px-2 pb-1">
{{ t('lastUsed') }}
{{ t("lastUsed") }}
</div>
<div
@ -80,17 +68,14 @@
<button
class="absolute right-2 btn btn-square btn-error btn-xs hidden group-hover:flex min-w-6"
>
<Icon
name="mdi:trash-can-outline"
@click="removeVaultAsync(vault.path)"
/>
<Icon name="mdi:trash-can-outline" @click="removeVaultAsync(vault.path)" />
</button>
</div>
</div>
</div>
<div class="flex flex-col items-center gap-2">
<h4>{{ t('sponsors') }}</h4>
<h4>{{ t("sponsors") }}</h4>
<div>
<button @click="openUrl('https://itemis.com')">
<UiLogoItemis class="text-[#00457C]" />
@ -102,13 +87,13 @@
</template>
<script setup lang="ts">
import { openUrl } from '@tauri-apps/plugin-opener';
import { openUrl } from "@tauri-apps/plugin-opener";
const passwordPromptOpen = ref(false);
const vaultPath = ref('');
const vaultPath = ref("");
definePageMeta({
name: 'vaultOpen',
name: "vaultOpen",
});
const { t } = useI18n();

View File

@ -1,7 +1,11 @@
<template>
<div>
<div class="text-white">
<NuxtLayout name="app">
<NuxtPage />
</NuxtLayout>
</div>
</template>
<script setup lang="ts">
const { createTable } = useVaultStore();
</script>

View File

@ -1,6 +1,6 @@
<template>
<div>
browser
browser {{ useRouter().currentRoute.value.meta.name }}
<HaexBrowser
:tabs="tabs"
:activeTabId="activeTabId"
@ -14,13 +14,13 @@
</template>
<script setup lang="ts">
import { invoke } from '@tauri-apps/api/core';
import { listen, type UnlistenFn } from '@tauri-apps/api/event';
import { Window, getCurrentWindow } from '@tauri-apps/api/window';
import { Webview } from '@tauri-apps/api/webview';
import { invoke } from "@tauri-apps/api/core";
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
import { Window, getCurrentWindow } from "@tauri-apps/api/window";
import { Webview } from "@tauri-apps/api/webview";
definePageMeta({
name: 'haexBrowser',
name: "haexBrowser",
});
interface Tab {
@ -40,10 +40,10 @@ let unlistenTabClosed: UnlistenFn | null = null;
onMounted(async () => {
// Erstelle einen ersten Tab beim Start
createNewTab('https://www.google.com');
//createNewTab("https://www.google.com");
// Höre auf Tab-Events
unlistenTabCreated = await listen('tab-created', (event) => {
unlistenTabCreated = await listen("tab-created", (event) => {
const newTab = event.payload as Tab;
tabs.value = tabs.value.map((tab) => ({
@ -58,7 +58,7 @@ onMounted(async () => {
activeTabId.value = newTab.id;
});
unlistenTabClosed = await listen('tab-closed', (event) => {
unlistenTabClosed = await listen("tab-closed", (event) => {
const closedTabId = event.payload as string;
tabs.value = tabs.value.filter((tab) => tab.id !== closedTabId);
});
@ -69,7 +69,7 @@ onUnmounted(() => {
if (unlistenTabClosed) unlistenTabClosed();
});
const createNewTab = async (url: string = 'about:blank') => {
const createNewTab = async (url: string = "about:blank") => {
try {
/* const appWindow = new Window('uniqueLabel111', {
fullscreen: true,
@ -86,10 +86,10 @@ const createNewTab = async (url: string = 'about:blank') => {
});
await webview.show(); */
//console.log('create webview', webview);
const tab_id = 'foo';
await invoke('create_tab', { url, tabId: 'foo' });
const tab_id = "foo";
await invoke("create_tab", { url, tabId: "foo" });
} catch (error) {
console.error('Fehler beim Erstellen des Tabs:', error);
console.error("Fehler beim Erstellen des Tabs:", error);
}
};
@ -97,7 +97,7 @@ const closeTab = async (tabId: string) => {
try {
//await invoke('close_tab', { tabId });
} catch (error) {
console.error('Fehler beim Schließen des Tabs:', error);
console.error("Fehler beim Schließen des Tabs:", error);
}
};
@ -105,7 +105,7 @@ const navigateToUrl = async (tabId: string, url: string) => {
try {
//await invoke('navigate_to_url', { tabId, url });
} catch (error) {
console.error('Fehler bei der Navigation:', error);
console.error("Fehler bei der Navigation:", error);
}
};
@ -113,7 +113,7 @@ const goBack = async (tabId: string | null) => {
try {
//await invoke('go_back', { tabId });
} catch (error) {
console.error('Fehler beim Zurückgehen:', error);
console.error("Fehler beim Zurückgehen:", error);
}
};
@ -121,7 +121,7 @@ const goForward = async (tabId: string | null) => {
try {
//await invoke('go_forward', { tabId });
} catch (error) {
console.error('Fehler beim Vorwärtsgehen:', error);
console.error("Fehler beim Vorwärtsgehen:", error);
}
};
</script>

View File

@ -1,13 +1,22 @@
<template>
<div>
hier kommt die erweiterung
{{ useRouter().currentRoute.value.params.extensionId }}
<iframe></iframe>
<div class="w-full h-full">
<iframe class="w-full h-full" @load="" ref="iFrameRef"> </iframe>
<p>{{ t("loading") }}</p>
</div>
</template>
<script setup lang="ts">
definePageMeta({
name: 'haexExtension',
name: "haexExtension",
});
const iframeRef = useTemplateRef("iFrameRef");
const { t } = useI18n();
</script>
<i18n lang="yaml">
de:
loading: Erweiterung wird geladen
en:
loading: Extension is loading
</i18n>

View File

@ -1,9 +1,9 @@
<template>
<div>ad extension</div>
<div>add extension</div>
</template>
<script setup lang="ts">
definePageMeta({
name: 'extensionAdd',
name: "haexExtensionAdd",
});
</script>

View File

@ -1,9 +1,9 @@
<template>
<div>vault</div>
<div class="h-screen bg-blue-200"></div>
</template>
<script setup lang="ts">
definePageMeta({
name: 'vaultOverview',
name: "vaultOverview",
});
</script>

View File

@ -1,57 +1,221 @@
import { getSingleRouteParam } from '~/composables/helper';
import type { RouteLocationRaw } from 'vue-router';
import { invoke } from "@tauri-apps/api/core";
import { join, resourceDir } from "@tauri-apps/api/path";
import { readTextFile, readDir } from "@tauri-apps/plugin-fs";
import { convertFileSrc } from "@tauri-apps/api/core";
export interface IExtensionLink {
const manifestFileName = "manifest.json";
export interface IHaexHubExtensionLink {
name: string;
icon: string;
tooltip?: string;
id: string;
}
export const useExtensionsStore = defineStore('extensionsStore', () => {
const extensions = ref<IExtensionLink[]>([
export interface IHaexHubExtensionManifest {
name: string;
entry: string;
permissions: {
database?: {
read?: string[];
write?: string[];
create?: string[];
};
http?: string[];
filesystem?: {
read?: string[];
write?: string[];
};
};
}
export const useExtensionsStore = defineStore("extensionsStore", () => {
const availableExtensions = ref<IHaexHubExtensionLink[]>([
{
id: 'haex-browser',
name: 'Haex Browser',
icon: 'solar:global-outline',
id: "haex-browser",
name: "Haex Browser",
icon: "solar:global-outline",
},
{
id: 'extensions',
name: 'sidebar.extensions',
icon: 'gg:extension',
id: "extensions",
name: "sidebar.extensions",
icon: "gg:extension",
},
{
id: 'settings',
name: 'sidebar.settings',
icon: 'ph:gear-six',
id: "settings",
name: "sidebar.settings",
icon: "ph:gear-six",
},
]);
const currentRoute = useRouter().currentRoute.value;
const currentRoute = useRouter().currentRoute;
const isActive = (id: string) =>
computed(
() =>
currentRoute.name === 'extension' &&
currentRoute.params.extensionId === id
currentRoute.value.name === "extension" &&
currentRoute.value.params.extensionId === id
);
const loadAsync = async (id: string) => {
extensions.value.some(async (extension) => {
if (extension.id === id) {
await navigateTo(
useLocalePath()({ name: 'extension', params: { extensionId: id } })
const currentExtension = computed(() => {
if (currentRoute.value.name !== "haexExtension") return;
const extensionId = getSingleRouteParam(
currentRoute.value.params.extensionId
);
if (!extensionId) return;
return availableExtensions.value.find(
(extension) => extension.id === extensionId
);
} else {
}
});
const checkExtensionDirectoryAsync = async (extensionDirectory: string) => {
try {
const dir = await readDir(extensionDirectory);
const manifest = dir.find(
(entry) => entry.name === manifestFileName && entry.isFile
);
if (!manifest) throw new Error("Kein Manifest für Erweiterung gefunden");
return true;
} catch (error) {
throw new Error(
`Keine Leseberechtigung für Ordner ${extensionDirectory}`
);
}
};
const installAsync = async (
extensionDirectory: string | null,
global: boolean = true
): Promise<void> => {
try {
if (!extensionDirectory)
throw new Error("Kein Ordner für Erweiterung angegeben");
const checkDirectory = await checkExtensionDirectoryAsync(
extensionDirectory
);
const manifestPath = await join(extensionDirectory, "manifest.json");
const manifest = await JSON.parse(await readTextFile(manifestPath));
console.log("manifest", manifest);
return;
} catch (error) {
throw error;
/*
const resourcePath = await resourceDir();
//const manifestPath = await join(extensionDirectory, 'manifest.json');
const manifestPath = await join(
resourcePath,
'extension',
'demo-addon',
'manifest.json'
);
const regex = /((href|src)=["'])([^"']+)(["'])/g;
let htmlContent = await readTextFile(
await join(resourcePath, 'extension', 'demo-addon', 'index.html')
);
const replacements = [];
let match;
while ((match = regex.exec(htmlContent)) !== null) {
const [fullMatch, prefix, attr, resource, suffix] = match;
if (!resource.startsWith('http')) {
replacements.push({ match: fullMatch, resource, prefix, suffix });
}
}
for (const { match, resource, prefix, suffix } of replacements) {
const fileContent = await readTextFile(
await join(resourcePath, 'extension', 'demo-addon', resource)
);
const blob = new Blob([fileContent], { type: getMimeType(resource) });
const blobUrl = URL.createObjectURL(blob);
console.log('blob', resource, blobUrl);
htmlContent = htmlContent.replace(
match,
`${prefix}${blobUrl}${suffix}`
);
}
console.log('htmlContent', htmlContent);
const blob = new Blob([htmlContent], { type: 'text/html' });
const iframeSrc = URL.createObjectURL(blob);
const manifestContent = await readTextFile(manifestPath);
console.log('iframeSrc', iframeSrc);
const manifest: PluginManifest = JSON.parse(manifestContent);
//const entryPath = await join(extensionDirectory, manifest.entry);
const entryPath = await join(
resourcePath,
'extension',
'demo-addon',
manifest.entry
);
console.log('extensionDirectory', extensionDirectory, entryPath);
const path = convertFileSrc(extensionDirectory, manifest.entry);
console.log('final path', path);
manifest.entry = iframeSrc;
/* await join(
path, //`file:/${extensionDirectory}`,
manifest.entry
); */
// Modul-Datei laden
//const modulePathFull = await join(basePath, manifest.main);
/* const manifest: PluginManifest = await invoke('load_plugin', {
manifestPath,
}); */
/* const iframe = document.createElement('iframe');
iframe.src = manifest.entry;
iframe.setAttribute('sandbox', 'allow-scripts');
iframe.style.width = '100%';
iframe.style.height = '100%';
iframe.style.border = 'none'; */
/* const addonApi = {
db_execute: async (sql: string, params: string[] = []) => {
return invoke('db_execute', {
addonId: manifest.name,
sql,
params,
});
},
db_select: async (sql: string, params: string[] = []) => {
return invoke('db_select', {
addonId: manifest.name,
sql,
params,
});
},
}; */
/* iframe.onload = () => {
iframe.contentWindow?.postMessage(
{ type: 'init', payload: addonApi },
'*'
);
};
window.addEventListener('message', (event) => {
if (event.source === iframe.contentWindow) {
const { type } = event.data;
if (type === 'ready') {
console.log(`Plugin ${manifest.name} ist bereit`);
}
}
}); */
/* plugins.value.push({ name: manifest.name, entry: manifest.entry });
console.log(`Plugin ${manifest.name} geladen.`); */
}
};
return {
extensions,
loadAsync,
availableExtensions,
currentExtension,
isActive,
};
});

View File

@ -1,28 +1,28 @@
import { getSingleRouteParam } from '~/composables/helper';
import type { RouteLocationRaw } from 'vue-router';
import { getSingleRouteParam } from "~/composables/helper";
import type { RouteLocationRaw, RouteLocationAsRelativeGeneric } from "vue-router";
export interface ISidebarItem {
name: string;
icon: string;
tooltip?: string;
id: string;
type: 'browser' | 'extension';
to?: RouteLocationAsRelativeGeneric;
}
export const useSidebarStore = defineStore('sidebarStore', () => {
export const useSidebarStore = defineStore("sidebarStore", () => {
const menu = ref<ISidebarItem[]>([
{
id: 'haex-browser',
name: 'Haex Browser',
icon: 'solar:global-outline',
type: 'browser',
id: "haex-browser",
name: "Haex Browser",
icon: "solar:global-outline",
to: { name: "haexBrowser" },
},
{
id: 'haex-vault',
name: 'Haex Vault',
icon: 'gg:extension',
type: 'extension',
id: "haex-extensions-add",
name: "Haex Extensions",
icon: "gg:extension",
to: { name: "haexExtensionAdd" },
},
]);

View File

@ -1,11 +1,11 @@
//import Database from '@tauri-apps/plugin-sql';
import { drizzle, SqliteRemoteDatabase } from 'drizzle-orm/sqlite-proxy';
import { drizzle, SqliteRemoteDatabase } from "drizzle-orm/sqlite-proxy";
//import Database from "tauri-plugin-sql-api";
import * as schema from '@/../src-tauri/database/schemas/vault';
import * as schema from "@/../src-tauri/database/schemas/vault";
import { invoke } from '@tauri-apps/api/core';
import { count } from 'drizzle-orm';
import { platform } from '@tauri-apps/plugin-os';
import { invoke } from "@tauri-apps/api/core";
import { count } from "drizzle-orm";
import { platform } from "@tauri-apps/plugin-os";
interface IVault {
//database: Database;
@ -18,24 +18,19 @@ interface IOpenVaults {
[vaultPath: string]: IVault;
}
export const useVaultStore = defineStore('vaultStore', () => {
export const useVaultStore = defineStore("vaultStore", () => {
const currentVaultId = computed<string | undefined>({
get: () =>
getSingleRouteParam(useRouter().currentRoute.value.params.vaultId),
get: () => getSingleRouteParam(useRouter().currentRoute.value.params.vaultId),
set: (newVaultId) => {
useRouter().currentRoute.value.params.vaultId = newVaultId ?? '';
useRouter().currentRoute.value.params.vaultId = newVaultId ?? "";
},
});
const read_only = computed<boolean>({
get: () => {
console.log(
'query showSidebar',
useRouter().currentRoute.value.query.readonly
);
console.log("query showSidebar", useRouter().currentRoute.value.query.readonly);
return JSON.parse(
getSingleRouteParam(useRouter().currentRoute.value.query.readonly) ||
'false'
getSingleRouteParam(useRouter().currentRoute.value.query.readonly) || "false"
);
},
set: (readonly) => {
@ -53,51 +48,32 @@ export const useVaultStore = defineStore('vaultStore', () => {
const currentVault = ref<IVault | undefined>();
/* computed(() => {
console.log('compute currentVault', currentVaultId.value, openVaults.value);
return openVaults.value?.[currentVaultId.value ?? ''];
}); */
watch(
currentVaultId,
async () => {
/* if (!openVaults.value?.[currentVaultId.value ?? '']) {
console.log(
'no vaultId',
currentVault.value,
openVaults.value?.[currentVaultId.value ?? '']
);
return await navigateTo(useLocalePath()({ name: 'vaultOpen' }));
} else */
currentVault.value = openVaults.value?.[currentVaultId.value ?? ''];
currentVault.value = openVaults.value?.[currentVaultId.value ?? ""];
},
{ immediate: true }
);
const openAsync = async ({
path = '',
password,
}: {
path: string;
password: string;
}) => {
//const sqlitePath = path?.startsWith('sqlite:') ? path : `sqlite:${path}`;
const openAsync = async ({ path = "", password }: { path: string; password: string }) => {
const sqlitePath = path?.startsWith("sqlite:") ? path : `sqlite:${path}`;
console.log('try to open db', path, password);
console.log("try to open db", path, password);
const result = await invoke<string>('open_encrypted_database', {
const result = await invoke<string>("open_encrypted_database", {
path,
key: password,
});
console.log('open vault from store', result);
if (!(await testDatabaseReadAsync())) throw new Error('Passwort falsch');
console.log("open vault from store", result);
if (!(await testDatabaseReadAsync())) throw new Error("Passwort falsch");
//const db = await Database.load(sqlitePath);
const vaultId = await getVaultIdAsync(path);
const seperator = platform() === 'windows' ? '\\' : '/';
const seperator = platform() === "windows" ? "\\" : "/";
const fileName = path.split(seperator).pop();
console.log('opened db fileName', fileName, vaultId);
console.log("opened db fileName", fileName, vaultId);
openVaults.value = {
...openVaults.value,
@ -113,14 +89,14 @@ export const useVaultStore = defineStore('vaultStore', () => {
// If the query is a SELECT, use the select method
if (isSelectQuery(sql)) {
rows = await invoke('db_select', { sql, params }).catch((e) => {
console.error('SQL Error:', e);
rows = await invoke("db_select", { sql, params }).catch((e) => {
console.error("SQL Error:", e);
return [];
});
} else {
// Otherwise, use the execute method
rows = await invoke('db_execute', { sql, params }).catch((e) => {
console.error('SQL Error:', e);
rows = await invoke("db_execute", { sql, params }).catch((e) => {
console.error("SQL Error:", e);
return [];
});
return { rows: [] };
@ -131,7 +107,7 @@ export const useVaultStore = defineStore('vaultStore', () => {
});
// If the method is "all", return all rows
results = method === 'all' ? rows : rows[0];
results = method === "all" ? rows : rows[0];
return { rows: results };
},
@ -147,11 +123,18 @@ export const useVaultStore = defineStore('vaultStore', () => {
return vaultId;
};
const createTable = () => {
console.log("ddd", schema.testTable.getSQL().queryChunks);
schema.testTable.getSQL().queryChunks.forEach((chunk) => {
const res = currentVault.value?.drizzle.run(chunk);
console.log("create table", res);
});
};
const testDatabaseReadAsync = async () => {
try {
currentVault.value?.drizzle
.select({ count: count() })
.from(schema.haexExtensions);
currentVault.value?.drizzle.select({ count: count() }).from(schema.haexExtensions);
return true;
} catch (error) {
return false;
@ -159,19 +142,13 @@ export const useVaultStore = defineStore('vaultStore', () => {
};
const refreshDatabaseAsync = async () => {
console.log('refreshDatabaseAsync');
console.log("refreshDatabaseAsync");
/* if (!currentVault.value?.database.close) {
return navigateTo(useLocaleRoute()({ name: 'vaultOpen' }));
} */
};
const createAsync = async ({
path,
password,
}: {
path: string;
password: string;
}) => {
const createAsync = async ({ path, password }: { path: string; password: string }) => {
/* const existDb = await exists('default.db', {
baseDir: BaseDirectory.Resource,
}); */
@ -179,12 +156,12 @@ export const useVaultStore = defineStore('vaultStore', () => {
/* const existDb = await resolveResource('resources/default.db');
if (!existDb) throw new Error('Keine Datenbank da');
await copyFile(existDb, path); */
const result = await invoke('create_encrypted_database', {
const result = await invoke("create_encrypted_database", {
path,
key: password,
});
console.log('create_encrypted_database', result);
return 'aaaaa'; //await openAsync({ path, password });
console.log("create_encrypted_database", result);
return await openAsync({ path, password });
};
const closeAsync = async () => {
@ -209,6 +186,7 @@ export const useVaultStore = defineStore('vaultStore', () => {
openVaults,
refreshDatabaseAsync,
read_only,
createTable,
};
});
@ -216,12 +194,10 @@ const getVaultIdAsync = async (path: string) => {
const encoder = new TextEncoder();
const data = encoder.encode(path);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
const hashHex = hashArray
.map((b) => b.toString(16).padStart(2, '0'))
.join(''); // convert bytes to hex string
console.log('vaultId', hashHex);
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join(""); // convert bytes to hex string
console.log("vaultId", hashHex);
return hashHex;
};

16
src/types/haexhub.d.ts vendored Normal file
View File

@ -0,0 +1,16 @@
export interface IHaexHubExtensionManifest {
name: string;
entry: string;
permissions: {
database?: {
read?: string[];
write?: string[];
create?: string[];
};
http?: string[];
filesystem?: {
read?: string[];
write?: string[];
};
};
}

View File

@ -40,13 +40,10 @@
const originalFetch = window.__TAURI__.http.fetch;
window.__TAURI__.http.fetch = async function (options) {
// Prüfe, ob die Ressource blockiert werden soll
const shouldBlock = await window.__TAURI__.invoke(
'block_resource_request',
{
const shouldBlock = await invoke('block_resource_request', {
url: options.url,
resourceType: 'tauri-fetch',
}
);
});
if (shouldBlock) {
throw new Error(`Ressourcenanfrage blockiert: ${options.url}`);