266 lines
7.4 KiB
TypeScript
266 lines
7.4 KiB
TypeScript
// 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 },
|
||
);
|
||
}
|
||
} |