import { mkdir, stat, writeFile } from "fs/promises"; import path from "path"; import { randomUUID } from "crypto"; import { NextResponse } from "next/server"; import { requireCurrentUser, UnauthorizedError } from "@/lib/auth"; import { prisma } from "@/lib/prisma"; const uploadRoot = "/data/uploads"; const maxFileSizeBytes = 2 * 1024 * 1024; const allowedMimeTypes: Record = { "image/png": "png", "image/svg+xml": "svg", "image/x-icon": "ico", "image/vnd.microsoft.icon": "ico" }; function getExtension(file: File): string | null { return allowedMimeTypes[file.type] ?? null; } async function ensureSettings(userId: string, email: string) { const existingSettings = await prisma.settings.findUnique({ where: { userId } }); if (existingSettings) { return existingSettings; } return prisma.settings.create({ data: { userId, darkMode: false, calendarIcsUrl: null, calendarMaxEvents: 8, calendarLookaheadDays: 60, dashboardTitle: "Personal Dashboard", dashboardSubtitle: email, logoUrl: "/logo.svg", faviconUrl: "/favicon.ico", backgroundImageUrl: "/background-fancy.svg", backgroundImageOpacity: 32, primaryColor: "#2563eb", secondaryColor: "#dbeafe", customCss: "" } }); } export async function POST(request: Request) { try { const user = await requireCurrentUser(); await ensureSettings(user.id, user.email); const formData = await request.formData(); const file = formData.get("file"); if (!(file instanceof File)) { return NextResponse.json({ error: "Keine Datei hochgeladen." }, { status: 400 }); } if (file.size <= 0 || file.size > maxFileSizeBytes) { return NextResponse.json({ error: "Datei ist leer oder größer als 2 MB." }, { status: 400 }); } const extension = getExtension(file); if (!extension) { return NextResponse.json({ error: "Nur ICO, PNG oder SVG sind erlaubt." }, { status: 400 }); } const userUploadDirectory = path.join(uploadRoot, "users", user.id); await mkdir(userUploadDirectory, { recursive: true }); const fileName = `favicon-${Date.now()}-${randomUUID()}.${extension}`; const filePath = path.join(userUploadDirectory, fileName); const bytes = await file.arrayBuffer(); await writeFile(filePath, Buffer.from(bytes)); const writtenFile = await stat(filePath); if (!writtenFile.isFile() || writtenFile.size <= 0) { return NextResponse.json({ error: "Favicon wurde nicht korrekt gespeichert." }, { status: 500 }); } const faviconUrl = `/api/uploads/users/${user.id}/${fileName}`; const settings = await prisma.settings.update({ where: { userId: user.id }, data: { faviconUrl } }); return NextResponse.json({ settings }); } catch (error) { if (error instanceof UnauthorizedError) { return NextResponse.json({ error: "Nicht angemeldet." }, { status: 401 }); } return NextResponse.json({ error: "Favicon konnte nicht hochgeladen werden." }, { status: 500 }); } } export async function DELETE() { try { const user = await requireCurrentUser(); await ensureSettings(user.id, user.email); const settings = await prisma.settings.update({ where: { userId: user.id }, data: { faviconUrl: "/favicon.ico" } }); return NextResponse.json({ settings }); } catch (error) { if (error instanceof UnauthorizedError) { return NextResponse.json({ error: "Nicht angemeldet." }, { status: 401 }); } return NextResponse.json({ error: "Favicon konnte nicht zurückgesetzt werden." }, { status: 500 }); } }