mobile menu

This commit is contained in:
2025-06-08 00:08:55 +02:00
parent 0f09bf8436
commit 18fee933ec
68 changed files with 4112 additions and 416 deletions

View File

@ -1,91 +0,0 @@
export interface ResourceRequestDetails {
url: string
resourceType: string
tabId?: string
frameId?: number
}
export interface ResourceRequestResult {
cancel: boolean
redirectUrl?: string
}
export interface ContentScript {
code: string
matches?: string[]
runAt?: 'document_start' | 'document_end' | 'document_idle'
}
export interface Extension {
id: string
name: string
version: string
description?: string
processNavigation?: (url: string) => boolean
processResourceRequest?: (
details: ResourceRequestDetails,
) => ResourceRequestResult
contentScripts?: ContentScript[]
}
export const useBrowserExtensionStore = defineStore(
'useBrowserExtensionStore',
() => {
const extensions = ref<Extension[]>([])
const isInitialized = ref<boolean>(false)
return {
extensions,
isInitialized,
initializeAsync,
processNavigation,
injectContentScripts,
}
},
)
const initializeAsync = async () => {
const { isInitialized } = storeToRefs(useBrowserExtensionStore())
return
if (isInitialized.value) return
// Lade Erweiterungen aus dem Erweiterungsverzeichnis
try {
const extensions = await loadExtensionsAsync()
for (const extension of extensions) {
registerExtension(extension)
}
isInitialized.value = true
console.log(`${extensions.length} Erweiterungen geladen`)
} catch (error) {
console.error('Fehler beim Laden der Erweiterungen:', error)
}
}
const loadExtensionsAsync = async (): Promise<Extension[]> => {
// In einer realen Implementierung würden Sie hier Erweiterungen aus einem Verzeichnis laden
// Für dieses Beispiel verwenden wir hartcodierte Erweiterungen
/* const adBlocker = (await import('./ad-blocker')).default;
const trackerBlocker = (await import('./tracker-blocker')).default; */
return []
}
const registerExtension = (extension: Extension): boolean => {
const { extensions } = storeToRefs(useBrowserExtensionStore())
if (!extension.id || !extension.name) {
console.error('Ungültige Erweiterung:', extension)
return false
}
console.log(`Erweiterung registriert: ${extension.name}`)
extensions.value.push(extension)
return true
}
const processNavigation = () => {
return true
}
const injectContentScripts = () => {}

View File

@ -0,0 +1,8 @@
{
"group": {
"create": "Gruppe erstellen"
},
"entry": {
"create": "Eintrag erstellen"
}
}

View File

@ -0,0 +1,8 @@
{
"group": {
"create": "Create Group"
},
"entry": {
"create": "Create Entry"
}
}

View File

@ -0,0 +1,44 @@
import type { IActionMenuItem } from '~/components/ui/button/types'
import de from './de.json'
import en from './en.json'
export const usePasswordsActionMenuStore = defineStore(
'passwordsActionMenuStore',
() => {
const { t } = useI18n({
messages: {
de: { passwordActionMenu: de },
en: { passwordActionMenu: en },
},
})
const menu = computed<IActionMenuItem[]>(() => [
{
label: t('passwordActionMenu.group.create'),
icon: 'mdi:folder-plus-outline',
to: {
name: 'passwordGroupCreate',
params: { groupId: usePasswordGroupStore().currentGroupId },
query: {
...useRouter().currentRoute.value.query,
},
},
},
{
label: t('passwordActionMenu.entry.create'),
icon: 'mdi:key-plus',
to: {
name: 'passwordItemCreate',
params: { groupId: usePasswordGroupStore().currentGroupId },
query: {
...useRouter().currentRoute.value.query,
},
},
},
])
return {
menu,
}
},
)

View File

