mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-17 06:30:50 +01:00
init commit
This commit is contained in:
90
src/stores/browser/extensions.ts
Normal file
90
src/stores/browser/extensions.ts
Normal 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) => {};
|
||||
0
src/stores/browser/index.ts
Normal file
0
src/stores/browser/index.ts
Normal file
57
src/stores/extensions/index.ts
Normal file
57
src/stores/extensions/index.ts
Normal 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
5
src/stores/ui/de.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"light": "Hell",
|
||||
"dark": "Dunkel",
|
||||
"soft": "Soft"
|
||||
}
|
||||
5
src/stores/ui/en.json
Normal file
5
src/stores/ui/en.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"light": "Light",
|
||||
"dark": "Dark",
|
||||
"soft": "Soft"
|
||||
}
|
||||
41
src/stores/ui/index.ts
Normal file
41
src/stores/ui/index.ts
Normal 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,
|
||||
};
|
||||
});
|
||||
22
src/stores/ui/notifications.ts
Normal file
22
src/stores/ui/notifications.ts
Normal 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
44
src/stores/ui/sidebar.ts
Normal 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
231
src/stores/vault/index.ts
Normal 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);
|
||||
}
|
||||
80
src/stores/vault/lastVaults.ts
Normal file
80
src/stores/vault/lastVaults.ts
Normal 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;
|
||||
};
|
||||
Reference in New Issue
Block a user