geraete/app/api/devices/route.ts
2025-11-18 14:44:36 +01:00

266 lines
7.4 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 '@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),
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,
} = 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 },
);
}
// aktuell eingeloggten User ermitteln
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 / Standort (per Name) anlegen/finden
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;
}
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,
// ⬇️ Relation über group/location, nicht groupId/locationId
...(groupId
? {
group: {
connect: { id: groupId },
},
}
: {}),
...(locationId
? {
location: {
connect: { id: locationId },
},
}
: {}),
// User, der das Gerät angelegt hat
...(canConnectUser && userId
? { createdBy: { connect: { id: userId } } }
: {}),
// Tags Many-to-Many
...(tagNames.length
? {
tags: {
connectOrCreate: tagNames.map((name) => ({
where: { name },
create: { name },
})),
},
}
: {}),
},
include: {
group: true,
location: true,
tags: true,
},
});
// History-Eintrag "CREATED" mit changedById
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),
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,
},
});
// Socket-Event
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),
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),
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 },
);
}
}