121 lines
3.5 KiB
TypeScript
121 lines
3.5 KiB
TypeScript
// app/api/profile/avatar/route.ts
|
|
import { NextRequest, NextResponse } from 'next/server';
|
|
import { getServerSession } from 'next-auth';
|
|
import { authOptions } from '@/lib/auth-options';
|
|
import { prisma } from '@/lib/prisma';
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
export async function POST(req: NextRequest) {
|
|
const session = await getServerSession(authOptions);
|
|
|
|
const user = session?.user as any | undefined;
|
|
const nwkennung: string | undefined = user?.nwkennung;
|
|
|
|
if (!session || !nwkennung) {
|
|
return NextResponse.json({ error: 'UNAUTHORIZED' }, { status: 401 });
|
|
}
|
|
|
|
const formData = await req.formData();
|
|
const file = formData.get('avatar');
|
|
|
|
if (!file || !(file instanceof Blob)) {
|
|
return NextResponse.json({ error: 'NO_FILE' }, { status: 400 });
|
|
}
|
|
|
|
const MAX_SIZE = 5 * 1024 * 1024; // 5 MB
|
|
const mime = file.type || '';
|
|
|
|
if (!mime.startsWith('image/')) {
|
|
return NextResponse.json({ error: 'INVALID_TYPE' }, { status: 400 });
|
|
}
|
|
|
|
const size: number | undefined = file.size;
|
|
if (typeof size === 'number' && size > MAX_SIZE) {
|
|
return NextResponse.json(
|
|
{ error: 'TOO_LARGE', maxSizeBytes: MAX_SIZE },
|
|
{ status: 413 },
|
|
);
|
|
}
|
|
|
|
// Dateiendung bestimmen
|
|
let ext = '';
|
|
if ('name' in file) {
|
|
const name = (file as any).name as string;
|
|
ext = path.extname(name);
|
|
}
|
|
if (!ext) {
|
|
if (mime === 'image/jpeg') ext = '.jpg';
|
|
else if (mime === 'image/png') ext = '.png';
|
|
else if (mime === 'image/gif') ext = '.gif';
|
|
else ext = '.img';
|
|
}
|
|
|
|
const avatarsDir = path.join(process.cwd(), 'public', 'avatars');
|
|
await fs.promises.mkdir(avatarsDir, { recursive: true });
|
|
|
|
// alte Avatare löschen
|
|
const existingFiles = await fs.promises.readdir(avatarsDir);
|
|
await Promise.all(
|
|
existingFiles
|
|
.filter((f) => f.startsWith(`${nwkennung}-`))
|
|
.map((f) => fs.promises.unlink(path.join(avatarsDir, f))),
|
|
);
|
|
|
|
// Neuer, eindeutiger Dateiname
|
|
const timestamp = Date.now();
|
|
const fileName = `${nwkennung}-${timestamp}${ext.toLowerCase()}`;
|
|
const filePath = path.join(avatarsDir, fileName);
|
|
|
|
const arrayBuffer = await file.arrayBuffer();
|
|
const buffer = Buffer.from(arrayBuffer);
|
|
await fs.promises.writeFile(filePath, buffer);
|
|
|
|
const avatarUrl = `/avatars/${fileName}`;
|
|
|
|
await prisma.user.update({
|
|
where: { nwkennung },
|
|
data: { avatarUrl },
|
|
});
|
|
|
|
return NextResponse.json({ ok: true, avatarUrl });
|
|
}
|
|
|
|
// 👇 Neu: Profilbild löschen
|
|
export async function DELETE(req: NextRequest) {
|
|
const session = await getServerSession(authOptions);
|
|
|
|
const user = session?.user as any | undefined;
|
|
const nwkennung: string | undefined = user?.nwkennung;
|
|
|
|
if (!session || !nwkennung) {
|
|
return NextResponse.json({ error: 'UNAUTHORIZED' }, { status: 401 });
|
|
}
|
|
|
|
const avatarsDir = path.join(process.cwd(), 'public', 'avatars');
|
|
|
|
// Dateien löschen (falls vorhanden)
|
|
try {
|
|
const existingFiles = await fs.promises.readdir(avatarsDir);
|
|
const userFiles = existingFiles.filter((f) =>
|
|
f.startsWith(`${nwkennung}-`),
|
|
);
|
|
await Promise.all(
|
|
userFiles.map((f) => fs.promises.unlink(path.join(avatarsDir, f))),
|
|
);
|
|
} catch (err: any) {
|
|
// Wenn es den Ordner nicht gibt, ignorieren
|
|
if (err?.code !== 'ENOENT') {
|
|
console.error('[DELETE /api/profile/avatar] cleanup error', err);
|
|
}
|
|
}
|
|
|
|
// avatarUrl in DB auf null setzen
|
|
await prisma.user.update({
|
|
where: { nwkennung },
|
|
data: { avatarUrl: null },
|
|
});
|
|
|
|
return NextResponse.json({ ok: true, avatarUrl: null });
|
|
}
|