Skip to content
Snippets Groups Projects

Add direct S3 interaction

Merged Daniel Göbel requested to merge feature/s3-interaction into development
1 file
+ 93
2
Compare changes
  • Side-by-side
  • Inline
@@ -6,12 +6,16 @@ import { ObjectService } from "@/client";
@@ -6,12 +6,16 @@ import { ObjectService } from "@/client";
import BootstrapIcon from "@/components/BootstrapIcon.vue";
import BootstrapIcon from "@/components/BootstrapIcon.vue";
import fileSize from "filesize";
import fileSize from "filesize";
import dayjs from "dayjs";
import dayjs from "dayjs";
import { Tooltip } from "bootstrap";
import { Toast, Tooltip } from "bootstrap";
import PermissionListModal from "@/components/Modals/PermissionListModal.vue";
import PermissionListModal from "@/components/Modals/PermissionListModal.vue";
import UploadObjectModal from "@/components/Modals/UploadObjectModal.vue";
import UploadObjectModal from "@/components/Modals/UploadObjectModal.vue";
import PermissionModal from "@/components/Modals/PermissionModal.vue";
import PermissionModal from "@/components/Modals/PermissionModal.vue";
import CreateFolderModal from "@/components/Modals/CreateFolderModal.vue";
import CreateFolderModal from "@/components/Modals/CreateFolderModal.vue";
import { S3Client } from "@aws-sdk/client-s3";
import {
 
S3Client,
 
DeleteObjectCommand,
 
DeleteObjectsCommand,
 
} from "@aws-sdk/client-s3";
import { awsAuthMiddlewareOptions } from "@aws-sdk/middleware-signing";
import { awsAuthMiddlewareOptions } from "@aws-sdk/middleware-signing";
const client = new S3Client({
const client = new S3Client({
@@ -46,6 +50,8 @@ const props = defineProps<{
@@ -46,6 +50,8 @@ const props = defineProps<{
subFolders: string[] | string;
subFolders: string[] | string;
permission: BucketPermission | undefined;
permission: BucketPermission | undefined;
}>();
}>();
 
const randomIDSuffix = Math.random().toString(16).substr(2, 8);
 
let successToast: Toast | null = null;
// Typescript types
// Typescript types
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
@@ -75,12 +81,14 @@ const objectState = reactive({
@@ -75,12 +81,14 @@ const objectState = reactive({
bucketNotFoundError: false,
bucketNotFoundError: false,
bucketPermissionError: false,
bucketPermissionError: false,
createdPermission: undefined,
createdPermission: undefined,
 
deletedItem: "",
} as {
} as {
objects: S3ObjectMetaInformation[];
objects: S3ObjectMetaInformation[];
loading: boolean;
loading: boolean;
bucketNotFoundError: boolean;
bucketNotFoundError: boolean;
bucketPermissionError: boolean;
bucketPermissionError: boolean;
createdPermission: undefined | BucketPermission;
createdPermission: undefined | BucketPermission;
 
deletedItem: string;
});
});
// Watcher
// Watcher
@@ -223,6 +231,7 @@ onMounted(() => {
@@ -223,6 +231,7 @@ onMounted(() => {
.forEach(
.forEach(
(tooltipTriggerEl) => new Tooltip(tooltipTriggerEl, { trigger: "hover" })
(tooltipTriggerEl) => new Tooltip(tooltipTriggerEl, { trigger: "hover" })
);
);
 
successToast = new Toast("#successToast-" + randomIDSuffix);
});
});
// Functions
// Functions
@@ -306,6 +315,60 @@ function objectUploaded(newObject: S3ObjectMetaInformation) {
@@ -306,6 +315,60 @@ function objectUploaded(newObject: S3ObjectMetaInformation) {
}
}
}
}
 
/**
 
* Delete a Object in the current folder
 
* @param key Key of the Object
 
*/
 
function deleteObject(key: string) {
 
const command = new DeleteObjectCommand({
 
Bucket: props.bucketName,
 
Key: key,
 
});
 
client
 
.send(command)
 
.then(() => {
 
const splittedKey = key.split("/");
 
objectState.deletedItem = splittedKey[splittedKey.length - 1];
 
successToast?.show();
 
objectState.objects = objectState.objects.filter(
 
(obj) => obj.key !== key
 
);
 
})
 
.catch((err) => {
 
console.error(err);
 
});
 
}
 
 
/**
 
* Delete a folder in the current Bucket
 
* @param folderPath Path to the folder with a trailing "/", e.g. some/path/to/a/folder/
 
*/
 
function deleteFolder(folderPath: string) {
 
const command = new DeleteObjectsCommand({
 
Bucket: props.bucketName,
 
Delete: {
 
Objects: objectState.objects
 
.filter((obj) => obj.key.startsWith(folderPath))
 
.map((obj) => {
 
return { Key: obj.key };
 
}),
 
},
 
});
 
client
 
.send(command)
 
.then(() => {
 
const splittedPath = folderPath.split("/");
 
objectState.deletedItem = splittedPath[splittedPath.length - 2];
 
successToast?.show();
 
objectState.objects = objectState.objects.filter(
 
(obj) => !obj.key.startsWith(folderPath)
 
);
 
})
 
.catch((err) => {
 
console.error(err);
 
});
 
}
 
watch(
watch(
visibleObjects,
visibleObjects,
(visObjs) => {
(visObjs) => {
@@ -323,6 +386,28 @@ watch(
@@ -323,6 +386,28 @@ watch(
</script>
</script>
<template>
<template>
 
<div class="toast-container position-fixed top-0 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="'successToast-' + randomIDSuffix"
 
>
 
<div class="d-flex">
 
<div class="toast-body">
 
Successfully deleted {{ objectState.deletedItem }}
 
</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>
<!-- Navbar Breadcrumb -->
<!-- Navbar Breadcrumb -->
<nav aria-label="breadcrumb" class="fs-2">
<nav aria-label="breadcrumb" class="fs-2">
<ol class="breadcrumb">
<ol class="breadcrumb">
@@ -590,6 +675,7 @@ watch(
@@ -590,6 +675,7 @@ watch(
<button
<button
class="dropdown-item text-danger align-middle"
class="dropdown-item text-danger align-middle"
type="button"
type="button"
 
@click="deleteObject(obj.key)"
>
>
<bootstrap-icon
<bootstrap-icon
icon="trash-fill"
icon="trash-fill"
@@ -608,6 +694,11 @@ watch(
@@ -608,6 +694,11 @@ watch(
<button
<button
type="button"
type="button"
class="btn btn-danger btn-sm align-middle"
class="btn btn-danger btn-sm align-middle"
 
@click="
 
deleteFolder(
 
obj.parentFolder.join('/') + '/' + obj.name + '/'
 
)
 
"
>
>
<bootstrap-icon
<bootstrap-icon
icon="trash-fill"
icon="trash-fill"
Loading