Add image upload to s3
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
import { useValidatedParams, zh } from 'h3-zod';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import { z } from 'zod';
|
||||
import prisma from '~~/lib/prisma';
|
||||
import { minioClient, s3Bucket, s3Host } from '~~/lib/s3';
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { questId } = await useValidatedParams(
|
||||
event,
|
||||
z.object({
|
||||
questId: zh.numAsString
|
||||
})
|
||||
);
|
||||
|
||||
const user = await requireUserSession(event);
|
||||
const userId = user.user.id;
|
||||
|
||||
const quest = await prisma.huntQuest.findUnique({
|
||||
where: {
|
||||
id: questId
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
hunt: {
|
||||
select: {
|
||||
teams: {
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
ownerId: userId
|
||||
},
|
||||
{
|
||||
members: {
|
||||
some: {
|
||||
memberId: userId
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
answers: {
|
||||
select: {
|
||||
id: true,
|
||||
text: true,
|
||||
picture: true,
|
||||
updatedAt: true
|
||||
},
|
||||
where: {
|
||||
team: {
|
||||
OR: [
|
||||
{
|
||||
ownerId: userId
|
||||
},
|
||||
{
|
||||
members: {
|
||||
some: {
|
||||
memberId: userId
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!quest) {
|
||||
throw createError({ status: 404, statusText: 'Not found!' });
|
||||
}
|
||||
|
||||
if (quest.hunt.teams.length <= 0) {
|
||||
throw createError({ status: 403, statusText: 'No team yet!' });
|
||||
}
|
||||
|
||||
const team = quest.hunt.teams[0];
|
||||
|
||||
const body = await readFormData(event);
|
||||
const formDataObj = Object.fromEntries(body.entries());
|
||||
|
||||
const data = z
|
||||
.object({
|
||||
updatedAt: z.iso.datetime().optional(),
|
||||
answer: z.string().optional(),
|
||||
image: z
|
||||
.file()
|
||||
.mime(['image/png', 'image/jpeg', 'image/jpg'])
|
||||
.max(10_000_000)
|
||||
.optional()
|
||||
})
|
||||
.parse(formDataObj);
|
||||
|
||||
const prevAnswer =
|
||||
quest.answers.length > 0
|
||||
? quest.answers[0]
|
||||
: {
|
||||
updatedAt: null,
|
||||
id: null
|
||||
};
|
||||
|
||||
if (
|
||||
prevAnswer.updatedAt &&
|
||||
(!data.updatedAt ||
|
||||
new Date(data.updatedAt).getTime() <
|
||||
new Date(prevAnswer.updatedAt).getTime())
|
||||
) {
|
||||
throw createError({
|
||||
status: 409,
|
||||
statusText: 'Updating on old data, please refresh!'
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: prevent updating reviewed answers
|
||||
// TODO: respect end of hunt
|
||||
// TODO: keep old images
|
||||
|
||||
let picture: string | undefined = undefined;
|
||||
|
||||
if (data.image) {
|
||||
const uuid = randomUUID();
|
||||
await minioClient.putObject(
|
||||
s3Bucket,
|
||||
uuid,
|
||||
Buffer.from(await data.image.arrayBuffer()),
|
||||
undefined,
|
||||
{
|
||||
'Cache-Control': 'public, max-age=31536000, immutable',
|
||||
'Content-Type': data.image.type
|
||||
}
|
||||
);
|
||||
picture = `https://${s3Bucket}.${s3Host}/${uuid}`;
|
||||
}
|
||||
|
||||
if (prevAnswer.id) {
|
||||
await prisma.questAnswer.update({
|
||||
where: {
|
||||
id: prevAnswer.id
|
||||
},
|
||||
data: {
|
||||
memberId: userId,
|
||||
text: data.answer,
|
||||
picture
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await prisma.questAnswer.create({
|
||||
data: {
|
||||
teamId: team.id,
|
||||
memberId: userId,
|
||||
questId: quest.id,
|
||||
text: data.answer,
|
||||
picture
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return { ok: true };
|
||||
});
|
||||
Reference in New Issue
Block a user