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

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 } })
);
} else {
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
);
});
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,32 +1,32 @@
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';
name: string;
icon: string;
tooltip?: string;
id: string;
to?: RouteLocationAsRelativeGeneric;
}
export const useSidebarStore = defineStore('sidebarStore', () => {
const menu = ref<ISidebarItem[]>([
{
id: 'haex-browser',
name: 'Haex Browser',
icon: 'solar:global-outline',
type: 'browser',
},
export const useSidebarStore = defineStore("sidebarStore", () => {
const menu = ref<ISidebarItem[]>([
{
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" },
},
]);
/* const loadAsync = async (id: string) => {
/* const loadAsync = async (id: string) => {
extensions.value.some(async (extension) => {
if (extension.id === id) {
await navigateTo(
@ -37,8 +37,8 @@ export const useSidebarStore = defineStore('sidebarStore', () => {
});
}; */
return {
menu,
//loadAsync,
};
return {
menu,
//loadAsync,
};
});

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;
};