Skip to content

Commit

Permalink
feat(kb): All content encrypted
Browse files Browse the repository at this point in the history
  • Loading branch information
RezaRahemtola committed Aug 3, 2024
1 parent 45d664f commit b3018da
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 50 deletions.
4 changes: 4 additions & 0 deletions quasar.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ module.exports = configure(function (/* ctx */) {
viteConf.plugins = [];
}
viteConf.plugins = [...viteConf.plugins, nodePolyfills()];
viteConf.define = {
...viteConf.define,
'process.browser': true,
};
},

// vueRouterBase,
Expand Down
47 changes: 35 additions & 12 deletions src/pages/KnowledgeBase.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

<q-btn class="tw-w-10 tw-h-10" unelevated @click="downloadDocument(document)">
<ltai-icon name="svguse:icons.svg#download" />
<q-tooltip>Download</q-tooltip>
</q-btn>

<q-btn-dropdown class="tw-p-1" dropdown-icon="more_horiz" unelevated>
Expand Down Expand Up @@ -80,16 +81,17 @@
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { KnowledgeBase, KnowledgeDocument } from 'src/types/knowledge';
import { KnowledgeBase, KnowledgeBaseIdentifier, KnowledgeDocument } from 'src/types/knowledge';
import { useKnowledgeStore } from 'stores/knowledge';
import { exportFile, useQuasar } from 'quasar';
import { useAccount } from '@wagmi/vue';
import { useAccountStore } from 'stores/account';
import { processDocument } from 'src/utils/knowledge/document';
import { filesize } from 'filesize';
import LtaiIcon from 'components/libertai/LtaiIcon.vue';
import LtaiDialog from 'components/libertai/LtaiDialog.vue';
import KnowledgeBaseRenameDocumentDialog from 'components/dialog/KnowledgeBaseRenameDocumentDialog.vue';
import { processDocument } from 'src/utils/knowledge/document';
import { decryptFile, encryptFile } from 'src/utils/encryption';
const $q = useQuasar();
const route = useRoute();
Expand All @@ -100,6 +102,7 @@ const accountStore = useAccountStore();
const knowledgeStore = useKnowledgeStore();
const knowledgeBaseRef = ref<KnowledgeBase | undefined>(undefined);
const knowledgeBaseIdentifierRef = ref<KnowledgeBaseIdentifier | undefined>(undefined);
const showRenameDocument = ref(false);
const showDeleteDocumentConfirmation = ref(false);
Expand Down Expand Up @@ -128,19 +131,23 @@ async function loadKnowledgeBase(id: string) {
}
const knowledgeBase = knowledgeStore.knowledgeBases.find((kb) => kb.id === id);
const knowledgeBaseIdentifier = knowledgeStore.knowledgeBaseIdentifiers.find(
(kbIdentifier) => kbIdentifier.id === id,
);
if (!knowledgeBase) {
if (!knowledgeBase || !knowledgeBaseIdentifier) {
$q.notify({ message: 'Knowledge base not found', color: 'negative' });
await router.push({ path: '/knowledge-base' });
return;
}
// Set the ref with a copy to avoid modifying the store value
knowledgeBaseRef.value = JSON.parse(JSON.stringify(knowledgeBase));
knowledgeBaseIdentifierRef.value = JSON.parse(JSON.stringify(knowledgeBaseIdentifier));
}
const uploadDocuments = async (event: any) => {
if (knowledgeBaseRef.value === undefined) {
if (knowledgeBaseRef.value === undefined || knowledgeBaseIdentifierRef.value === undefined) {
return;
}
if (accountStore.alephStorage === null) {
Expand All @@ -154,11 +161,15 @@ const uploadDocuments = async (event: any) => {
const target = event.target as HTMLInputElement;
const documents: KnowledgeDocument[] = [];
const encryptionKey = Buffer.from(knowledgeBaseIdentifierRef.value.encryption.key);
const encryptionIv = Buffer.from(knowledgeBaseIdentifierRef.value.encryption.iv);
await Promise.all(
Array.from(target.files as FileList).map(async (file) => {
try {
const document = await processDocument(file);
const uploadedFileMessage = await accountStore.alephStorage!.uploadFile(file);
const encryptedFile = await encryptFile(file, encryptionKey, encryptionIv);
const uploadedFileMessage = await accountStore.alephStorage!.uploadFile(encryptedFile);
documents.push({
...document,
Expand All @@ -176,22 +187,30 @@ const uploadDocuments = async (event: any) => {
knowledgeBaseRef.value.documents = knowledgeBaseRef.value.documents.concat(documents);
await knowledgeStore.updateKnowledgeBase(
knowledgeBaseRef.value.id,
JSON.parse(JSON.stringify(knowledgeBaseRef.value)),
knowledgeBaseIdentifierRef.value,
);
};
const downloadDocument = async (document: KnowledgeDocument) => {
if (accountStore.alephStorage === null) {
if (accountStore.alephStorage === null || knowledgeBaseIdentifierRef.value === undefined) {
return;
}
const encryptionKey = Buffer.from(knowledgeBaseIdentifierRef.value.encryption.key);
const encryptionIv = Buffer.from(knowledgeBaseIdentifierRef.value.encryption.iv);
const downloadedFile = await accountStore.alephStorage.downloadFile(document.store.ipfs_hash);
exportFile(document.name, downloadedFile);
const decryptedFile = decryptFile(downloadedFile, encryptionKey, encryptionIv);
exportFile(document.name, decryptedFile);
};
const renameDocument = async (document: KnowledgeDocument, newName: string) => {
if (knowledgeBaseRef.value === undefined || accountStore.alephStorage === null) {
if (
knowledgeBaseRef.value === undefined ||
knowledgeBaseIdentifierRef.value === undefined ||
accountStore.alephStorage === null
) {
return;
}
Expand All @@ -203,8 +222,8 @@ const renameDocument = async (document: KnowledgeDocument, newName: string) => {
return d;
});
await knowledgeStore.updateKnowledgeBase(
knowledgeBaseRef.value.id,
JSON.parse(JSON.stringify(knowledgeBaseRef.value)),
knowledgeBaseIdentifierRef.value,
);
} catch (error) {
$q.notify({
Expand All @@ -215,7 +234,11 @@ const renameDocument = async (document: KnowledgeDocument, newName: string) => {
};
const deleteDocument = async (document: KnowledgeDocument) => {
if (knowledgeBaseRef.value === undefined || accountStore.alephStorage === null) {
if (
knowledgeBaseRef.value === undefined ||
knowledgeBaseIdentifierRef.value === undefined ||
accountStore.alephStorage === null
) {
return;
}
Expand All @@ -224,8 +247,8 @@ const deleteDocument = async (document: KnowledgeDocument) => {
knowledgeBaseRef.value.documents = knowledgeBaseRef.value.documents.filter((d) => d.id !== document.id);
await knowledgeStore.updateKnowledgeBase(
knowledgeBaseRef.value.id,
JSON.parse(JSON.stringify(knowledgeBaseRef.value)),
knowledgeBaseIdentifierRef.value,
);
} catch (error) {
$q.notify({
Expand Down
9 changes: 7 additions & 2 deletions src/stores/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,14 @@ import { useKnowledgeStore } from 'stores/knowledge';

const LTAI_BASE_ADDRESS = '0xF8B1b47AA748F5C7b5D0e80C726a843913EB573a';

type AccountStoreState = {
alephStorage: AlephPersistentStorage | null;
ltaiBalance: number;
};

export const useAccountStore = defineStore('account', {
state: () => ({
alephStorage: null as AlephPersistentStorage | null,
state: (): AccountStoreState => ({
alephStorage: null,
ltaiBalance: 0,
}),
actions: {
Expand Down
50 changes: 32 additions & 18 deletions src/stores/knowledge.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { defineStore } from 'pinia';
import { v4 as uuidv4 } from 'uuid';

import { KnowledgeBase } from 'src/types/knowledge';
import { KnowledgeBase, KnowledgeBaseIdentifier } from 'src/types/knowledge';
import { useAccountStore } from 'stores/account';

type KnowledgeStoreState = {
knowledgeBases: KnowledgeBase[];
knowledgeBaseIdentifiers: KnowledgeBaseIdentifier[];
isLoaded: boolean;
};

export const useKnowledgeStore = defineStore('knowledge', {
state: (): KnowledgeStoreState => ({
knowledgeBases: [],
knowledgeBaseIdentifiers: [],
isLoaded: false,
}),
actions: {
Expand All @@ -21,39 +23,51 @@ export const useKnowledgeStore = defineStore('knowledge', {
return;
}

const knowledgeBases = await alephStorage.fetchKnowledgeBases();
this.isLoaded = true;
if (knowledgeBases === undefined) {
return;
}
const knowledgeBaseIdentifiers = await alephStorage.fetchKnowledgeBaseIdentifiers();
this.knowledgeBaseIdentifiers = knowledgeBaseIdentifiers ?? [];

this.knowledgeBases = knowledgeBases;
const knowledgeBases = await Promise.all(
this.knowledgeBaseIdentifiers.map(async (kbIdentifier): Promise<KnowledgeBase | undefined> => {
return await alephStorage.fetchKnowledgeBase(
kbIdentifier.post_hash,
Buffer.from(kbIdentifier.encryption.key),
Buffer.from(kbIdentifier.encryption.iv),
);
}),
);
this.knowledgeBases = knowledgeBases.filter((kb) => kb !== undefined);
this.isLoaded = true;
},

async saveOnAleph() {
async createKnowledgeBase(name: string) {
const { alephStorage } = useAccountStore();
if (alephStorage === null) {
return;
}

await alephStorage.saveKnowledgeBases(this.knowledgeBases);
},

async createKnowledgeBase(name: string) {
this.knowledgeBases.push({ id: uuidv4(), name, documents: [], lastUpdatedAt: new Date().toISOString() });
const newKb: KnowledgeBase = { id: uuidv4(), name, documents: [], lastUpdatedAt: new Date().toISOString() };

await this.saveOnAleph();
const kbIdentifier = await alephStorage.createKnowledgeBase(newKb, this.knowledgeBaseIdentifiers);
if (kbIdentifier === undefined) {
throw new Error('Knowledge base creation failed');
}
this.knowledgeBases.push(newKb);
this.knowledgeBaseIdentifiers.push(kbIdentifier);
},

async updateKnowledgeBase(id: string, kb: KnowledgeBase) {
async updateKnowledgeBase(kb: KnowledgeBase, kbIdentifier: KnowledgeBaseIdentifier) {
const { alephStorage } = useAccountStore();
if (alephStorage === null) {
return;
}
await alephStorage.updateKnowledgeBase(kb, kbIdentifier);

this.knowledgeBases = this.knowledgeBases.map((knowledgeBase) => {
if (knowledgeBase.id === id) {
if (knowledgeBase.id === kb.id) {
return { ...kb, lastUpdatedAt: new Date().toISOString() };
}
return knowledgeBase;
});

await this.saveOnAleph();
},
},
});
13 changes: 12 additions & 1 deletion src/types/knowledge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,15 @@ export const knowledgeSchema = z.object({
});
export type KnowledgeBase = z.infer<typeof knowledgeSchema>;

export const knowledgeAlephStorage = z.object({ data: z.array(knowledgeSchema) });
export const knowledgeBaseIdentifierSchema = z.object({
id: z.string().uuid(),
post_hash: z.string(),
encryption: z.object({
key: z.string(),
iv: z.string(),
}),
});

export type KnowledgeBaseIdentifier = z.infer<typeof knowledgeBaseIdentifierSchema>;

export const knowledgeBaseIdentifiersAlephStorage = z.object({ data: z.array(knowledgeBaseIdentifierSchema) });
Loading

0 comments on commit b3018da

Please sign in to comment.