Skip to content
Snippets Groups Projects

Add direct S3 interaction

Merged Daniel Göbel requested to merge feature/s3-interaction into development
4 files
+ 283
31
Compare changes
  • Side-by-side
  • Inline
Files
4
+ 216
0
 
<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 type { BucketOut } from "@/client";
 
import { Modal, Toast } from "bootstrap";
 
import { onMounted, reactive, watch, computed } from "vue";
 
import type { ComputedRef } from "vue";
 
 
const props = defineProps<{
 
modalID: string;
 
modalLabel: string;
 
sourceBucketName: string;
 
sourceKey: string;
 
s3Client: S3Client;
 
availableBuckets: BucketOut[];
 
}>();
 
 
const formState = reactive({
 
destKey: "",
 
destBucket: "",
 
uploading: false,
 
} as {
 
destKey: string;
 
destBucket: string;
 
uploading: boolean;
 
});
 
 
const randomIDSuffix = Math.random().toString(16).substr(2, 8);
 
let copyModal: Modal | null = null;
 
let successToast: Toast | null = null;
 
let errorToast: Toast | null = null;
 
 
const sourceFilteredBuckets: ComputedRef<BucketOut[]> = computed(() => {
 
return props.availableBuckets.filter(
 
(bucket) => bucket.name !== props.sourceBucketName
 
);
 
});
 
 
function getFileName(key: string): string {
 
const spliitedKey = key.split("/");
 
return spliitedKey[spliitedKey.length - 1];
 
}
 
 
function copyObject() {
 
const command = new CopyObjectCommand({
 
Bucket: formState.destBucket,
 
CopySource: encodeURI(`/${props.sourceBucketName}/${props.sourceKey}`),
 
Key: formState.destKey,
 
});
 
formState.uploading = true;
 
props.s3Client
 
.send(command)
 
.then(() => {
 
copyModal?.hide();
 
successToast?.show();
 
formState.destBucket = "";
 
})
 
.catch((e) => {
 
console.error(e);
 
errorToast?.show();
 
})
 
.finally(() => {
 
formState.uploading = false;
 
});
 
}
 
 
function modalClosed() {
 
formState.destBucket = "";
 
}
 
 
watch(
 
() => props.sourceKey,
 
(newKey) => {
 
formState.destKey = newKey;
 
}
 
);
 
 
onMounted(() => {
 
copyModal = new Modal("#" + props.modalID);
 
successToast = new Toast("#successToast-" + randomIDSuffix);
 
errorToast = new Toast("#errorToast-" + randomIDSuffix);
 
});
 
</script>
 
 
<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 copied file</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="toast-container position-fixed top-0 end-0 p-3">
 
<div
 
role="alert"
 
aria-live="assertive"
 
aria-atomic="true"
 
class="toast text-bg-danger align-items-center border-0"
 
data-bs-autohide="true"
 
:id="'errorToast-' + randomIDSuffix"
 
>
 
<div class="d-flex">
 
<div class="toast-body">
 
There has been some Error.<br />
 
Try again later
 
</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>
 
<bootstrap-modal
 
:modalID="modalID"
 
:static-backdrop="true"
 
:modal-label="modalLabel"
 
v-on="{ 'hidden.bs.modal': modalClosed }"
 
>
 
<template v-slot:header>
 
<h4>Copy file {{ getFileName(props.sourceKey) }}</h4>
 
</template>
 
<template v-slot:body>
 
<div class="container-fluid">
 
<div class="row">
 
<form
 
class="col-7"
 
:id="'copyObjectForm' + randomIDSuffix"
 
@submit.prevent="copyObject"
 
>
 
<div class="mb-3">
 
<label
 
:for="'destinationBucket' + randomIDSuffix"
 
class="form-label"
 
>
 
Destination Bucket *
 
</label>
 
<select
 
class="form-select text-lowercase"
 
:id="'destinationBucket' + randomIDSuffix"
 
required
 
v-model="formState.destBucket"
 
>
 
<option disabled selected>Select one...</option>
 
<option
 
v-for="bucket in sourceFilteredBuckets"
 
:key="bucket.name"
 
:value="bucket.name"
 
>
 
{{ bucket.name }}
 
</option>
 
</select>
 
</div>
 
<div class="mb-3">
 
<label :for="'objectKey' + randomIDSuffix" class="form-label"
 
>Destination Filename *</label
 
>
 
<input
 
type="text"
 
class="form-control"
 
:id="'objectKey' + randomIDSuffix"
 
required
 
v-model="formState.destKey"
 
/>
 
</div>
 
</form>
 
<div class="col-5">
 
You can copy objects. You have to create destination container prior
 
to copy.<br />
 
You can specify folder by using '/' at destination object field. For
 
example, if you want to copy object under the folder named
 
'folder1', you need to specify destination object like
 
'folder1/[your object name]'.
 
</div>
 
</div>
 
</div>
 
</template>
 
<template v-slot:footer>
 
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
 
Close
 
</button>
 
<button
 
:disabled="formState.uploading"
 
type="submit"
 
:form="'copyObjectForm' + randomIDSuffix"
 
class="btn btn-primary"
 
>
 
<span
 
v-if="formState.uploading"
 
class="spinner-border spinner-border-sm"
 
role="status"
 
aria-hidden="true"
 
></span>
 
Copy
 
</button>
 
</template>
 
</bootstrap-modal>
 
</template>
 
 
<style scoped></style>
Loading