@ -0,0 +1,188 @@
import { and, eq, isNull, sql, type SQLWrapper } from 'drizzle-orm'
import {
haexPasswordsGroupItems,
haexPasswordsGroups,
haexPasswordsItems,
type InsertHaexPasswordsGroups,
type InsertHaexPasswordsItems,
type SelectHaexPasswordsGroups,
type SelectHaexPasswordsItems,
} from '~~/src-tauri/database/schemas/vault'
export const usePasswordGroupStore = defineStore('passwordGroupStore', () => {
const groups = ref<SelectHaexPasswordsGroups[]>([])
const currentGroupId = computed<string | null>({
get: () =>
getSingleRouteParam(useRouter().currentRoute.value.params.groupId) ||
null,
set: (newGroupId) => {
console.log('set groupId', newGroupId)
useRouter().currentRoute.value.params.groupId = newGroupId ?? ''
},
})
const currentGroup = ref()
const currentGroupItems = reactive<{
items: SelectHaexPasswordsItems[]
groups: SelectHaexPasswordsGroups[]
}>({
items: [],
groups: [],
})
const syncGroupItemsAsync = async (currentGroupId: string | null) => {
const { addNotificationAsync } = useNotificationStore()
const { readByGroupIdAsync } = usePasswordItemStore()
/* const { currentGroup, groups, currentGroupItems } = storeToRefs(
usePasswordGroupStore(),
) */
groups.value = await readGroupsAsync()
currentGroup.value = groups.value?.find(
(group) => group.id === currentGroupId,
)
try {
currentGroupItems.groups =
(await getByParentIdAsync(currentGroupId)) ?? []
currentGroupItems.items = (await readByGroupIdAsync(currentGroupId)) ?? []
console.log('search current group', groups.value, currentGroup.value)
} catch (error) {
console.error(error)
currentGroupItems.groups = []
currentGroupItems.items = []
await addNotificationAsync({
type: 'log',
text: JSON.stringify(error),
})
}
}
watch(
currentGroupId,
async () => {
syncGroupItemsAsync(currentGroupId.value)
},
{ immediate: true },
)
return {
addGroupAsync,
currentGroup,
currentGroupId,
currentGroupItems,
groups,
navigateToGroupAsync,
navigateToGroupItemsAsync,
readGroupAsync,
readGroupItemsAsync,
readGroupsAsync,
updateAsync,
}
})
const addGroupAsync = async (group: Partial<InsertHaexPasswordsGroups>) => {
const { currentVault } = useVaultStore()
const newGroup: InsertHaexPasswordsGroups = {
id: crypto.randomUUID(),
parentId: group.parentId,
color: group.color,
icon: group.icon,
name: group.name,
order: group.order,
}
await currentVault.drizzle.insert(haexPasswordsGroups).values(newGroup)
return newGroup
}
const readGroupAsync = async (groupId: string) => {
const { currentVault } = useVaultStore()
return (
await currentVault.drizzle
.select()
.from(haexPasswordsGroups)
.where(eq(haexPasswordsGroups.id, groupId))
).at(0)
}
const readGroupsAsync = async (filter?: { parentId?: string | null }) => {
const { currentVault } = storeToRefs(useVaultStore())
if (filter?.parentId) {
return await currentVault.value.drizzle
.select()
.from(haexPasswordsGroups)
.where(eq(haexPasswordsGroups.id, filter.parentId))
} else {
return await currentVault.value.drizzle
.select()
.from(haexPasswordsGroups)
.where(isNull(haexPasswordsGroups.parentId))
.orderBy(sql`${haexPasswordsGroups.order} nulls last`)
}
}
const readGroupItemsAsync = async (id?: string | null) => {
const { currentVault } = useVaultStore()
currentVault.drizzle.select().from(haexPasswordsGroupItems)
}
const getByParentIdAsync = async (parentId?: string | null) => {
try {
const { currentVault } = useVaultStore()
if (parentId) {
const groups = await currentVault.drizzle
.select()
.from(haexPasswordsGroups)
.where(eq(haexPasswordsGroups.parentId, parentId))
.orderBy(sql`${haexPasswordsGroups.order} nulls last`)
console.log('found groups', groups)
return groups
} else {
const groups = await currentVault.drizzle
.select()
.from(haexPasswordsGroups)
.where(isNull(haexPasswordsGroups.parentId))
.orderBy(sql`${haexPasswordsGroups.order} nulls last`)
console.log('found groups', groups)
return groups
}
} catch (error) {
console.error(error)
}
}
const navigateToGroupAsync = (groupId?: string | null) =>
navigateTo(
useLocaleRoute()({
name: 'passwordGroupEdit',
params: {
vaultId: useRouter().currentRoute.value.params.vaultId,
groupId,
},
query: {
...useRouter().currentRoute.value.query,
},
}),
)
const updateAsync = async () => {}
const navigateToGroupItemsAsync = (groupId: string) => {
navigateTo(
useLocaleRoute()({
name: 'passwordGroupItems',
params: {
vaultId: useRouter().currentRoute.value.params.vaultId,
groupId,
},
query: {
...useRouter().currentRoute.value.query,
},
}),
)
}

