item handling

This commit is contained in:
2025-06-16 22:06:15 +02:00
parent 0b8f2c5532
commit 2972bb9e91
63 changed files with 3975 additions and 979 deletions

View File

@ -1,52 +1,78 @@
import { and, eq, isNull, sql, type SQLWrapper } from 'drizzle-orm'
import { eq, isNull, sql } from 'drizzle-orm'
import type { IPasswordMenuItem } from '~/components/haex/pass/mobile/menu/types'
import {
haexPasswordsGroupItems,
haexPasswordsGroups,
haexPasswordsItems,
type InsertHaexPasswordsGroups,
type InsertHaexPasswordsItems,
type SelectHaexPasswordsGroupItems,
type SelectHaexPasswordsGroups,
type SelectHaexPasswordsItems,
type SelectHaexPasswordsItemDetails,
} from '~~/src-tauri/database/schemas/vault'
export const trashId = 'trash'
export const usePasswordGroupStore = defineStore('passwordGroupStore', () => {
const groups = ref<SelectHaexPasswordsGroups[]>([])
const currentGroupId = computed<string | null>({
const currentGroupId = computed<string | null | undefined>({
get: () =>
getSingleRouteParam(useRouter().currentRoute.value.params.groupId) ||
null,
undefined,
set: (newGroupId) => {
console.log('set groupId', newGroupId)
useRouter().currentRoute.value.params.groupId = newGroupId ?? ''
},
})
const currentGroup = ref()
const currentGroup = computedAsync(() =>
currentGroupId.value ? readGroupAsync(currentGroupId.value) : null,
)
const currentGroupItems = reactive<{
items: SelectHaexPasswordsItems[]
items: SelectHaexPasswordsItemDetails[]
groups: SelectHaexPasswordsGroups[]
}>({
items: [],
groups: [],
})
const syncGroupItemsAsync = async (currentGroupId: string | null) => {
const selectedGroupItems = ref<IPasswordMenuItem[]>()
const breadCrumbs = computed(() => getParentChain(currentGroupId.value))
const getParentChain = (
groupId?: string | null,
chain: SelectHaexPasswordsGroups[] = [],
) => {
const group = groups.value.find((group) => group.id === groupId)
console.log('getParentChain1: found group', group, chain)
if (group) {
chain.push(group)
console.log('getParentChain: found group', group, chain)
return getParentChain(group.parentId, chain)
}
return chain.reverse()
}
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,
)
console.log(
'syncGroupItemsAsync',
groups.value,
currentGroup.value,
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 = []
@ -57,34 +83,45 @@ export const usePasswordGroupStore = defineStore('passwordGroupStore', () => {
})
}
}
watch(
currentGroupId,
async () => {
syncGroupItemsAsync(currentGroupId.value)
},
{ immediate: true },
watch(currentGroupId, () => syncGroupItemsAsync(currentGroupId.value), {
immediate: true,
})
const inTrashGroup = computed(() =>
breadCrumbs.value?.some((item) => item.id === trashId),
)
return {
addGroupAsync,
breadCrumbs,
createTrashIfNotExistsAsync,
currentGroup,
currentGroupId,
currentGroupItems,
deleteGroupAsync,
getChildGroupsRecursiveAsync,
groups,
inTrashGroup,
insertGroupItemsAsync,
navigateToGroupAsync,
navigateToGroupItemsAsync,
readGroupAsync,
readGroupItemsAsync,
readGroupsAsync,
selectedGroupItems,
syncGroupItemsAsync,
trashId,
updateAsync,
}
})
const addGroupAsync = async (group: Partial<InsertHaexPasswordsGroups>) => {
const { currentVault } = useVaultStore()
const { syncGroupItemsAsync } = usePasswordGroupStore()
const newGroup: InsertHaexPasswordsGroups = {
id: crypto.randomUUID(),
id: group.id || crypto.randomUUID(),
parentId: group.parentId,
color: group.color,
icon: group.icon,
@ -92,6 +129,7 @@ const addGroupAsync = async (group: Partial<InsertHaexPasswordsGroups>) => {
order: group.order,
}
await currentVault.drizzle.insert(haexPasswordsGroups).values(newGroup)
await syncGroupItemsAsync()
return newGroup
}
@ -117,21 +155,47 @@ const readGroupsAsync = async (filter?: { parentId?: string | null }) => {
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 readGroupItemsAsync = async (
groupId?: string | null,
): Promise<SelectHaexPasswordsGroupItems[]> => {
const { currentVault } = useVaultStore()
currentVault.drizzle.select().from(haexPasswordsGroupItems)
if (groupId) {
return currentVault.drizzle
.select()
.from(haexPasswordsGroupItems)
.where(eq(haexPasswordsGroupItems.groupId, groupId))
} else {
return currentVault.drizzle
.select()
.from(haexPasswordsGroupItems)
.where(isNull(haexPasswordsGroupItems.groupId))
}
}
const getByParentIdAsync = async (parentId?: string | null) => {
const getChildGroupsRecursiveAsync = async (
groupId: string,
groups: SelectHaexPasswordsGroups[] = [],
) => {
const childGroups = (await getByParentIdAsync(groupId)) ?? []
for (const child of childGroups) {
groups.push(...(await getChildGroupsRecursiveAsync(child.id)))
}
return groups
}
const getByParentIdAsync = async (
parentId?: string | null,
): Promise<SelectHaexPasswordsGroups[]> => {
try {
const { currentVault } = useVaultStore()
console.log('getByParentIdAsync', parentId)
if (parentId) {
const groups = await currentVault.drizzle
.select()
@ -139,7 +203,6 @@ const getByParentIdAsync = async (parentId?: string | null) => {
.where(eq(haexPasswordsGroups.parentId, parentId))
.orderBy(sql`${haexPasswordsGroups.order} nulls last`)
console.log('found groups', groups)
return groups
} else {
const groups = await currentVault.drizzle
@ -148,11 +211,11 @@ const getByParentIdAsync = async (parentId?: string | null) => {
.where(isNull(haexPasswordsGroups.parentId))
.orderBy(sql`${haexPasswordsGroups.order} nulls last`)
console.log('found groups', groups)
return groups
}
} catch (error) {
console.error(error)
return []
}
}
@ -170,14 +233,22 @@ const navigateToGroupAsync = (groupId?: string | null) =>
}),
)
const updateAsync = async () => {}
const updateAsync = async (group: InsertHaexPasswordsGroups) => {
console.log('updateAsync', group)
const { currentVault } = storeToRefs(useVaultStore())
if (!group.id) return
return currentVault.value.drizzle
.update(haexPasswordsGroups)
.set(group)
.where(eq(haexPasswordsGroups.id, group.id))
}
const navigateToGroupItemsAsync = (groupId: string) => {
navigateTo(
return navigateTo(
useLocaleRoute()({
name: 'passwordGroupItems',
params: {
vaultId: useRouter().currentRoute.value.params.vaultId,
groupId,
},
query: {
@ -186,3 +257,80 @@ const navigateToGroupItemsAsync = (groupId: string) => {
}),
)
}
const insertGroupItemsAsync = async (
items: IPasswordMenuItem[],
groupdId?: string | null,
) => {
const { currentVault } = useVaultStore()
const { groups } = usePasswordGroupStore()
const { syncGroupItemsAsync } = usePasswordGroupStore()
const targetGroup = groups.find((group) => group.id === groupdId)
console.log('insertGroupItemsAsync', items, targetGroup)
for (const item of items) {
if (item.type === 'group') {
const updateGroup = groups.find((group) => group.id === item.id)
if (updateGroup?.parentId === targetGroup?.id) return
if (updateGroup) {
updateGroup.parentId = targetGroup?.id ?? null
await currentVault.drizzle
.update(haexPasswordsGroups)
.set(updateGroup)
.where(eq(haexPasswordsGroups.id, updateGroup.id))
}
} else {
if (targetGroup)
await currentVault.drizzle
.update(haexPasswordsGroupItems)
.set({ groupId: targetGroup.id, itemId: item.id })
.where(eq(haexPasswordsGroupItems.itemId, item.id))
}
}
return syncGroupItemsAsync(targetGroup?.id)
}
const createTrashIfNotExistsAsync = async () => {
const exists = await readGroupAsync(trashId)
console.log('found trash', exists)
if (exists) return true
return addGroupAsync({
name: 'Trash',
id: trashId,
icon: 'mdi:trash-outline',
parentId: null,
})
}
const deleteGroupAsync = async (groupId: string, final: boolean = false) => {
const { currentVault } = useVaultStore()
const { readByGroupIdAsync, deleteAsync } = usePasswordItemStore()
console.log('deleteGroupAsync', groupId, final)
if (final || groupId === trashId) {
const childGroups = await getByParentIdAsync(groupId)
for (const child of childGroups) {
await deleteGroupAsync(child.id, true)
}
const items = await readByGroupIdAsync(groupId)
console.log('deleteGroupAsync delete Items', items)
for (const item of items) {
await deleteAsync(item.id, true)
}
return await currentVault.drizzle
.delete(haexPasswordsGroups)
.where(eq(haexPasswordsGroups.id, groupId))
} else {
if (await createTrashIfNotExistsAsync())
await updateAsync({ id: groupId, parentId: trashId })
}
}

View File

@ -1,10 +1,14 @@
import { eq, isNull } from 'drizzle-orm'
import {
haexPasswordsGroupItems,
haexPasswordsGroups,
haexPasswordsItems,
type InsertHaexPasswordsItems,
type InsertHaexPasswordsItemsKeyValues,
haexPasswordsItemDetails,
haexPasswordsItemHistory,
haexPasswordsItemKeyValues,
type InsertHaexPasswordsItemDetails,
type InserthaexPasswordsItemKeyValues,
type SelectHaexPasswordsGroups,
type SelectHaexPasswordsItemDetails,
type SelectHaexPasswordsItemKeyValues,
} from '~~/src-tauri/database/schemas/vault'
export const usePasswordItemStore = defineStore('passwordItemStore', () => {
@ -17,34 +21,114 @@ export const usePasswordItemStore = defineStore('passwordItemStore', () => {
},
})
const currentItem = computedAsync(
async () => await readAsync(currentItemId.value),
)
return {
currentItemId,
currentItem,
addAsync,
addKeyValueAsync,
addKeyValuesAsync,
deleteAsync,
deleteKeyValueAsync,
readByGroupIdAsync,
readAsync,
readKeyValuesAsync,
updateAsync,
}
})
const addAsync = async (
item: InsertHaexPasswordsItems,
keyValues: InsertHaexPasswordsItemsKeyValues,
details: SelectHaexPasswordsItemDetails,
keyValues: SelectHaexPasswordsItemKeyValues[],
group?: SelectHaexPasswordsGroups | null,
) => {
const { currentVault } = useVaultStore()
/* const { currentGroupId } = useVaultGroupStore();
console.log('addItem', details, group)
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 });
});
const newDetails: InsertHaexPasswordsItemDetails = {
id: crypto.randomUUID(),
icon: details.icon || group?.icon || null,
note: details.note,
password: details.password,
tags: details.tags,
title: details.title,
url: details.url,
username: details.username,
}
return entry.id; */
const newKeyValues: InserthaexPasswordsItemKeyValues[] = keyValues.map(
(keyValue) => ({
id: crypto.randomUUID(),
itemId: newDetails.id,
key: keyValue.key,
value: keyValue.value,
}),
)
try {
await currentVault?.drizzle.transaction(async (tx) => {
await tx.insert(haexPasswordsItemDetails).values(newDetails)
await tx
.insert(haexPasswordsGroupItems)
.values({ itemId: newDetails.id, groupId: group?.id ?? null })
if (newKeyValues.length)
await tx.insert(haexPasswordsItemKeyValues).values(newKeyValues)
})
} catch (error) {
console.error('ERROR addItem', error)
}
return newDetails.id
}
const addKeyValueAsync = async (
item?: InserthaexPasswordsItemKeyValues | null,
itemId?: string,
) => {
const newKeyValue: InserthaexPasswordsItemKeyValues = {
id: crypto.randomUUID(),
itemId: item?.itemId || itemId,
key: item?.key,
value: item?.value,
}
try {
const { currentVault } = useVaultStore()
return await currentVault?.drizzle
.insert(haexPasswordsItemKeyValues)
.values(newKeyValue)
} catch (error) {
console.error('ERROR addItem', error)
}
}
const addKeyValuesAsync = async (
items: InserthaexPasswordsItemKeyValues[],
itemId?: string,
) => {
const { currentVault } = useVaultStore()
console.log('addKeyValues', items, itemId)
const newKeyValues: InserthaexPasswordsItemKeyValues[] = items?.map(
(item) => ({
id: crypto.randomUUID(),
itemId: item.itemId || itemId,
key: item.key,
value: item.value,
}),
)
try {
return await currentVault?.drizzle
.insert(haexPasswordsItemKeyValues)
.values(newKeyValues)
} catch (error) {
console.error('ERROR addItem', error)
}
}
const readByGroupIdAsync = async (groupId?: string | null) => {
@ -58,25 +142,25 @@ const readByGroupIdAsync = async (groupId?: string | null) => {
.select()
.from(haexPasswordsGroupItems)
.innerJoin(
haexPasswordsItems,
eq(haexPasswordsItems.id, haexPasswordsGroupItems.itemId),
haexPasswordsItemDetails,
eq(haexPasswordsItemDetails.id, haexPasswordsGroupItems.itemId),
)
.where(eq(haexPasswordsGroupItems.groupId, groupId))
console.log('found entries by groupId', entries)
return entries.map((entry) => entry.haex_passwords_items)
return entries.map((entry) => entry.haex_passwords_item_details)
} else {
const entries = await currentVault.drizzle
.select()
.from(haexPasswordsGroupItems)
.innerJoin(
haexPasswordsItems,
eq(haexPasswordsItems.id, haexPasswordsGroupItems.itemId),
haexPasswordsItemDetails,
eq(haexPasswordsItemDetails.id, haexPasswordsGroupItems.itemId),
)
.where(isNull(haexPasswordsGroupItems.groupId))
console.log('found entries', entries)
return entries.map((entry) => entry.haex_passwords_items)
return entries.map((entry) => entry.haex_passwords_item_details)
}
} catch (error) {
console.error(error)
@ -91,11 +175,11 @@ const readAsync = async (itemId: string | null) => {
const { currentVault } = useVaultStore()
const details =
await currentVault.drizzle.query.haexPasswordsItems.findFirst({
where: eq(haexPasswordsItems.id, itemId),
await currentVault.drizzle.query.haexPasswordsItemDetails.findFirst({
where: eq(haexPasswordsItemDetails.id, itemId),
})
if (!details) return {}
if (!details) return null
const history = (await usePasswordHistoryStore().getAsync(itemId)) ?? []
const keyValues = (await readKeyValuesAsync(itemId)) ?? []
@ -113,8 +197,127 @@ const readKeyValuesAsync = async (itemId: string | null) => {
const { currentVault } = useVaultStore()
const keyValues =
await currentVault.drizzle.query.haexPasswordsItemsKeyValues.findMany({
where: eq(haexPasswordsItems.id, itemId),
await currentVault.drizzle.query.haexPasswordsItemKeyValues.findMany({
where: eq(haexPasswordsGroupItems.itemId, itemId),
})
return keyValues
}
const updateAsync = async ({
details,
keyValues,
keyValuesAdd,
keyValuesDelete,
groupId,
}: {
details: SelectHaexPasswordsItemDetails
keyValues: SelectHaexPasswordsItemKeyValues[]
keyValuesAdd: SelectHaexPasswordsItemKeyValues[]
keyValuesDelete: SelectHaexPasswordsItemKeyValues[]
groupId: string | null
}) => {
const { currentVault } = useVaultStore()
if (!details.id) return
const newDetails: InsertHaexPasswordsItemDetails = {
id: details.id,
icon: details.icon,
note: details.note,
password: details.password,
tags: details.tags,
title: details.title,
url: details.url,
username: details.username,
}
const newKeyValues: InserthaexPasswordsItemKeyValues[] = keyValues
.map((keyValue) => ({
id: keyValue.id,
itemId: newDetails.id,
key: keyValue.key,
value: keyValue.value,
}))
.filter((keyValue) => keyValue.id)
const newKeyValuesAdd: InserthaexPasswordsItemKeyValues[] = keyValuesAdd.map(
(keyValue) => ({
id: keyValue.id || crypto.randomUUID(),
itemId: newDetails.id,
key: keyValue.key,
value: keyValue.value,
}),
)
console.log('update item', newDetails, newKeyValues, newKeyValuesAdd, groupId)
return await currentVault?.drizzle.transaction(async (tx) => {
await tx
.update(haexPasswordsItemDetails)
.set(newDetails)
.where(eq(haexPasswordsItemDetails.id, newDetails.id))
await tx
.update(haexPasswordsGroupItems)
.set({ itemId: newDetails.id, groupId })
.where(eq(haexPasswordsGroupItems.itemId, newDetails.id))
const promises = newKeyValues.map((keyValue) =>
tx
.update(haexPasswordsItemKeyValues)
.set(keyValue)
.where(eq(haexPasswordsItemKeyValues.id, keyValue.id)),
)
await Promise.all(promises)
if (newKeyValuesAdd.length)
await tx.insert(haexPasswordsItemKeyValues).values(newKeyValuesAdd)
const promisesDelete = keyValuesDelete.map((keyValue) =>
tx
.delete(haexPasswordsItemKeyValues)
.where(eq(haexPasswordsItemKeyValues.id, keyValue.id)),
)
await Promise.all(promisesDelete)
return newDetails.id
})
}
const deleteAsync = async (itemId: string, final: boolean = false) => {
const { currentVault } = useVaultStore()
const { createTrashIfNotExistsAsync, trashId } = usePasswordGroupStore()
console.log('deleteAsync', itemId, final)
if (final)
await currentVault?.drizzle.transaction(async (tx) => {
await tx
.delete(haexPasswordsItemKeyValues)
.where(eq(haexPasswordsItemKeyValues.itemId, itemId))
await tx
.delete(haexPasswordsItemHistory)
.where(eq(haexPasswordsItemHistory.itemId, itemId))
await tx
.delete(haexPasswordsGroupItems)
.where(eq(haexPasswordsGroupItems.itemId, itemId))
await tx
.delete(haexPasswordsItemDetails)
.where(eq(haexPasswordsItemDetails.id, itemId))
})
else {
if (await createTrashIfNotExistsAsync())
await currentVault.drizzle
.update(haexPasswordsGroupItems)
.set({ groupId: trashId })
.where(eq(haexPasswordsGroupItems.itemId, itemId))
}
}
const deleteKeyValueAsync = async (id: string) => {
console.log('deleteKeyValueAsync', id)
const { currentVault } = useVaultStore()
return await currentVault.drizzle
.delete(haexPasswordsItemKeyValues)
.where(eq(haexPasswordsItemKeyValues.id, id))
}

View File

@ -12,7 +12,7 @@ export const useUiStore = defineStore('uiStore', () => {
const breakpoints = useBreakpoints(breakpointsTailwind)
const currentScreenSize = computed(() =>
breakpoints.active().value.length > 0 ? breakpoints.active().value : 'xs'
breakpoints.active().value.length > 0 ? breakpoints.active().value : 'xs',
)
const { t } = useI18n({
@ -45,11 +45,14 @@ export const useUiStore = defineStore('uiStore', () => {
const currentTheme = ref(defaultTheme)
const currentThemeValue = computed(() => currentTheme.value.value)
return {
availableThemes,
breakpoints,
currentScreenSize,
currentTheme,
currentThemeValue,
defaultTheme,
}
})

View File

@ -27,20 +27,8 @@ export const useSidebarStore = defineStore('sidebarStore', () => {
},
])
/* 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,
isVisible,
//loadAsync,
}
})

View File

@ -1,5 +1,6 @@
import * as schema from '@/../src-tauri/database/schemas/vault'
import { invoke } from '@tauri-apps/api/core'
import { exists } from '@tauri-apps/plugin-fs'
import { platform } from '@tauri-apps/plugin-os'
import { eq } from 'drizzle-orm'
import type { SqliteRemoteDatabase } from 'drizzle-orm/sqlite-proxy'
@ -120,8 +121,7 @@ export const useVaultStore = defineStore('vaultStore', () => {
return vaultId
} catch (error) {
console.error('Error openAsync ', error)
throw new Error(JSON.stringify(error))
return false
throw error
}
}