From 1d1178ae2842092317910a131fbda875dedce198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B6bel?= <dgoebel@techfak.uni-bielefeld.de> Date: Thu, 5 Oct 2023 16:35:52 +0200 Subject: [PATCH] Fetch s3 keys with data repository architecture #67 --- .../modals/CreateBucketModal.vue | 2 +- src/stores/s3keys.ts | 58 +++++++++++++++++++ src/stores/users.ts | 22 ++----- src/views/object-storage/BucketView.vue | 55 ++++++------------ src/views/object-storage/S3KeysView.vue | 41 +++++-------- 5 files changed, 95 insertions(+), 83 deletions(-) create mode 100644 src/stores/s3keys.ts diff --git a/src/components/object-storage/modals/CreateBucketModal.vue b/src/components/object-storage/modals/CreateBucketModal.vue index ca3ae22..01411cd 100644 --- a/src/components/object-storage/modals/CreateBucketModal.vue +++ b/src/components/object-storage/modals/CreateBucketModal.vue @@ -119,7 +119,7 @@ function modalClosed() { class="form-control" id="bucketDescriptionInput" required - rows="5" + rows="3" minlength="32" maxlength="65536" v-model="bucket.description" diff --git a/src/stores/s3keys.ts b/src/stores/s3keys.ts new file mode 100644 index 0000000..7b6b187 --- /dev/null +++ b/src/stores/s3keys.ts @@ -0,0 +1,58 @@ +import { defineStore } from "pinia"; +import { useAuthStore } from "@/stores/users"; +import type { S3Key } from "@/client/s3proxy"; +import { S3KeyService } from "@/client/s3proxy"; + +export const useS3KeyStore = defineStore({ + id: "s3keys", + state: () => + ({ + keyMapping: {}, + }) as { + keyMapping: Record<string, S3Key>; + }, + getters: { + keys(): S3Key[] { + const tempList = Object.values(this.keyMapping); + tempList.sort((keyA, keyB) => + keyA.access_key > keyB.access_key ? 1 : -1, + ); + return tempList; + }, + }, + actions: { + fetchS3Keys(uid: string, onFinally?: () => void): Promise<S3Key[]> { + if (this.keys.length > 0) { + onFinally?.(); + } + return S3KeyService.s3KeyGetUserKeys(uid) + .then((keys) => { + const newMapping: Record<string, S3Key> = {}; + for (const key of keys) { + newMapping[key.access_key] = key; + } + this.keyMapping = newMapping; + return keys; + }) + .finally(onFinally); + }, + deleteS3Key(access_id: string): Promise<void> { + const userRepository = useAuthStore(); + return S3KeyService.s3KeyDeleteUserKey( + access_id, + userRepository.currentUID, + ).then(() => { + delete this.keyMapping[access_id]; + }); + }, + createS3Key(): Promise<S3Key> { + const userRepository = useAuthStore(); + return S3KeyService.s3KeyCreateUserKey(userRepository.currentUID).then( + (key) => { + this.keyMapping[key.access_key] = key; + return key; + }, + ); + }, + }, +}); diff --git a/src/stores/users.ts b/src/stores/users.ts index 1947988..9a96af0 100644 --- a/src/stores/users.ts +++ b/src/stores/users.ts @@ -1,14 +1,13 @@ import { defineStore } from "pinia"; import type { User } from "@/client/auth"; import { UserService, RoleEnum } from "@/client/auth"; -import { S3KeyService } from "@/client/s3proxy"; -import type { S3Key } from "@/client/s3proxy"; import { OpenAPI as S3ProxyOpenAPI } from "@/client/s3proxy"; import { OpenAPI as AuthOpenAPI } from "@/client/auth"; import { OpenAPI as WorkflowOpenAPI } from "@/client/workflow"; import { useWorkflowExecutionStore } from "@/stores/workflowExecutions"; import { useBucketStore } from "@/stores/buckets"; import { useWorkflowStore } from "@/stores/workflows"; +import { useS3KeyStore } from "@/stores/s3keys"; type DecodedToken = { exp: number; @@ -21,7 +20,6 @@ export type RootState = { token: string | null; decodedToken: DecodedToken | null; user: User | null; - s3key: S3Key | null; userMapping: Record<string, string>; }; @@ -48,7 +46,6 @@ export const useAuthStore = defineStore({ token: null, decodedToken: null, user: null, - s3key: null, userMapping: {}, }) as RootState, getters: { @@ -105,28 +102,17 @@ export const useAuthStore = defineStore({ this.user = null; } }, - setS3Key(key: S3Key | null) { - this.s3key = key; - }, updateUser(user: User) { this.user = user; - S3KeyService.s3KeyGetUserKeys(user.uid) - .then((keys) => { - if (keys.length > 0) { - this.setS3Key(keys[0]); - } else { - this.setS3Key(null); - } - }) - .catch(() => { - this.setS3Key(null); - }); + const keyRepository = useS3KeyStore(); + keyRepository.fetchS3Keys(user.uid); }, logout() { this.$reset(); useWorkflowExecutionStore().$reset(); useBucketStore().$reset(); useWorkflowStore().$reset(); + useS3KeyStore().$reset(); }, fetchUsernames(uids: string[]): Promise<User[]> { const filteredIds = uids diff --git a/src/views/object-storage/BucketView.vue b/src/views/object-storage/BucketView.vue index 6797814..2493b5d 100644 --- a/src/views/object-storage/BucketView.vue +++ b/src/views/object-storage/BucketView.vue @@ -32,9 +32,11 @@ import { awsAuthMiddlewareOptions } from "@aws-sdk/middleware-signing"; import { useAuthStore } from "@/stores/users"; import { useBucketStore } from "@/stores/buckets"; import { environment } from "@/environment"; +import { useS3KeyStore } from "@/stores/s3keys"; const authStore = useAuthStore(); const bucketRepository = useBucketStore(); +const keyRepository = useS3KeyStore(); const middleware = [ // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -49,39 +51,20 @@ const middleware = [ }, ]; -let client = new S3Client({ - region: "us-east-1", - endpoint: environment.S3_URL, - forcePathStyle: true, - credentials: { - accessKeyId: authStore.s3key?.access_key ?? "", - secretAccessKey: authStore.s3key?.secret_key ?? "", - }, -}); -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -client.middlewareStack.addRelativeTo(middleware[0], middleware[1]); - -// If S3 Key changes -authStore.$onAction(({ name, args }) => { - if (name === "setS3Key") { - if (args[0] === null) { - console.error("There are no S3 Keys"); - } else { - client = new S3Client({ - region: "us-east-1", - endpoint: environment.S3_URL, - forcePathStyle: true, - credentials: { - accessKeyId: args[0].access_key, - secretAccessKey: args[0].secret_key, - }, - }); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - client.middlewareStack.addRelativeTo(middleware[0], middleware[1]); - } - } +const client = computed<S3Client>(() => { + const client = new S3Client({ + region: "us-east-1", + endpoint: environment.S3_URL, + forcePathStyle: true, + credentials: { + accessKeyId: keyRepository.keys[0]?.access_key ?? "", + secretAccessKey: keyRepository.keys[0]?.secret_key ?? "", + }, + }); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + client.middlewareStack.addRelativeTo(middleware[0], middleware[1]); + return client; }); // Constants @@ -427,7 +410,7 @@ function confirmedDeleteObject(key: string) { Bucket: props.bucketName, Key: key, }); - client + client.value .send(command) .then(() => { bucketRepository.fetchBucket(props.bucketName); @@ -453,7 +436,7 @@ async function downloadObject(key: string, bucket: string) { Bucket: bucket, Key: key, }); - const url = await getSignedUrl(client, command, { expiresIn: 30 }); + const url = await getSignedUrl(client.value, command, { expiresIn: 30 }); //creating an invisible element const element = document.createElement("a"); element.setAttribute("href", url); @@ -483,7 +466,7 @@ function confirmedDeleteFolder(folderPath: string) { }), }, }); - client + client.value .send(command) .then(() => { bucketRepository.fetchBucket(props.bucketName); diff --git a/src/views/object-storage/S3KeysView.vue b/src/views/object-storage/S3KeysView.vue index 7a6ab5d..a9072b8 100644 --- a/src/views/object-storage/S3KeysView.vue +++ b/src/views/object-storage/S3KeysView.vue @@ -2,42 +2,37 @@ import S3KeyView from "@/views/object-storage/S3KeyView.vue"; import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; import { reactive, onMounted, computed } from "vue"; -import type { S3Key } from "@/client/s3proxy"; -import { S3KeyService } from "@/client/s3proxy"; import { useAuthStore } from "@/stores/users"; import { Toast, Tooltip } from "bootstrap"; +import { useS3KeyStore } from "@/stores/s3keys"; const authStore = useAuthStore(); +const keyRepository = useS3KeyStore(); let successToast: Toast | null = null; let refreshTimeout: NodeJS.Timeout | undefined = undefined; const keyState = reactive<{ - keys: S3Key[]; activeKey: number; initialLoading: boolean; deletedKey: string; }>({ - keys: [], activeKey: 0, initialLoading: true, deletedKey: "", }); -const allowKeyDeletion = computed<boolean>(() => keyState.keys.length > 1); +const allowKeyDeletion = computed<boolean>(() => keyRepository.keys.length > 1); function fetchKeys() { - S3KeyService.s3KeyGetUserKeys(authStore.currentUID) + keyRepository + .fetchS3Keys(authStore.currentUID, () => (keyState.initialLoading = false)) .then((keys) => { if (keyState.activeKey >= keys.length) { keyState.activeKey = keys.length - 1; } - keyState.keys = keys; }) - .catch((err) => console.error(err)) - .finally(() => { - keyState.initialLoading = false; - }); + .catch((err) => console.error(err)); } function refreshKeys() { @@ -49,14 +44,11 @@ function refreshKeys() { function deleteKey(accessKey: string) { if (allowKeyDeletion.value && authStore.authenticated) { - S3KeyService.s3KeyDeleteUserKey(accessKey, authStore.currentUID) + keyRepository + .deleteS3Key(accessKey) .then(() => { - keyState.deletedKey = accessKey; keyState.activeKey = 0; - keyState.keys = keyState.keys.filter( - (s3key) => s3key.access_key !== accessKey, - ); - authStore.setS3Key(keyState.keys[0]); + keyState.deletedKey = accessKey; successToast?.show(); }) .catch((err) => console.error(err)); @@ -65,14 +57,7 @@ function deleteKey(accessKey: string) { function createKey() { if (authStore.authenticated) { - S3KeyService.s3KeyCreateUserKey(authStore.currentUID) - .then((s3key) => { - keyState.keys.push(s3key); - keyState.keys = [...keyState.keys].sort((keyA, keyB) => - keyA.access_key > keyB.access_key ? 1 : -1, - ); - }) - .catch((err) => console.error(err)); + keyRepository.createS3Key().catch((err) => console.error(err)); } } @@ -153,7 +138,7 @@ onMounted(() => { </div> <div v-else class="d-grid gap-3"> <button - v-for="(s3key, index) in keyState.keys" + v-for="(s3key, index) in keyRepository.keys" :key="s3key.access_key" class="btn fs-5 text-truncate border hover-shadow" type="button" @@ -170,11 +155,11 @@ onMounted(() => { </div> <div class="col-7 offset-md-1"> <s3-key-view - v-if="keyState.keys.length > 0 || keyState.initialLoading" + v-if="keyRepository.keys.length > 0 || keyState.initialLoading" :s3key=" keyState.initialLoading ? { user: '', access_key: '', secret_key: '' } - : keyState.keys[keyState.activeKey] + : keyRepository.keys[keyState.activeKey] ?? keyRepository.keys[0] " :deletable="allowKeyDeletion" :loading="keyState.initialLoading" -- GitLab