init commit

This commit is contained in:
Martin Drechsel
2025-04-02 18:54:55 +02:00
commit 2c5ec6b281
126 changed files with 21323 additions and 0 deletions

View File

@ -0,0 +1,90 @@
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());
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 = (url: string) => {
return true;
};
const injectContentScripts = (t: string) => {};

View File

View File

@ -0,0 +1,57 @@
import { getSingleRouteParam } from '~/composables/helper';
import type { RouteLocationRaw } from 'vue-router';
export interface IExtensionLink {
name: string;
icon: string;
tooltip?: string;
id: string;
}
export const useExtensionsStore = defineStore('extensionsStore', () => {
const extensions = ref<IExtensionLink[]>([
{
id: 'haex-browser',
name: 'Haex Browser',
icon: 'solar:global-outline',
},
{
id: 'extensions',
name: 'sidebar.extensions',
icon: 'gg:extension',
},
{
id: 'settings',
name: 'sidebar.settings',
icon: 'ph:gear-six',
},
]);
const currentRoute = useRouter().currentRoute.value;
const isActive = (id: string) =>
computed(
() =>
currentRoute.name === 'extension' &&
currentRoute.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 {
}
});
};
return {
extensions,
loadAsync,
isActive,
};
});

5
src/stores/ui/de.json Normal file
View File

@ -0,0 +1,5 @@
{
"light": "Hell",
"dark": "Dunkel",
"soft": "Soft"
}

5
src/stores/ui/en.json Normal file
View File

@ -0,0 +1,5 @@
{
"light": "Light",
"dark": "Dark",
"soft": "Soft"
}

41
src/stores/ui/index.ts Normal file
View File

@ -0,0 +1,41 @@
import { breakpointsTailwind, useBreakpoints } from '@vueuse/core';
import de from './de.json';
import en from './en.json';
export const useUiStore = defineStore('uiStore', () => {
const breakpoints = useBreakpoints(breakpointsTailwind);
const currentScreenSize = computed(() =>
breakpoints.active().value.length > 0 ? breakpoints.active().value : 'xs'
);
const { t } = useI18n({
messages: {
de: { ui: de },
en: { ui: en },
},
});
const availableThemes = ref([
{
value: 'dark',
name: t('ui.dark'),
icon: 'line-md:moon-rising-alt-loop',
},
{
value: 'light',
name: t('ui.light'),
icon: 'line-md:moon-to-sunny-outline-loop-transition',
},
{ value: 'soft', name: t('ui.soft'), icon: 'line-md:paint-drop' },
]);
const currentTheme = ref(availableThemes.value[0].value);
return {
breakpoints,
currentScreenSize,
currentTheme,
availableThemes,
};
});

View File

@ -0,0 +1,22 @@
export interface IHaexNotication {
title: string;
description?: string;
icon?: string;
image?: string;
alt?: string;
}
export const useNotificationStore = defineStore('notificationStore', () => {
const notifications = ref<IHaexNotication[]>([
{
title: 'huhu',
alt: 'test',
description: 'Ganz was tolles',
image: 'https://cdn.flyonui.com/fy-assets/avatar/avatar-1.png',
},
]);
return {
notifications,
};
});

44
src/stores/ui/sidebar.ts Normal file
View File

@ -0,0 +1,44 @@
import { getSingleRouteParam } from '~/composables/helper';
import type { RouteLocationRaw } from 'vue-router';
export interface ISidebarItem {
name: string;
icon: string;
tooltip?: string;
id: string;
type: 'browser' | 'extension';
}
export const useSidebarStore = defineStore('sidebarStore', () => {
const menu = ref<ISidebarItem[]>([
{
id: 'haex-browser',
name: 'Haex Browser',
icon: 'solar:global-outline',
type: 'browser',
},
{
id: 'haex-vault',
name: 'Haex Vault',
icon: 'gg:extension',
type: 'extension',
},
]);
/* const loadAsync = async (id: string) => {
extensions.value.some(async (extension) => {
if (extension.id === id) {
await navigateTo(
useLocalePath()({ name: 'extension', params: { extensionId: id } })
);
} else {
}
});
}; */
return {
menu,
//loadAsync,
};
});

231
src/stores/vault/index.ts Normal file
View File

