geraete/app/api/devices/route.ts
2025-11-24 08:59:14 +01:00

301 lines
8.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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 },
);
}
}