Skip to content
Snippets Groups Projects
Verified Commit a55e8cf0 authored by Daniel Göbel's avatar Daniel Göbel
Browse files

Add functionality for creating and deleting buckets

#5
parent 0311130a
No related branches found
No related tags found
1 merge request!4Display buckets and their objects
......@@ -19,6 +19,7 @@
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.1.0",
"@types/bootstrap": "^5.2.0",
"@types/node": "^16.11.45",
"@vitejs/plugin-vue": "^3.0.1",
"@vue/eslint-config-prettier": "^7.0.0",
......@@ -1686,6 +1687,15 @@
"integrity": "sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==",
"dev": true
},
"node_modules/@types/bootstrap": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.2.0.tgz",
"integrity": "sha512-zncxyEdbShnbOkY1zGBNqvpdVuIGDEA3rxcANJ4v9b3yaMxp+xDCNSwrXK5+uM7Wz7cb1RoIoRUQ0Q5JyfjyfA==",
"dev": true,
"dependencies": {
"@popperjs/core": "^2.9.2"
}
},
"node_modules/@types/json-schema": {
"version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
......@@ -6984,6 +6994,15 @@
"integrity": "sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==",
"dev": true
},
"@types/bootstrap": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.2.0.tgz",
"integrity": "sha512-zncxyEdbShnbOkY1zGBNqvpdVuIGDEA3rxcANJ4v9b3yaMxp+xDCNSwrXK5+uM7Wz7cb1RoIoRUQ0Q5JyfjyfA==",
"dev": true,
"requires": {
"@popperjs/core": "^2.9.2"
}
},
"@types/json-schema": {
"version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
......
......@@ -22,6 +22,7 @@
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.1.0",
"@types/bootstrap": "^5.2.0",
"@types/node": "^16.11.45",
"@vitejs/plugin-vue": "^3.0.1",
"@vue/eslint-config-prettier": "^7.0.0",
......
......@@ -27,7 +27,7 @@ onBeforeMount(() => {
<template>
<NavbarTop />
<div class="container-fluid">
<div class="container-fluid" style="min-height: 90vh">
<router-view></router-view>
</div>
</template>
......
@import "base.css";
body {
padding-top: 4rem;
max-height: 100vh;
}
<script setup lang="ts">
import { useRoute } from "vue-router";
import { useRoute, useRouter } from "vue-router";
import { BucketService } from "@/client";
const emit = defineEmits(["bucketDeleted"]);
const route = useRoute();
const router = useRouter();
function deleteBucket() {
BucketService.bucketDeleteBucket(route.params.bucket_name as string).then(
() => {
emit("bucketDeleted", route.params.bucket_name);
router.push({ name: "buckets" });
}
);
}
</script>
<template>
<p>Hello {{ route.params.bucket_name }}</p>
<button type="button" class="btn btn-danger" @click="deleteBucket">
Delete
</button>
</template>
<style scoped></style>
<script setup lang="ts">
import { BucketService } from "@/client";
import type { BucketIn } from "@/client";
import { reactive, ref } from "vue";
import { onMounted } from "vue";
import { Modal } from "bootstrap";
const emit = defineEmits(["bucketCreated"]);
const bucket = reactive({ name: "", description: "" } as BucketIn);
const formState = reactive({
validated: false,
bucketNameTaken: false,
loading: false,
} as {
validated: boolean;
bucketNameTaken: boolean;
loading: boolean;
});
let theModal: Modal | null = null;
const modal = ref(null as Element | null);
defineProps<{
modalid: string;
modallabel: string;
}>();
onMounted(() => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
theModal = Modal.getOrCreateInstance(modal.value!);
});
function createBucket() {
formState.validated = true;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const form = document.getElementById("bucketCreateForm")! as HTMLFormElement;
formState.bucketNameTaken = false;
if (form.checkValidity()) {
formState.loading = true;
BucketService.bucketCreateBucket(bucket)
.then((createdBucket) => {
emit("bucketCreated", createdBucket);
console.log(theModal);
//theModal?.hide();
bucket.name = "";
bucket.description = "";
formState.bucketNameTaken = false;
formState.validated = false;
})
.catch((error) => {
if (
error.status === 400 &&
error.body["detail"] === "Bucket name is already taken"
) {
console.log("Bucket name already taken");
formState.bucketNameTaken = true;
}
})
.finally(() => {
formState.loading = false;
});
}
}
function modalClosed() {
formState.validated = false;
formState.bucketNameTaken = false;
}
</script>
<template>
<div
class="modal"
:id="modalid"
ref="modal"
tabindex="-1"
data-bs-backdrop="static"
:aria-labelledby="modallabel"
aria-hidden="true"
v-on="{ 'hidden.bs.modal': modalClosed }"
>
<div class="modal-dialog modal-dialog-centered text-dark">
<div class="modal-content">
<div class="modal-header">
<div class="modal-title fs-5" :id="modallabel">
Create new Bucket
<div
class="spinner-border text-primary ms-2"
role="status"
v-if="formState.loading"
>
<span class="visually-hidden">Loading...</span>
</div>
</div>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
<form
id="bucketCreateForm"
:class="{ 'was-validated': formState.validated }"
novalidate
>
<div class="mb-3">
<label for="bucketNameInput" class="form-label"
>Bucket Name</label
>
<div class="input-group">
<input
type="text"
class="form-control"
id="bucketNameInput"
required
minlength="3"
maxlength="63"
pattern="(?!(^((2(5[0-5]|[0-4]\d)|[01]?\d{1,2})\.){3}(2(5[0-5]|[0-4]\d)|[01]?\d{1,2})$))^[a-z\d][a-z\d.-]{1,61}[a-z\d]$"
v-model.trim="bucket.name"
/>
<div class="invalid-feedback">
Requirements
<ul>
<li>At least 3 Characters long</li>
<li>Lowercase</li>
<li>Only [a-z][0-9].-</li>
<li>No IP address</li>
</ul>
</div>
</div>
</div>
<div class="mb-3">
<label for="bucketDescriptionInput" class="form-label">
Description
</label>
<div class="input-group">
<textarea
class="form-control"
id="bucketDescriptionInput"
required
rows="5"
minlength="126"
maxlength="65536"
v-model.trim="bucket.description"
placeholder="Describe the purpose of the bucket"
></textarea>
<div class="invalid-feedback">
Requirements
<ul>
<li>At least 126 Characters long</li>
</ul>
</div>
</div>
</div>
</form>
<div v-if="formState.bucketNameTaken" class="text-danger">
Bucket name already taken.
</div>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal"
>
Close
</button>
<button
type="submit"
form="bucketCreateForm"
class="btn btn-primary"
:disabled="formState.loading"
@click.prevent="createBucket"
>
Save
</button>
</div>
</div>
</div>
</div>
</template>
<style scoped></style>
......@@ -22,7 +22,7 @@ import "bootstrap/js/dist/button";
// import "bootstrap/js/dist/carousel";
import "bootstrap/js/dist/collapse";
import "bootstrap/js/dist/dropdown";
// import "bootstrap/js/dist/modal";
import "bootstrap/js/dist/modal";
// import "bootstrap/js/dist/offcanvas";
// import "bootstrap/js/dist/popover";
// import "bootstrap/js/dist/scrollspy";
......
import { createRouter, createWebHistory } from "vue-router";
import BucketsView from "../views/object-storage/BucketsView.vue";
import DashboardView from "../views/DashboardView.vue";
import LoginView from "../views/LoginView.vue";
......@@ -14,7 +13,7 @@ const router = createRouter({
{
path: "object-storage/buckets",
name: "buckets",
component: BucketsView,
component: () => import("../views/object-storage/BucketsView.vue"),
children: [
{
path: ":bucket_name",
......
......@@ -5,7 +5,7 @@ import SidebarLeft from "../components/SidebarLeft.vue";
<template>
<div class="row">
<SidebarLeft class="col-2" />
<div class="col-10 offset-md-3">
<div class="col-10 offset-md-2">
<router-view />
</div>
</div>
......
......@@ -18,7 +18,7 @@ onBeforeMount(() => {
<template>
<div
class="card text-center bg-dark ms-md-auto position-absolute top-50 left-50 translate-middle"
class="card text-center bg-dark ms-md-auto position-absolute top-50 start-50 translate-middle"
>
<div class="card-header text-dark bg-light">LoginView</div>
<div class="card-body p-5">
......
......@@ -5,6 +5,7 @@ import type { BucketOut } from "@/client";
import { BucketService } from "@/client";
import { useRoute } from "vue-router";
import BootstrapIcon from "../../components/BootstrapIcon.vue";
import CreateBucketComponent from "../../components/CreateBucketComponent.vue";
const route = useRoute();
......@@ -31,6 +32,16 @@ function fetchBuckets() {
});
}
function addBucket(bucket: BucketOut) {
bucketsState.buckets.push(bucket);
}
function deleteBucket(bucket_name: string) {
bucketsState.buckets = bucketsState.buckets.filter(
(bucket) => bucket.name !== bucket_name
);
}
onMounted(() => {
fetchBuckets();
});
......@@ -49,11 +60,17 @@ onMounted(() => {
<button
type="button"
class="btn btn-light ms-2"
@click.stop.prevent="fetchBuckets"
data-bs-toggle="modal"
data-bs-target="#exampleModal"
>
<bootstrap-icon icon="plus-lg" />
</button>
<div class="list-group">
<create-bucket-component
modalid="exampleModal"
modallabel="exampleModallabel"
@bucket-created="addBucket"
/>
<div class="list-group overflow-scroll">
<div v-if="!bucketsState.loading">
<router-link
v-for="bucket in bucketsState.buckets"
......@@ -77,7 +94,7 @@ onMounted(() => {
</div>
</div>
<div class="col-8">
<router-view></router-view>
<router-view @bucket-deleted="deleteBucket"></router-view>
</div>
</div>
</template>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment