mirror of
https://github.com/Drop-OSS/drop.git
synced 2025-11-10 04:22:09 +10:00
* feat: support for file upload handler to track multiple files * feat: update image upload endpoint to allow multiple files * fix: lint
This commit is contained in:
@ -51,15 +51,22 @@
|
||||
class="transition mt-2 block text-sm font-semibold text-zinc-400 group-hover:text-zinc-500"
|
||||
>Upload file</span
|
||||
>
|
||||
<p v-if="currentFile" class="mt-1 text-xs text-zinc-400">
|
||||
{{ currentFile.name }}
|
||||
</p>
|
||||
<div v-if="currentFileList">
|
||||
<p
|
||||
v-for="currentFile in currentFileList"
|
||||
:key="currentFile"
|
||||
class="mt-1 text-[10px] text-zinc-500 whitespace-nowrap"
|
||||
>
|
||||
{{ currentFile }}
|
||||
</p>
|
||||
</div>
|
||||
</label>
|
||||
<input
|
||||
id="file-upload"
|
||||
:accept="props.accept"
|
||||
class="hidden"
|
||||
type="file"
|
||||
:multiple="props.multiple"
|
||||
@change="(e) => (file = (e.target as any)?.files)"
|
||||
/>
|
||||
</div>
|
||||
@ -67,7 +74,7 @@
|
||||
</div>
|
||||
<div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
||||
<LoadingButton
|
||||
:disabled="currentFile == undefined"
|
||||
:disabled="currentFiles == undefined"
|
||||
type="button"
|
||||
:loading="uploadLoading"
|
||||
:class="['inline-flex w-full shadow-sm sm:ml-3 sm:w-auto']"
|
||||
@ -123,10 +130,19 @@ const open = defineModel<boolean>({
|
||||
});
|
||||
|
||||
const file = ref<FileList | undefined>();
|
||||
const currentFile = computed(() => file.value?.item(0));
|
||||
const currentFiles = computed(() => file.value);
|
||||
const currentFileList = computed(() => {
|
||||
if (!currentFiles.value) return undefined;
|
||||
const list = [];
|
||||
for (const file of currentFiles.value) {
|
||||
list.push(file.name);
|
||||
}
|
||||
return list;
|
||||
});
|
||||
const props = defineProps<{
|
||||
endpoint: string;
|
||||
accept: string;
|
||||
multiple?: boolean;
|
||||
options?: { [key: string]: string };
|
||||
}>();
|
||||
const emit = defineEmits(["upload"]);
|
||||
@ -134,10 +150,12 @@ const emit = defineEmits(["upload"]);
|
||||
const uploadLoading = ref(false);
|
||||
const uploadError = ref<string | undefined>();
|
||||
async function uploadFile() {
|
||||
if (!currentFile.value) return;
|
||||
if (!currentFiles.value) return;
|
||||
|
||||
const form = new FormData();
|
||||
form.append("file", currentFile.value);
|
||||
for (const file of currentFiles.value) {
|
||||
form.append(file.name, file);
|
||||
}
|
||||
|
||||
if (props.options) {
|
||||
for (const [key, value] of Object.entries(props.options)) {
|
||||
|
||||
@ -293,6 +293,7 @@
|
||||
:options="{ id: game.id }"
|
||||
accept="image/*"
|
||||
endpoint="/api/v1/admin/game/image"
|
||||
:multiple="true"
|
||||
@upload="(result: Game) => uploadAfterImageUpload(result)"
|
||||
/>
|
||||
<ModalTemplate v-model="showAddCarouselModal">
|
||||
|
||||
@ -20,8 +20,8 @@ export default defineEventHandler(async (h3) => {
|
||||
statusMessage: "Failed to upload file",
|
||||
});
|
||||
|
||||
const [id, options, pull, dump] = uploadResult;
|
||||
if (!id) {
|
||||
const [ids, options, pull, dump] = uploadResult;
|
||||
if (ids.length == 0) {
|
||||
dump();
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
@ -48,7 +48,7 @@ export default defineEventHandler(async (h3) => {
|
||||
},
|
||||
data: {
|
||||
mImageLibraryObjectIds: {
|
||||
push: id,
|
||||
push: ids,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -14,14 +14,16 @@ export default defineEventHandler(async (h3) => {
|
||||
statusMessage: "This endpoint requires multipart form data.",
|
||||
});
|
||||
|
||||
const uploadResult = await handleFileUpload(h3, {}, ["internal:read"]);
|
||||
const uploadResult = await handleFileUpload(h3, {}, ["internal:read"], 1);
|
||||
if (!uploadResult)
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Failed to upload file",
|
||||
});
|
||||
|
||||
const [id, options, pull, dump] = uploadResult;
|
||||
const [ids, options, pull, dump] = uploadResult;
|
||||
|
||||
const id = ids.at(0);
|
||||
|
||||
// handleFileUpload reads the rest of the options for us.
|
||||
const name = options.name;
|
||||
|
||||
@ -14,19 +14,20 @@ export default defineEventHandler(async (h3) => {
|
||||
statusMessage: "This endpoint requires multipart form data.",
|
||||
});
|
||||
|
||||
const uploadResult = await handleFileUpload(h3, {}, ["internal:read"]);
|
||||
const uploadResult = await handleFileUpload(h3, {}, ["internal:read"], 1);
|
||||
if (!uploadResult)
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Failed to upload file",
|
||||
});
|
||||
|
||||
const [imageId, options, pull, _dump] = uploadResult;
|
||||
const [imageIds, options, pull, _dump] = uploadResult;
|
||||
|
||||
const title = options.title;
|
||||
const description = options.description;
|
||||
const content = options.content;
|
||||
const tags = options.tags ? (JSON.parse(options.tags) as string[]) : [];
|
||||
const imageId = imageIds.at(0);
|
||||
|
||||
if (!title || !description || !content)
|
||||
throw createError({
|
||||
|
||||
@ -6,23 +6,21 @@ export async function handleFileUpload(
|
||||
h3: H3Event<EventHandlerRequest>,
|
||||
metadata: { [key: string]: string },
|
||||
permissions: Array<string>,
|
||||
): Promise<
|
||||
[string | undefined, { [key: string]: string }, Pull, Dump] | undefined
|
||||
> {
|
||||
max = -1,
|
||||
): Promise<[string[], { [key: string]: string }, Pull, Dump] | undefined> {
|
||||
const formData = await readMultipartFormData(h3);
|
||||
if (!formData) return undefined;
|
||||
const transactionalHandler = new ObjectTransactionalHandler();
|
||||
const [add, pull, dump] = transactionalHandler.new(metadata, permissions);
|
||||
const options: { [key: string]: string } = {};
|
||||
let id;
|
||||
const ids = [];
|
||||
|
||||
for (const entry of formData) {
|
||||
if (entry.filename) {
|
||||
// Only pick one file
|
||||
if (id) continue;
|
||||
if (max > 0 && ids.length >= max) continue;
|
||||
|
||||
// Add file to transaction handler so we can void it later if we error out
|
||||
id = add(entry.data);
|
||||
ids.push(add(entry.data));
|
||||
continue;
|
||||
}
|
||||
if (!entry.name) continue;
|
||||
@ -30,5 +28,5 @@ export async function handleFileUpload(
|
||||
options[entry.name] = entry.data.toString("utf-8");
|
||||
}
|
||||
|
||||
return [id, options, pull, dump];
|
||||
return [ids, options, pull, dump];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user