diff --git a/Dockerfile b/Dockerfile index eb4c7fb260bd3b9743d04d5eac727a20f19110f6..a4eba23cfd2e33d53d8ed21c5b0c529bb034a520 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ RUN npm run build-only # production stage FROM nginx:stable-alpine as production-stage -HEALTHCHECK --interval=35s --timeout=4s CMD curl -f http://localhost || exit 1 +HEALTHCHECK --interval=35s --timeout=4s CMD curl --head -f http://localhost || exit 1 COPY --from=build-stage /app/dist /usr/share/nginx/html COPY --from=build-stage /app/src/assets/env.template.js /tmp COPY nginx.conf /etc/nginx/conf.d/default.conf diff --git a/index.html b/index.html index 0aa6bdd0bc4e0120526b53463a27e8ceaf379794..482eda96a495b02a952cbced079a488220542251 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ <meta charset="UTF-8" /> <link rel="icon" href="/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> - <title>S3 Proxy</title> + <title>CloWM</title> <script src="/env.js"></script> </head> <body> diff --git a/src/assets/main.css b/src/assets/main.css index a78c1e6441961749ba7ae2a72e26b699008f73de..06d798ea4ea6f5af9cf9dce8a26eaf8bbdc7ba90 100644 --- a/src/assets/main.css +++ b/src/assets/main.css @@ -1,7 +1,5 @@ @import "base.css"; body { - padding-top: 4rem; - max-height: 100vh; background: #181818; } diff --git a/src/components/NavbarTop.vue b/src/components/NavbarTop.vue index 2f8132b6451238bb832827e41d67d95be05ccfb6..81a6b2e3406253bbd54e7697caf514c460877a6d 100644 --- a/src/components/NavbarTop.vue +++ b/src/components/NavbarTop.vue @@ -1,29 +1,16 @@ <script setup lang="ts"> import BootstrapIcon from "@/components/BootstrapIcon.vue"; -import { reactive, onBeforeUnmount, onMounted } from "vue"; -// import { MiscellaneousService } from "@/client/auth"; import { useAuthStore } from "@/stores/auth"; import { useRoute, useRouter } from "vue-router"; import { useCookies } from "vue3-cookies"; -import { watch, ref } from "vue"; +import { watch, ref, computed } from "vue"; +import type { ComputedRef } from "vue"; const router = useRouter(); const store = useAuthStore(); const { cookies } = useCookies(); const route = useRoute(); -const api_connection = reactive({ connected: true, timer: null }); -let timer: ReturnType<typeof setInterval> | undefined = undefined; -function checkApiHealth() { - // MiscellaneousService.miscellaneousHealthCheck() - // .then(() => { - // api_connection.connected = true; - // }) - // .catch(() => { - // api_connection.connected = false; - // }); -} - function logout() { store.logout(); cookies.remove("bearer"); @@ -31,6 +18,9 @@ function logout() { } const activeRoute = ref(""); +const objectStorageActive: ComputedRef<boolean> = computed( + () => activeRoute.value == "buckets" || activeRoute.value == "s3_keys" +); watch( () => route.name, @@ -48,19 +38,23 @@ watch( } } ); - -onMounted(() => { - checkApiHealth(); - timer = setInterval(checkApiHealth, 20000); -}); -onBeforeUnmount(() => { - clearInterval(timer); -}); </script> <template> - <nav class="navbar navbar-dark fixed-top navbar-expand-lg bg-dark"> - <div class="container-fluid text-light"> + <header class="navbar navbar-expand-lg navbar-dark bd-navbar sticky-top"> + <nav class="container-xxl bd-gutter flex-wrap flex-lg-nowrap text-light"> + <button + class="navbar-toggler" + type="button" + data-bs-toggle="collapse" + data-bs-target="#navDropdown" + aria-controls="navDropdown" + aria-expanded="false" + aria-label="Toggle navigation" + v-if="store.authenticated" + > + <span class="navbar-toggler-icon"></span> + </button> <router-link class="navbar-brand ms-3" to="/"> <img src="/src/assets/images/denbi.svg" @@ -71,53 +65,67 @@ onBeforeUnmount(() => { /> CloWM </router-link> - <button - class="navbar-toggler" - type="button" - data-bs-toggle="collapse" - data-bs-target="#navbarNavAltMarkup" - aria-controls="navbarNavAltMarkup" - aria-expanded="false" - aria-label="Toggle navigation" - v-if="store.authenticated && store.user != null" - > - <span class="navbar-toggler-icon"></span> - </button> + <div class="collapse navbar-collapse" - id="navbarNavAltMarkup" - v-if="store.authenticated && store.user != null" + id="navDropdown" + v-if="store.authenticated" > - <div class="navbar-nav"> - <router-link - class="nav-link" - :aria-current="activeRoute === 'buckets' ? 'page' : null" - :to="{ name: 'buckets' }" - :class="{ active: activeRoute === 'buckets' }" - > - Buckets - </router-link> - <router-link - class="nav-link" - :to="{ name: 's3_keys' }" - :aria-current="activeRoute === 's3_keys' ? 'page' : null" - :class="{ active: activeRoute === 's3_keys' }" - > - S3 Keys - </router-link> - </div> + <ul id="objectStorageNav" class="navbar-nav"> + <li class="nav-item dropdown"> + <a + class="nav-link dropdown-toggle" + :class="{ active: objectStorageActive }" + href="#" + role="button" + data-bs-toggle="dropdown" + aria-expanded="false" + > + Object Storage + </a> + <ul class="dropdown-menu dropdown-menu-dark"> + <li> + <router-link + class="dropdown-item" + :to="{ name: 'buckets' }" + :class="{ active: activeRoute === 'buckets' }" + >Buckets</router-link + > + </li> + <li> + <router-link + class="dropdown-item" + :to="{ name: 's3_keys' }" + :class="{ active: activeRoute === 's3_keys' }" + >S3 Keys</router-link + > + </li> + </ul> + </li> + </ul> + <ul id="workflowNav" class="navbar-nav"> + <li class="nav-item dropdown"> + <a + class="nav-link dropdown-toggle" + href="#" + role="button" + data-bs-toggle="dropdown" + aria-expanded="false" + > + Workflows + </a> + <ul class="dropdown-menu dropdown-menu-dark"> + <li> + <a class="dropdown-item" href="#">Workflows</a> + </li> + <li> + <a class="dropdown-item" href="#">Executions</a> + </li> + </ul> + </li> + </ul> </div> - - <span - v-if="!api_connection.connected" - class="navbar-text text-bg-danger p-1" - > - Backend not reachable - </span> - <div - class="dropdown d-flex me-3" - v-if="store.authenticated && store.user != null" - > + <div class="dropdown" v-if="store.authenticated && store.user != null"> <a href="#" class="d-flex align-items-center text-white text-decoration-none dropdown-toggle-split" @@ -138,8 +146,8 @@ onBeforeUnmount(() => { </li> </ul> </div> - </div> - </nav> + </nav> + </header> </template> <style scoped> diff --git a/src/components/Modals/BootstrapModal.vue b/src/components/modals/BootstrapModal.vue similarity index 100% rename from src/components/Modals/BootstrapModal.vue rename to src/components/modals/BootstrapModal.vue diff --git a/src/components/Modals/DeleteModal.vue b/src/components/modals/DeleteModal.vue similarity index 97% rename from src/components/Modals/DeleteModal.vue rename to src/components/modals/DeleteModal.vue index 00dbff6fa32c8691fd18371dc532e61479b3793c..644cebf12fa0594e7792f83bec2da51f25b24a5a 100644 --- a/src/components/Modals/DeleteModal.vue +++ b/src/components/modals/DeleteModal.vue @@ -2,7 +2,7 @@ import { onMounted, ref } from "vue"; import type { Ref } from "vue"; import { Modal } from "bootstrap"; -import BootstrapModal from "@/components/Modals/BootstrapModal.vue"; +import BootstrapModal from "@/components/modals/BootstrapModal.vue"; const props = defineProps<{ modalID: string; diff --git a/src/components/Modals/SearchUserModal.vue b/src/components/modals/SearchUserModal.vue similarity index 98% rename from src/components/Modals/SearchUserModal.vue rename to src/components/modals/SearchUserModal.vue index d0fd7087aa225ec9824b0b98ba6e57e9df72f58e..e17b35e14d8c9636c4675a8757c8ce66479e844c 100644 --- a/src/components/Modals/SearchUserModal.vue +++ b/src/components/modals/SearchUserModal.vue @@ -1,6 +1,6 @@ <script setup lang="ts"> import { reactive, watch } from "vue"; -import BootstrapModal from "@/components/Modals/BootstrapModal.vue"; +import BootstrapModal from "@/components/modals/BootstrapModal.vue"; import BootstrapIcon from "@/components/BootstrapIcon.vue"; import { UserService } from "@/client/auth"; import type { User } from "@/client/auth"; diff --git a/src/components/BucketListItem.vue b/src/components/object-storage/BucketListItem.vue similarity index 96% rename from src/components/BucketListItem.vue rename to src/components/object-storage/BucketListItem.vue index cddcc0a6355069712147dc799a120ec5764d80b4..1412a89896a7bcc6eecccc74902062585ff48a93 100644 --- a/src/components/BucketListItem.vue +++ b/src/components/object-storage/BucketListItem.vue @@ -5,8 +5,8 @@ import type { BucketPermissionOut, } from "@/client/s3proxy"; import BootstrapIcon from "@/components/BootstrapIcon.vue"; -import PermissionModal from "@/components/Modals/PermissionModal.vue"; -import BucketDetailModal from "@/components/Modals/BucketDetailModal.vue"; +import PermissionModal from "@/components/object-storage/modals/PermissionModal.vue"; +import BucketDetailModal from "@/components/object-storage/modals/BucketDetailModal.vue"; import dayjs from "dayjs"; import { filesize } from "filesize"; import { computed, onMounted } from "vue"; diff --git a/src/components/BucketView.vue b/src/components/object-storage/BucketView.vue similarity index 98% rename from src/components/BucketView.vue rename to src/components/object-storage/BucketView.vue index a8114846e4db2ee6cf64b349761b0f88ca6c7c5a..86e21c8a16c19abc7081ef8e44b42a98d1c4d62d 100644 --- a/src/components/BucketView.vue +++ b/src/components/object-storage/BucketView.vue @@ -15,13 +15,13 @@ import BootstrapIcon from "@/components/BootstrapIcon.vue"; import { filesize } from "filesize"; import dayjs from "dayjs"; import { Toast, Tooltip } from "bootstrap"; -import PermissionListModal from "@/components/Modals/PermissionListModal.vue"; -import UploadObjectModal from "@/components/Modals/UploadObjectModal.vue"; -import CopyObjectModal from "@/components/Modals/CopyObjectModal.vue"; -import PermissionModal from "@/components/Modals/PermissionModal.vue"; -import ObjectDetailModal from "@/components/Modals/ObjectDetailModal.vue"; -import CreateFolderModal from "@/components/Modals/CreateFolderModal.vue"; -import DeleteModal from "@/components/Modals/DeleteModal.vue"; +import PermissionListModal from "@/components/object-storage/modals/PermissionListModal.vue"; +import UploadObjectModal from "@/components/object-storage/modals/UploadObjectModal.vue"; +import CopyObjectModal from "@/components/object-storage/modals/CopyObjectModal.vue"; +import PermissionModal from "@/components/object-storage/modals/PermissionModal.vue"; +import ObjectDetailModal from "@/components/object-storage/modals/ObjectDetailModal.vue"; +import CreateFolderModal from "@/components/object-storage/modals/CreateFolderModal.vue"; +import DeleteModal from "@/components/modals/DeleteModal.vue"; import { S3Client, DeleteObjectCommand, diff --git a/src/components/S3KeyView.vue b/src/components/object-storage/S3KeyView.vue similarity index 97% rename from src/components/S3KeyView.vue rename to src/components/object-storage/S3KeyView.vue index c2f6e491ad4fa1af0854fa8a740a377ad35c27e6..1f29e7ed3c85e875b82fb621c45e251f382e56ba 100644 --- a/src/components/S3KeyView.vue +++ b/src/components/object-storage/S3KeyView.vue @@ -3,7 +3,7 @@ import type { S3Key } from "@/client/s3proxy"; import type { Ref } from "vue"; import { ref, watch } from "vue"; import BootstrapIcon from "@/components/BootstrapIcon.vue"; -import DeleteModal from "@/components/Modals/DeleteModal.vue"; +import DeleteModal from "@/components/modals/DeleteModal.vue"; const props = defineProps<{ s3key: S3Key; diff --git a/src/components/Modals/BucketDetailModal.vue b/src/components/object-storage/modals/BucketDetailModal.vue similarity index 96% rename from src/components/Modals/BucketDetailModal.vue rename to src/components/object-storage/modals/BucketDetailModal.vue index 50145d6cec14af5e1f2bad4499c7a94f2fdbfbac..270320f86186305552c04373b47836e993821315 100644 --- a/src/components/Modals/BucketDetailModal.vue +++ b/src/components/object-storage/modals/BucketDetailModal.vue @@ -1,5 +1,5 @@ <script setup lang="ts"> -import BootstrapModal from "@/components/Modals/BootstrapModal.vue"; +import BootstrapModal from "@/components/modals/BootstrapModal.vue"; import type { BucketOut } from "@/client/s3proxy"; import dayjs from "dayjs"; import { filesize } from "filesize"; diff --git a/src/components/Modals/CopyObjectModal.vue b/src/components/object-storage/modals/CopyObjectModal.vue similarity index 98% rename from src/components/Modals/CopyObjectModal.vue rename to src/components/object-storage/modals/CopyObjectModal.vue index b782b447e28b67c322a1a35b558b26aa0fab42c2..09fb71ecb3a27d1db8979495e1452005099c7532 100644 --- a/src/components/Modals/CopyObjectModal.vue +++ b/src/components/object-storage/modals/CopyObjectModal.vue @@ -1,7 +1,7 @@ <script setup lang="ts"> import type { S3Client } from "@aws-sdk/client-s3"; import { CopyObjectCommand } from "@aws-sdk/client-s3"; -import BootstrapModal from "@/components/Modals/BootstrapModal.vue"; +import BootstrapModal from "@/components/modals/BootstrapModal.vue"; import { Modal, Toast } from "bootstrap"; import { onMounted, reactive, watch } from "vue"; import type { S3ObjectMetaInformation } from "@/client/s3proxy"; diff --git a/src/components/Modals/CreateBucketModal.vue b/src/components/object-storage/modals/CreateBucketModal.vue similarity index 98% rename from src/components/Modals/CreateBucketModal.vue rename to src/components/object-storage/modals/CreateBucketModal.vue index 6b7a85a32c641b6e624f7ffd3fb845609de34876..b1dd71a7fe8f5f5b46b57531079c1d25e83c7347 100644 --- a/src/components/Modals/CreateBucketModal.vue +++ b/src/components/object-storage/modals/CreateBucketModal.vue @@ -1,7 +1,7 @@ <script setup lang="ts"> import type { BucketIn } from "@/client/s3proxy"; import { reactive, onMounted } from "vue"; -import BootstrapModal from "@/components/Modals/BootstrapModal.vue"; +import BootstrapModal from "@/components/modals/BootstrapModal.vue"; import { useRouter } from "vue-router"; import { Modal } from "bootstrap"; import { useBucketStore } from "@/stores/buckets"; diff --git a/src/components/Modals/CreateFolderModal.vue b/src/components/object-storage/modals/CreateFolderModal.vue similarity index 98% rename from src/components/Modals/CreateFolderModal.vue rename to src/components/object-storage/modals/CreateFolderModal.vue index d4f042534917d38b03efedfd0006f8cc490eae66..ef0cf39a93a721e96c555b354387e03e27a8b917 100644 --- a/src/components/Modals/CreateFolderModal.vue +++ b/src/components/object-storage/modals/CreateFolderModal.vue @@ -1,7 +1,7 @@ <script setup lang="ts"> import type { S3Client } from "@aws-sdk/client-s3"; import { PutObjectCommand } from "@aws-sdk/client-s3"; -import BootstrapModal from "@/components/Modals/BootstrapModal.vue"; +import BootstrapModal from "@/components/modals/BootstrapModal.vue"; import { computed, onMounted, reactive } from "vue"; import type { ComputedRef } from "vue"; import type { S3ObjectMetaInformation } from "@/client/s3proxy"; diff --git a/src/components/Modals/ObjectDetailModal.vue b/src/components/object-storage/modals/ObjectDetailModal.vue similarity index 96% rename from src/components/Modals/ObjectDetailModal.vue rename to src/components/object-storage/modals/ObjectDetailModal.vue index a3cd0f4b1e8a0c281970365eba4ab1cba433b8b9..b5946bab08aa9e5fe6eea946c762c22bd52f3d20 100644 --- a/src/components/Modals/ObjectDetailModal.vue +++ b/src/components/object-storage/modals/ObjectDetailModal.vue @@ -1,5 +1,5 @@ <script setup lang="ts"> -import BootstrapModal from "@/components/Modals/BootstrapModal.vue"; +import BootstrapModal from "@/components/modals/BootstrapModal.vue"; import type { S3ObjectMetaInformation } from "@/client/s3proxy"; import dayjs from "dayjs"; import { filesize } from "filesize"; diff --git a/src/components/Modals/PermissionListModal.vue b/src/components/object-storage/modals/PermissionListModal.vue similarity index 96% rename from src/components/Modals/PermissionListModal.vue rename to src/components/object-storage/modals/PermissionListModal.vue index 8660e3cff8171d40e1bd590630b7b6bd5da81029..37b2125381ff18dbe0f4e6bd70e7a2811b1a5e9d 100644 --- a/src/components/Modals/PermissionListModal.vue +++ b/src/components/object-storage/modals/PermissionListModal.vue @@ -4,8 +4,8 @@ import type { FolderTree } from "@/types/PseudoFolder"; import { reactive } from "vue"; import { BucketPermissionService } from "@/client/s3proxy"; import { onBeforeMount, watch } from "vue"; -import BootstrapModal from "@/components/Modals/BootstrapModal.vue"; -import PermissionModal from "@/components/Modals/PermissionModal.vue"; +import BootstrapModal from "@/components/modals/BootstrapModal.vue"; +import PermissionModal from "@/components/object-storage/modals/PermissionModal.vue"; // Props // ----------------------------------------------------------------------------- diff --git a/src/components/Modals/PermissionModal.vue b/src/components/object-storage/modals/PermissionModal.vue similarity index 98% rename from src/components/Modals/PermissionModal.vue rename to src/components/object-storage/modals/PermissionModal.vue index eab180e18258479df8b5fb648832653aad739e93..ddc289951059108f1f8066d5e98e56c2aa26adcb 100644 --- a/src/components/Modals/PermissionModal.vue +++ b/src/components/object-storage/modals/PermissionModal.vue @@ -1,8 +1,8 @@ <script setup lang="ts"> import { onMounted, reactive, watch, ref, computed } from "vue"; -import BootstrapModal from "@/components/Modals/BootstrapModal.vue"; -import DeleteModal from "@/components/Modals/DeleteModal.vue"; -import SearchUserModal from "@/components/Modals/SearchUserModal.vue"; +import BootstrapModal from "@/components/modals/BootstrapModal.vue"; +import DeleteModal from "@/components/modals/DeleteModal.vue"; +import SearchUserModal from "@/components/modals/SearchUserModal.vue"; import { Modal } from "bootstrap"; import dayjs from "dayjs"; import type { @@ -508,7 +508,7 @@ onMounted(() => { type="button" class="btn btn-outline-danger btn-sm float-end" @click="permission.file_prefix = undefined" - :disabled="permission.file_prefix === undefined" + :hidden="permission.file_prefix == undefined" > <bootstrap-icon icon="x-lg" diff --git a/src/components/Modals/UploadObjectModal.vue b/src/components/object-storage/modals/UploadObjectModal.vue similarity index 99% rename from src/components/Modals/UploadObjectModal.vue rename to src/components/object-storage/modals/UploadObjectModal.vue index a80296e8d44928f4c02866027aa53d14f4ed844f..12851500b4c9b61b2c2abdd311bb4b06cbec51c7 100644 --- a/src/components/Modals/UploadObjectModal.vue +++ b/src/components/object-storage/modals/UploadObjectModal.vue @@ -1,7 +1,7 @@ <script setup lang="ts"> import type { S3Client } from "@aws-sdk/client-s3"; import { Upload } from "@aws-sdk/lib-storage"; -import BootstrapModal from "@/components/Modals/BootstrapModal.vue"; +import BootstrapModal from "@/components/modals/BootstrapModal.vue"; import { computed, onMounted, reactive, watch } from "vue"; import type { ComputedRef } from "vue"; import type { S3ObjectMetaInformation } from "@/client/s3proxy"; diff --git a/src/router/index.ts b/src/router/index.ts index 187869497a62e4bb8ff84338708d2beca0711ef2..9dbda65734c162aff7ae181d09b704a5ba1bbd3d 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -18,7 +18,8 @@ const router = createRouter({ { path: ":bucketName/:subFolders*", name: "bucket", - component: () => import("../components/BucketView.vue"), + component: () => + import("../components/object-storage/BucketView.vue"), props: true, }, ], diff --git a/src/stores/auth.ts b/src/stores/auth.ts index 8cc0dfdae780eda32dfaa1ac9ad7f2c6809cffcd..3926c95d939e95230cb1cee943d8755e60e05dc5 100644 --- a/src/stores/auth.ts +++ b/src/stores/auth.ts @@ -30,7 +30,7 @@ export const useAuthStore = defineStore({ state.user?.roles?.includes(RoleEnum.REVIEWER) ?? false, workflowDev: (state) => state.user?.roles?.includes(RoleEnum.DEVELOPER) ?? false, - workflowAdmin: (state) => + admin: (state) => state.user?.roles?.includes(RoleEnum.ADMINISTRATOR) ?? false, }, actions: { diff --git a/src/views/object-storage/BucketsView.vue b/src/views/object-storage/BucketsView.vue index 875776f65a675756f6da6ffe899dbf0182d2466b..877761c706508cf06b32dee924f84252820e29b0 100644 --- a/src/views/object-storage/BucketsView.vue +++ b/src/views/object-storage/BucketsView.vue @@ -4,9 +4,9 @@ import { computed, onMounted, reactive } from "vue"; import type { BucketOut } from "@/client/s3proxy"; import { useRoute, useRouter } from "vue-router"; import BootstrapIcon from "@/components/BootstrapIcon.vue"; -import CreateBucketModal from "@/components/Modals/CreateBucketModal.vue"; -import DeleteModal from "@/components/Modals/DeleteModal.vue"; -import BucketListItem from "@/components/BucketListItem.vue"; +import CreateBucketModal from "@/components/object-storage/modals/CreateBucketModal.vue"; +import DeleteModal from "@/components/modals/DeleteModal.vue"; +import BucketListItem from "@/components/object-storage/BucketListItem.vue"; import { useBucketStore } from "@/stores/buckets"; import { Modal } from "bootstrap"; import { useAuthStore } from "@/stores/auth"; diff --git a/src/views/object-storage/S3KeysView.vue b/src/views/object-storage/S3KeysView.vue index dbca2375ae93778c456f648293e732779fee8410..d234f3294416817478138ce48fa58c15da6c3094 100644 --- a/src/views/object-storage/S3KeysView.vue +++ b/src/views/object-storage/S3KeysView.vue @@ -1,5 +1,5 @@ <script setup lang="ts"> -import S3KeyView from "@/components/S3KeyView.vue"; +import S3KeyView from "@/components/object-storage/S3KeyView.vue"; import BootstrapIcon from "@/components/BootstrapIcon.vue"; import { reactive, onMounted, computed } from "vue"; import type { ComputedRef } from "vue";