@@ -267,6 +314,50 @@ async function addUser() {
>
+
+ Image
+
+
+
+
+
+ {{ t(imageError) }}
+
+
+ Upload new image
+
+ Delete image
+
+
Hunt admin members:
diff --git a/server/api/admin/hunt/[huntId]/image.delete.ts b/server/api/admin/hunt/[huntId]/image.delete.ts
new file mode 100644
index 0000000..7ee6a71
--- /dev/null
+++ b/server/api/admin/hunt/[huntId]/image.delete.ts
@@ -0,0 +1,55 @@
+import { useValidatedParams, z, zh } from 'h3-zod';
+import prisma from '~~/lib/prisma';
+
+export default defineEventHandler(async (event) => {
+ const user = await requireUserSession(event);
+
+ if (user.user.role !== 'ADMIN') {
+ throw createError({
+ status: 403,
+ statusText: 'Not allowed!'
+ });
+ }
+ const userId = user.user.id;
+ const { huntId } = await useValidatedParams(
+ event,
+ z.object({
+ huntId: zh.numAsString
+ })
+ );
+
+ const hunt = await prisma.hunt.findUnique({
+ where: {
+ id: huntId,
+ OR: [
+ {
+ creatorId: userId
+ },
+ {
+ members: {
+ some: {
+ memberId: userId
+ }
+ }
+ }
+ ]
+ },
+ select: {
+ id: true
+ }
+ });
+ if (!hunt) {
+ throw createError({ status: 404, statusText: 'Not found!' });
+ }
+
+ await prisma.hunt.update({
+ where: {
+ id: huntId
+ },
+ data: {
+ pictureId: null
+ }
+ });
+
+ return {success: true};
+});
diff --git a/server/api/admin/hunt/[huntId]/image.post.ts b/server/api/admin/hunt/[huntId]/image.post.ts
new file mode 100644
index 0000000..7aeb615
--- /dev/null
+++ b/server/api/admin/hunt/[huntId]/image.post.ts
@@ -0,0 +1,88 @@
+import { useValidatedParams, z, zh } from 'h3-zod';
+import { randomUUID } from 'node:crypto';
+import prisma from '~~/lib/prisma';
+import { minioClient, s3Bucket, s3Host } from '~~/lib/s3';
+
+export default defineEventHandler(async (event) => {
+ const user = await requireUserSession(event);
+
+ if (user.user.role !== 'ADMIN') {
+ throw createError({
+ status: 403,
+ statusText: 'Not allowed!'
+ });
+ }
+ const userId = user.user.id;
+ const { huntId } = await useValidatedParams(
+ event,
+ z.object({
+ huntId: zh.numAsString
+ })
+ );
+
+ const hunt = await prisma.hunt.findUnique({
+ where: {
+ id: huntId,
+ OR: [
+ {
+ creatorId: userId
+ },
+ {
+ members: {
+ some: {
+ memberId: userId
+ }
+ }
+ }
+ ]
+ },
+ select: {
+ id: true
+ }
+ });
+ if (!hunt) {
+ throw createError({ status: 404, statusText: 'Not found!' });
+ }
+
+ const body = await readFormData(event);
+ const formDataObj = Object.fromEntries(body.entries());
+
+ const { image } = z
+ .object({
+ image: z
+ .file()
+ .mime(['image/png', 'image/jpeg', 'image/jpg'])
+ .max(10_000_000)
+ })
+ .parse(formDataObj);
+
+ const uuid = randomUUID();
+ await minioClient.putObject(
+ s3Bucket,
+ uuid,
+ Buffer.from(await image.arrayBuffer()),
+ undefined,
+ {
+ 'Cache-Control': 'public, max-age=31536000, immutable',
+ 'Content-Type': image.type
+ }
+ );
+
+ await prisma.hunt.update({
+ where: {
+ id: huntId
+ },
+ data: {
+ picture: {
+ create: {
+ creatorId: userId,
+ url: `https://${s3Bucket}.${s3Host}/${uuid}`,
+ uuid,
+ huntId
+ }
+ }
+ }
+ });
+
+ return { success: true };
+});
diff --git a/server/api/admin/hunt/[huntId]/index.get.ts b/server/api/admin/hunt/[huntId]/index.get.ts
index 3431468..fba7f8b 100644
--- a/server/api/admin/hunt/[huntId]/index.get.ts
+++ b/server/api/admin/hunt/[huntId]/index.get.ts
@@ -48,6 +48,12 @@ export default defineEventHandler(async (event) => {
revealQuests: true,
revealAnswers: true,
updatedAt: true,
+ picture: {
+ select: {
+ id: true,
+ url: true
+ }
+ },
creator: {
select: {
name: true,
diff --git a/shared/utils/image.ts b/shared/utils/image.ts
new file mode 100644
index 0000000..3ea4f95
--- /dev/null
+++ b/shared/utils/image.ts
@@ -0,0 +1 @@
+export const createImageURL = (file: File) => URL.createObjectURL(file);