diff --git a/components/UploadFileDialog.vue b/components/UploadFileDialog.vue
index b2b8b91..101f40c 100644
--- a/components/UploadFileDialog.vue
+++ b/components/UploadFileDialog.vue
@@ -51,15 +51,22 @@
class="transition mt-2 block text-sm font-semibold text-zinc-400 group-hover:text-zinc-500"
>Upload file
-
- {{ currentFile.name }}
-
+
+
+ {{ currentFile }}
+
+
(file = (e.target as any)?.files)"
/>
@@ -67,7 +74,7 @@
({
});
const file = ref();
-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();
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)) {
diff --git a/pages/admin/metadata/games/[id]/index.vue b/pages/admin/metadata/games/[id]/index.vue
index f773a3c..415fe7e 100644
--- a/pages/admin/metadata/games/[id]/index.vue
+++ b/pages/admin/metadata/games/[id]/index.vue
@@ -293,6 +293,7 @@
:options="{ id: game.id }"
accept="image/*"
endpoint="/api/v1/admin/game/image"
+ :multiple="true"
@upload="(result: Game) => uploadAfterImageUpload(result)"
/>
diff --git a/server/api/v1/admin/game/image/index.post.ts b/server/api/v1/admin/game/image/index.post.ts
index 7ff453d..e230e9f 100644
--- a/server/api/v1/admin/game/image/index.post.ts
+++ b/server/api/v1/admin/game/image/index.post.ts
@@ -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,
},
},
});
diff --git a/server/api/v1/admin/game/metadata.post.ts b/server/api/v1/admin/game/metadata.post.ts
index df64c04..c64ef97 100644
--- a/server/api/v1/admin/game/metadata.post.ts
+++ b/server/api/v1/admin/game/metadata.post.ts
@@ -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;
diff --git a/server/api/v1/admin/news/index.post.ts b/server/api/v1/admin/news/index.post.ts
index 5e3286e..abda24d 100644
--- a/server/api/v1/admin/news/index.post.ts
+++ b/server/api/v1/admin/news/index.post.ts
@@ -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({
diff --git a/server/internal/utils/handlefileupload.ts b/server/internal/utils/handlefileupload.ts
index e5fe80c..4e36c44 100644
--- a/server/internal/utils/handlefileupload.ts
+++ b/server/internal/utils/handlefileupload.ts
@@ -6,23 +6,21 @@ export async function handleFileUpload(
h3: H3Event,
metadata: { [key: string]: string },
permissions: Array,
-): 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];
}