// app/api/devices/route.ts import { NextResponse } from 'next/server'; import { prisma } from '@/lib/prisma'; import type { Prisma } from '@/generated/prisma/client'; import { getCurrentUserId } from '@/lib/auth'; import type { Server as IOServer } from 'socket.io'; export async function GET() { try { const devices = await prisma.device.findMany({ include: { group: true, location: true, tags: true, }, }); return NextResponse.json( devices.map((d) => ({ inventoryNumber: d.inventoryNumber, name: d.name, manufacturer: d.manufacturer, model: d.model, serialNumber: d.serialNumber, productNumber: d.productNumber, comment: d.comment, ipv4Address: d.ipv4Address, ipv6Address: d.ipv6Address, macAddress: d.macAddress, username: d.username, passwordHash: d.passwordHash, group: d.group?.name ?? null, location: d.location?.name ?? null, tags: d.tags.map((t) => t.name), loanedTo: d.loanedTo, loanedFrom: d.loanedFrom ? d.loanedFrom.toISOString() : null, loanedUntil: d.loanedUntil ? d.loanedUntil.toISOString() : null, loanComment: d.loanComment, updatedAt: d.updatedAt.toISOString(), })), ); } catch (err) { console.error('[GET /api/devices]', err); return NextResponse.json({ error: 'INTERNAL_ERROR' }, { status: 500 }); } } export async function POST(req: Request) { try { const body = await req.json(); const { inventoryNumber, name, manufacturer, model, serialNumber, productNumber, comment, group, location, ipv4Address, ipv6Address, macAddress, username, passwordHash, tags, // Verleih-Felder loanedTo, loanedFrom, loanedUntil, loanComment, } = body; if (!inventoryNumber || !name) { return NextResponse.json( { error: 'inventoryNumber und name sind Pflichtfelder.' }, { status: 400 }, ); } const existing = await prisma.device.findUnique({ where: { inventoryNumber }, }); if (existing) { return NextResponse.json( { error: 'Gerät mit dieser Inventar-Nr. existiert bereits.' }, { status: 409 }, ); } const userId = await getCurrentUserId(); let canConnectUser = false; if (userId) { const user = await prisma.user.findUnique({ where: { id: userId }, select: { id: true }, }); if (user) { canConnectUser = true; } else { console.warn( `[POST /api/devices] User mit id=${userId} nicht gefunden – createdBy/changedBy werden nicht gesetzt.`, ); } } // Gruppe let groupId: string | null = null; if (group && typeof group === 'string' && group.trim() !== '') { const grp = await prisma.deviceGroup.upsert({ where: { name: group.trim() }, update: {}, create: { name: group.trim() }, }); groupId = grp.id; } // Standort let locationId: string | null = null; if (location && typeof location === 'string' && location.trim() !== '') { const loc = await prisma.location.upsert({ where: { name: location.trim() }, update: {}, create: { name: location.trim() }, }); locationId = loc.id; } const tagNames: string[] = Array.isArray(tags) ? tags.map((t: unknown) => String(t).trim()).filter(Boolean) : []; const created = await prisma.device.create({ data: { inventoryNumber, name, manufacturer, model, serialNumber: serialNumber ?? null, productNumber: productNumber ?? null, comment: comment ?? null, ipv4Address: ipv4Address ?? null, ipv6Address: ipv6Address ?? null, macAddress: macAddress ?? null, username: username ?? null, passwordHash: passwordHash ?? null, // Verleih-Felder loanedTo: loanedTo ?? null, loanedFrom: loanedFrom ? new Date(loanedFrom) : null, loanedUntil: loanedUntil ? new Date(loanedUntil) : null, loanComment: loanComment ?? null, ...(groupId ? { group: { connect: { id: groupId }, }, } : {}), ...(locationId ? { location: { connect: { id: locationId }, }, } : {}), ...(canConnectUser && userId ? { createdBy: { connect: { id: userId } } } : {}), ...(tagNames.length ? { tags: { connectOrCreate: tagNames.map((name) => ({ where: { name }, create: { name }, })), }, } : {}), }, include: { group: true, location: true, tags: true, }, }); const snapshot: Prisma.JsonObject = { before: null, after: { inventoryNumber: created.inventoryNumber, name: created.name, manufacturer: created.manufacturer, model: created.model, serialNumber: created.serialNumber, productNumber: created.productNumber, comment: created.comment, ipv4Address: created.ipv4Address, ipv6Address: created.ipv6Address, macAddress: created.macAddress, username: created.username, passwordHash: created.passwordHash, group: created.group?.name ?? null, location: created.location?.name ?? null, tags: created.tags.map((t) => t.name), loanedTo: created.loanedTo, loanedFrom: created.loanedFrom ? created.loanedFrom.toISOString() : null, loanedUntil: created.loanedUntil ? created.loanedUntil.toISOString() : null, loanComment: created.loanComment, createdAt: created.createdAt.toISOString(), updatedAt: created.updatedAt.toISOString(), }, changes: [], }; await prisma.deviceHistory.create({ data: { deviceId: created.inventoryNumber, changeType: 'CREATED', snapshot, changedById: canConnectUser && userId ? userId : null, }, }); const io = (global as any).devicesIo as IOServer | undefined; if (io) { io.emit('device:created', { inventoryNumber: created.inventoryNumber, name: created.name, manufacturer: created.manufacturer, model: created.model, serialNumber: created.serialNumber, productNumber: created.productNumber, comment: created.comment, ipv4Address: created.ipv4Address, ipv6Address: created.ipv6Address, macAddress: created.macAddress, username: created.username, group: created.group?.name ?? null, location: created.location?.name ?? null, tags: created.tags.map((t) => t.name), loanedTo: created.loanedTo, loanedFrom: created.loanedFrom ? created.loanedFrom.toISOString() : null, loanedUntil: created.loanedUntil ? created.loanedUntil.toISOString() : null, loanComment: created.loanComment, updatedAt: created.updatedAt.toISOString(), }); } return NextResponse.json( { inventoryNumber: created.inventoryNumber, name: created.name, manufacturer: created.manufacturer, model: created.model, serialNumber: created.serialNumber, productNumber: created.productNumber, comment: created.comment, ipv4Address: created.ipv4Address, ipv6Address: created.ipv6Address, macAddress: created.macAddress, username: created.username, passwordHash: created.passwordHash, group: created.group?.name ?? null, location: created.location?.name ?? null, tags: created.tags.map((t) => t.name), loanedTo: created.loanedTo, loanedFrom: created.loanedFrom ? created.loanedFrom.toISOString() : null, loanedUntil: created.loanedUntil ? created.loanedUntil.toISOString() : null, loanComment: created.loanComment, updatedAt: created.updatedAt.toISOString(), }, { status: 201 }, ); } catch (err) { console.error('[POST /api/devices]', err); return NextResponse.json( { error: 'Interner Serverfehler beim Anlegen des Geräts.' }, { status: 500 }, ); } }