@ -0,0 +1,231 @@
//import Database from '@tauri-apps/plugin-sql';
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 { invoke } from '@tauri-apps/api/core';
import { count } from 'drizzle-orm';
import { platform } from '@tauri-apps/plugin-os';
interface IVault {
//database: Database;
path: string;
password: string;
name: string;
drizzle: SqliteRemoteDatabase<typeof schema>;
}
interface IOpenVaults {
[vaultPath: string]: IVault;
}
export const useVaultStore = defineStore('vaultStore', () => {
const currentVaultId = computed<string | undefined>({
get: () =>
getSingleRouteParam(useRouter().currentRoute.value.params.vaultId),
set: (newVaultId) => {
useRouter().currentRoute.value.params.vaultId = newVaultId ?? '';
},
});
const read_only = computed<boolean>({
get: () => {
console.log(
'query showSidebar',
useRouter().currentRoute.value.query.readonly
);
return JSON.parse(
getSingleRouteParam(useRouter().currentRoute.value.query.readonly) ||
'false'
);
},
set: (readonly) => {
const router = useRouter();
router.replace({
query: {
...router.currentRoute.value.query,
readonly: JSON.stringify(readonly ? true : false),
},
});
},
});
const openVaults = ref<IOpenVaults | undefined>();
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 ?? ''];
},
{ immediate: true }
);
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);
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');
//const db = await Database.load(sqlitePath);
const vaultId = await getVaultIdAsync(path);
const seperator = platform() === 'windows' ? '\\' : '/';
const fileName = path.split(seperator).pop();
console.log('opened db fileName', fileName, vaultId);
openVaults.value = {
...openVaults.value,
[vaultId]: {
//database: db,
path,
password,
name: fileName ?? path,
drizzle: drizzle<typeof schema>(
async (sql, params, method) => {
let rows: any = [];
let results = [];
// 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);
return [];
});
} else {
// Otherwise, use the execute method
rows = await invoke('db_execute', { sql, params }).catch((e) => {
console.error('SQL Error:', e);
return [];
});
return { rows: [] };
}
rows = rows.map((row: any) => {
return Object.values(row);
});
// If the method is "all", return all rows
results = method === 'all' ? rows : rows[0];
return { rows: results };
},
// Pass the schema to the drizzle instance
{ schema: schema, logger: true }
),
},
};
const { addVaultAsync } = useLastVaultStore();
await addVaultAsync({ path });
return vaultId;
};
const testDatabaseReadAsync = async () => {
try {
currentVault.value?.drizzle
.select({ count: count() })
.from(schema.haexExtensions);
return true;
} catch (error) {
return false;
}
};
const refreshDatabaseAsync = async () => {
console.log('refreshDatabaseAsync');
/* if (!currentVault.value?.database.close) {
return navigateTo(useLocaleRoute()({ name: 'vaultOpen' }));
} */
};
const createAsync = async ({
path,
password,
}: {
path: string;
password: string;
}) => {
/* const existDb = await exists('default.db', {
baseDir: BaseDirectory.Resource,
}); */
/* 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', {
path,
key: password,
});
console.log('create_encrypted_database', result);
return 'aaaaa'; //await openAsync({ path, password });
};
const closeAsync = async () => {
if (!currentVaultId.value) return;
/* if (
typeof openVaults.value?.[currentVaultId.value]?.database?.close ===
'function'
) {
console.log('close db', openVaults.value?.[currentVaultId.value]);
return openVaults.value?.[currentVaultId.value]?.database?.close();
} */
delete openVaults.value?.[currentVaultId.value];
};
return {
closeAsync,
createAsync,
currentVault,
currentVaultId,
openAsync,
openVaults,
refreshDatabaseAsync,
read_only,
};
});
const getVaultIdAsync = async (path: string) => {
const encoder = new TextEncoder();
const data = encoder.encode(path);
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);
return hashHex;
};
function isSelectQuery(sql: string): boolean {
const selectRegex = /^\s*SELECT\b/i;
return selectRegex.test(sql);
}

View File

@ -0,0 +1,80 @@
import { load, Store } from '@tauri-apps/plugin-store';
interface ILastVault {
lastUsed: Date;
name: string;
path: string;
}
export const useLastVaultStore = defineStore('lastVaultStore', () => {
const {
public: { haexVault },
} = useRuntimeConfig();
const lastVaults = ref<ILastVault[]>([]);
const keyName = 'lastVaults';
const getStoreAsync = async () => {
return await load(haexVault.lastVaultFileName);
};
const syncLastVaultsAsync = async () => {
const store = await getStoreAsync();
lastVaults.value =
(await store.get<ILastVault[]>(keyName))?.sort(
(a, b) => +new Date(b.lastUsed) - +new Date(a.lastUsed)
) ?? [];
return lastVaults.value;
};
const addVaultAsync = async ({
name,
path,
}: {
name?: string;
path: string;
}) => {
if (!lastVaults.value) await syncLastVaultsAsync();
const saveName = name || getFileNameFromPath(path);
lastVaults.value = lastVaults.value.filter((vault) => vault.path !== path);
lastVaults.value.push({ lastUsed: new Date(), name: saveName, path });
await saveLastVaultsAsync();
};
const removeVaultAsync = async (vaultPath: string) => {
console.log('remove', vaultPath, lastVaults.value);
lastVaults.value = lastVaults.value.filter(
(vault) => vault.path !== vaultPath
);
await saveLastVaultsAsync();
};
const saveLastVaultsAsync = async () => {
const store = await getStoreAsync();
console.log('save lastVaults', keyName, lastVaults.value);
await store.set(keyName, lastVaults.value);
await syncLastVaultsAsync();
};
return {
addVaultAsync,
syncLastVaultsAsync,
lastVaults,
removeVaultAsync,
saveLastVaultsAsync,
};
});
const getFileNameFromPath = (path: string) => {
const lastBackslashIndex = path.lastIndexOf('\\');
const lastSlashIndex = path.lastIndexOf('/');
const lastIndex = Math.max(lastBackslashIndex, lastSlashIndex);
const fileName = path.substring(lastIndex + 1);
return fileName;
};