View File

@ -0,0 +1,28 @@
import { eq } from 'drizzle-orm'
import { haexPasswordsItemHistory } from '~~/src-tauri/database/schemas/vault'
export const usePasswordHistoryStore = defineStore(
'passwordHistoryStore',
() => {
return { getAsync }
},
)
const getAsync = async (itemId: string | null) => {
if (!itemId) return null
try {
const { currentVault } = useVaultStore()
const history = await currentVault.drizzle
.select()
.from(haexPasswordsItemHistory)
.where(eq(haexPasswordsItemHistory.itemId, itemId))
console.log('found history ', history)
return history
} catch (error) {
console.error(error)
throw error
}
}

View File

@ -0,0 +1,120 @@
import { eq, isNull } from 'drizzle-orm'
import {
haexPasswordsGroupItems,
haexPasswordsGroups,
haexPasswordsItems,
type InsertHaexPasswordsItems,
type InsertHaexPasswordsItemsKeyValues,
} from '~~/src-tauri/database/schemas/vault'
export const usePasswordItemStore = defineStore('passwordItemStore', () => {
const currentItemId = computed({
get: () =>
getSingleRouteParam(useRouter().currentRoute.value.params.itemId),
set: (entryId) => {
console.log('set entryId', entryId)
useRouter().currentRoute.value.params.entryId = entryId ?? ''
},
})
return {
currentItemId,
addAsync,
readByGroupIdAsync,
readAsync,
readKeyValuesAsync,
}
})
const addAsync = async (
item: InsertHaexPasswordsItems,
keyValues: InsertHaexPasswordsItemsKeyValues,
) => {
const { currentVault } = useVaultStore()
/* const { currentGroupId } = useVaultGroupStore();
entry.id = crypto.randomUUID();
entry.createdAt = null;
entry.updateAt = null;
console.log('store create entry', entry, currentGroupId);
await currentVault?.drizzle.transaction(async (tx) => {
await tx.insert(vaultEntry).values(entry);
await tx
.insert(vaultGroupEntry)
.values({ entryId: entry.id, groupId: currentGroupId });
});
return entry.id; */
}
const readByGroupIdAsync = async (groupId?: string | null) => {
try {
const { currentVault } = useVaultStore()
console.log('get entries by groupId', groupId || null)
if (groupId) {
const entries = await currentVault.drizzle
.select()
.from(haexPasswordsGroupItems)
.innerJoin(
haexPasswordsItems,
eq(haexPasswordsItems.id, haexPasswordsGroupItems.itemId),
)
.where(eq(haexPasswordsGroupItems.groupId, groupId))
console.log('found entries by groupId', entries)
return entries.map((entry) => entry.haex_passwords_items)
} else {
const entries = await currentVault.drizzle
.select()
.from(haexPasswordsGroupItems)
.innerJoin(
haexPasswordsItems,
eq(haexPasswordsItems.id, haexPasswordsGroupItems.itemId),
)
.where(isNull(haexPasswordsGroupItems.groupId))
console.log('found entries', entries)
return entries.map((entry) => entry.haex_passwords_items)
}
} catch (error) {
console.error(error)
return []
}
}
const readAsync = async (itemId: string | null) => {
if (!itemId) return null
try {
const { currentVault } = useVaultStore()
const details =
await currentVault.drizzle.query.haexPasswordsItems.findFirst({
where: eq(haexPasswordsItems.id, itemId),
})
if (!details) return {}
const history = (await usePasswordHistoryStore().getAsync(itemId)) ?? []
const keyValues = (await readKeyValuesAsync(itemId)) ?? []
console.log('found item by id', { details, history, keyValues })
return { details, history, keyValues }
} catch (error) {
console.error(error)
throw error
}
}
const readKeyValuesAsync = async (itemId: string | null) => {
if (!itemId) return null
const { currentVault } = useVaultStore()
const keyValues =
await currentVault.drizzle.query.haexPasswordsItemsKeyValues.findMany({
where: eq(haexPasswordsItems.id, itemId),
})
return keyValues
}

View File

