Files
online-energieausweis/src/pages/api/bild.ts
2025-10-14 11:44:18 -04:00

122 lines
2.9 KiB
TypeScript

import { maybeAuthorizationMiddleware } from "#lib/middleware/authorization.js";
import { prisma } from "#lib/server/prisma.js";
import { defineApiRoute, APIError } from "astro-typesafe-api/server";
import { z } from "astro:content";
import isBase64 from "is-base64";
import { IDWithPrefix } from "#components/Ausweis/types.js";
import { BildSchema } from "src/generated/zod/bild.js";
import sharp from "sharp";
import { PutObjectCommand } from "@aws-sdk/client-s3";
import { s3Client } from "#lib/s3.js";
import { generateIDWithPrefix } from "#lib/db.js";
import { VALID_UUID_PREFIXES } from "#lib/constants.js";
import { logger } from "#lib/logger.js";
export const PUT = defineApiRoute({
input: BildSchema.pick({
kategorie: true,
name: true,
}).merge(
z.object({
data: z.string(),
})
),
output: z.object({
id: z.string({ description: "Die id des Bildes." }),
}),
middleware: maybeAuthorizationMiddleware,
async fetch(input, context, user) {
const data = input.data;
if (!isBase64(data, { mimeRequired: true })) {
throw new APIError({
code: "BAD_REQUEST",
message: "Das Bild ist nicht base64.",
});
}
const dataWithoutPrefix = data.replace(/^data:image\/\w+;base64,/, "");
const buffer = Buffer.from(dataWithoutPrefix, "base64");
const id = generateIDWithPrefix(9, VALID_UUID_PREFIXES.Bild);
const bild = await prisma.bild.create({
data: {
id,
kategorie: input.kategorie,
name: input.name,
benutzer_id: user ? user.id : null,
},
});
try {
// Wir optimieren das Bild und konvertieren es in JPEG
const optimizedBuffer = await sharp(buffer)
.jpeg({ quality: 80 })
.toBuffer();
const command = new PutObjectCommand({
Bucket: "ibc-images",
Key: `${id}.jpg`,
Body: optimizedBuffer,
ACL: "private",
});
const response = await s3Client.send(command);
} catch (e) {
// Bild wurde nicht gespeichert, wir löschen den Eintrag wieder
await prisma.bild.delete({
where: {
id,
},
});
logger.error("Fehler beim Speichern des Bildes in S3: " + e);
// Und geben einen Fehler zurück
throw new APIError({
code: "INTERNAL_SERVER_ERROR",
message: "Bild konnte nicht gespeichert werden.",
});
}
return {
id,
};
},
});
export const DELETE = defineApiRoute({
input: z.object({
id: IDWithPrefix,
}),
middleware: maybeAuthorizationMiddleware,
async fetch(input, context, user) {
try {
if (user) {
await prisma.bild.delete({
where: {
id: input.id,
OR: [{
benutzer_id: user.id
}, {
benutzer_id: null
}]
},
});
} else {
await prisma.bild.delete({
where: {
id: input.id,
aufnahme_id: null,
},
});
}
} catch (e) {
logger.error("Fehler beim Löschen des Bildes: " + e);
throw new APIError({
code: "INTERNAL_SERVER_ERROR",
message: "Bild konnte nicht gelöscht werden.",
});
}
},
});