Files
vereinskalender/app/api/settings/logo/route.ts
2026-01-15 23:18:42 +01:00

123 lines
3.5 KiB
TypeScript

import { NextResponse } from "next/server";
import { promises as fs } from "fs";
import path from "path";
import { prisma } from "../../../../lib/prisma";
import { isSuperAdminSession, requireSession } from "../../../../lib/auth-helpers";
export const dynamic = "force-dynamic";
export const revalidate = 0;
const DATA_DIR = path.join(process.cwd(), "prisma", "data");
const UPLOADS_DIR = path.join(DATA_DIR, "uploads");
const MIME_TO_EXT: Record<string, string> = {
"image/png": "png",
"image/jpeg": "jpg",
"image/webp": "webp",
"image/svg+xml": "svg"
};
const MAX_FILE_SIZE = 2 * 1024 * 1024;
const resolveLogoPath = (relativePath: string) => {
const absolutePath = path.join(DATA_DIR, relativePath);
if (!absolutePath.startsWith(DATA_DIR)) {
throw new Error("Ungültiger Pfad.");
}
return absolutePath;
};
const getLogoSetting = async () =>
prisma.setting.findUnique({ where: { key: "app_logo_path" } });
const getLogoTypeSetting = async () =>
prisma.setting.findUnique({ where: { key: "app_logo_type" } });
export async function POST(request: Request) {
const { session } = await requireSession();
if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
if (!isSuperAdminSession(session)) {
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
}
const formData = await request.formData();
const file = formData.get("file");
if (!file || !(file instanceof File)) {
return NextResponse.json({ error: "Datei fehlt." }, { status: 400 });
}
const extension = MIME_TO_EXT[file.type];
if (!extension) {
return NextResponse.json({ error: "Dateityp nicht unterstützt." }, { status: 400 });
}
if (file.size > MAX_FILE_SIZE) {
return NextResponse.json({ error: "Datei ist zu groß (max. 2 MB)." }, { status: 400 });
}
await fs.mkdir(UPLOADS_DIR, { recursive: true });
const previousSetting = await getLogoSetting();
const previousTypeSetting = await getLogoTypeSetting();
const filename = `app-logo.${extension}`;
const relativePath = path.join("uploads", filename);
const absolutePath = resolveLogoPath(relativePath);
const buffer = Buffer.from(await file.arrayBuffer());
await fs.writeFile(absolutePath, buffer);
if (previousSetting?.value && previousSetting.value !== relativePath) {
try {
await fs.unlink(resolveLogoPath(previousSetting.value));
} catch {
// ignore missing old file
}
}
await prisma.setting.upsert({
where: { key: "app_logo_path" },
update: { value: relativePath },
create: { key: "app_logo_path", value: relativePath }
});
await prisma.setting.upsert({
where: { key: "app_logo_type" },
update: { value: file.type },
create: { key: "app_logo_type", value: file.type }
});
return NextResponse.json({ ok: true });
}
export async function DELETE() {
const { session } = await requireSession();
if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
if (!isSuperAdminSession(session)) {
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
}
const logoSetting = await getLogoSetting();
const typeSetting = await getLogoTypeSetting();
if (logoSetting?.value) {
try {
await fs.unlink(resolveLogoPath(logoSetting.value));
} catch {
// ignore missing file
}
}
if (logoSetting) {
await prisma.setting.delete({ where: { key: "app_logo_path" } });
}
if (typeSetting) {
await prisma.setting.delete({ where: { key: "app_logo_type" } });
}
return NextResponse.json({ ok: true });
}