@ -17,7 +17,7 @@ export const useSidebarStore = defineStore('sidebarStore', () => {
id: 'haex-pass',
name: 'HaexPass',
icon: 'mdi:safe',
to: { name: 'haexpassOverview' },
to: { name: 'passwords' },
},
{
id: 'haex-extensions',

View File

@ -93,6 +93,7 @@ export const useVaultStore = defineStore('vaultStore', () => {
})
console.log('select', rows)
} else {
console.log('sql_execute', sql, params)
// Otherwise, use the execute method
rows = await invoke<unknown[]>('sql_execute', {
sql,

View File

@ -1,10 +1,9 @@
import { eq } from 'drizzle-orm'
import { and, eq, or, type SQLWrapper } from 'drizzle-orm'
import {
haexNotifications,
type InsertHaexNotifications,
} from '~~/src-tauri/database/schemas/vault'
import {
channels,
isPermissionGranted,
requestPermission,
sendNotification,
@ -18,7 +17,7 @@ export interface IHaexNotification {
image?: string | null
alt?: string | null
date: string | null
type?: 'error' | 'success' | 'warning' | 'info' | null
type?: 'error' | 'success' | 'warning' | 'info' | 'log' | null
}
export const useNotificationStore = defineStore('notificationStore', () => {
@ -29,15 +28,8 @@ export const useNotificationStore = defineStore('notificationStore', () => {
const permission = await requestPermission()
console.log('got permission', permission)
isNotificationAllowed.value = permission === 'granted'
sendNotification({
title: 'Tauri',
body: 'Tauri is awesome!',
icon: 'dialog-information',
})
/* const existingChannels = await channels()
console.log('existingChannels', existingChannels) */
}
const test = async () => console.log('test')
const checkNotificationAsync = async () => {
isNotificationAllowed.value = await isPermissionGranted()
return isNotificationAllowed.value
@ -45,13 +37,24 @@ export const useNotificationStore = defineStore('notificationStore', () => {
const notifications = ref<IHaexNotification[]>([])
const readNotificationsAsync = async (read: boolean = false) => {
const readNotificationsAsync = async (filter?: SQLWrapper[]) => {
const { currentVault } = storeToRefs(useVaultStore())
notifications.value = await currentVault.value.drizzle
.select()
.from(haexNotifications)
.where(eq(haexNotifications.read, read))
console.log('readNotificationsAsync', notifications.value)
console.log('readNotificationsAsync', filter)
if (filter) {
return await currentVault.value.drizzle
.select()
.from(haexNotifications)
.where(and(...filter))
} else {
return await currentVault.value.drizzle.select().from(haexNotifications)
}
}
const syncNotificationsAsync = async () => {
notifications.value = await readNotificationsAsync([
eq(haexNotifications.read, false),
])
}
const addNotificationAsync = async (
@ -61,21 +64,22 @@ export const useNotificationStore = defineStore('notificationStore', () => {
try {
const _notification: InsertHaexNotifications = {
id: crypto.randomUUID(),
type: notification.type || 'info',
alt: notification.alt,
date: new Date().toUTCString(),
date: notification.date || new Date().toUTCString(),
icon: notification.icon,
image: notification.image,
read: notification.read || false,
text: notification.text ?? '',
title: notification.title ?? '',
source: notification.source,
text: notification.text,
title: notification.title,
type: notification.type || 'info',
}
await currentVault.value.drizzle
.insert(haexNotifications)
.values(_notification)
await readNotificationsAsync()
await syncNotificationsAsync()
if (!isNotificationAllowed.value) {
const permission = await requestPermission()
@ -88,16 +92,29 @@ export const useNotificationStore = defineStore('notificationStore', () => {
body: _notification.text!,
})
}
} catch (error) {}
} catch (error) {
console.error(error)
}
}
const deleteNotificationsAsync = async (notificationIds: string[]) => {
const { currentVault } = storeToRefs(useVaultStore())
const filter = notificationIds.map((id) => eq(haexNotifications.id, id))
console.log('deleteNotificationsAsync', notificationIds)
return currentVault.value.drizzle
.delete(haexNotifications)
.where(or(...filter))
}
return {
notifications,
isNotificationAllowed,
checkNotificationAsync,
addNotificationAsync,
checkNotificationAsync,
deleteNotificationsAsync,
isNotificationAllowed,
notifications,
readNotificationsAsync,
requestNotificationPermissionAsync,
test,
syncNotificationsAsync,
}
})