<script setup lang="ts"> 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/auth"; import { Toast } from "bootstrap"; const authStore = useAuthStore(); authStore.$onAction(({ name, args }) => { if (name === "updateUser") { refreshKeys(args[0].uid); } }); let successToast: Toast | null = null; 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); function refreshKeys(uid: string) { S3KeyService.s3KeyGetUserKeys(uid) .then((keys) => { if (keyState.activeKey >= keys.length) { keyState.activeKey = keys.length - 1; } keyState.keys = keys; }) .catch((err) => console.error(err)) .finally(() => (keyState.initialLoading = false)); } function deleteKey(accessKey: string) { if (allowKeyDeletion.value && authStore.authenticated) { S3KeyService.s3KeyDeleteUserKey(accessKey, authStore.currentUID) .then(() => { keyState.deletedKey = accessKey; keyState.activeKey = 0; keyState.keys = keyState.keys.filter( (s3key) => s3key.access_key !== accessKey, ); authStore.setS3Key(keyState.keys[0]); successToast?.show(); }) .catch((err) => console.error(err)); } } 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)); } } onMounted(() => { successToast = new Toast("#successKeyToast"); if (authStore.authenticated) { refreshKeys(authStore.currentUID); } }); </script> <template> <div class="toast-container position-fixed top-toast end-0 p-3"> <div role="alert" aria-live="assertive" aria-atomic="true" class="toast text-bg-success align-items-center border-0" data-bs-autohide="true" :id="'successKeyToast'" > <div class="d-flex"> <div class="toast-body"> Successfully deleted S3 Key {{ keyState.deletedKey.slice(0, 5) }}... </div> <button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close" ></button> </div> </div> </div> <div class="row m-2 border-bottom mt-4"> <div class="col-12"></div> <h1 class="mb-2">S3 Keys</h1> </div> <div class="row m-2 mt-4"> <div class="col-4"> <div class="d-flex justify-content-between mb-4"> <button type="button" class="btn btn-light border shadow-sm" @click="refreshKeys(authStore.currentUID)" > <font-awesome-icon icon="fa-solid fa-arrow-rotate-right" class="fs-5" /> <span class="visually-hidden">Refresh S3 Keys</span> </button> <button type="button" class="btn btn-light border shadow-sm" @click="createKey" > <font-awesome-icon icon="fa-solid fa-plus" class="fs-5" /> <span class="visually-hidden">Create S3 Key</span> </button> </div> <div v-if="keyState.initialLoading" class="placeholder-glow"> <a v-for="n in 3" :key="n" type="button" aria-hidden="true" class="btn w-100 fs-5 mb-3 btn-secondary disabled placeholder" /> </div> <div v-else class="d-grid gap-3"> <button v-for="(s3key, index) in keyState.keys" :key="s3key.access_key" class="btn fs-5 text-truncate border hover-shadow" type="button" @click="keyState.activeKey = index" :class="{ 'btn-light': keyState.activeKey !== index, 'btn-primary': keyState.activeKey === index, shadow: keyState.activeKey === index, }" > {{ s3key.access_key.slice(0, 5) }}... </button> </div> </div> <div class="col-7 offset-md-1"> <s3-key-view v-if="keyState.keys.length > 0 || keyState.initialLoading" :s3key=" keyState.initialLoading ? { user: '', access_key: '', secret_key: '' } : keyState.keys[keyState.activeKey] " :deletable="allowKeyDeletion" :loading="keyState.initialLoading" @delete-key="deleteKey" /> <div v-else> No keys here. <br /> Create a new Key to interact with your Buckets again. </div> </div> </div> </template> <style scoped></style>