updated
This commit is contained in:
parent
3af854663e
commit
d1f13fd77e
@ -566,7 +566,7 @@ export default function DevicesPage() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className='col-span-2'>
|
||||
<p className="text-xs font-semibold uppercase tracking-wide text-gray-400">
|
||||
MAC-Adresse
|
||||
</p>
|
||||
|
||||
@ -2,14 +2,19 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import type { Prisma } from '@prisma/client';
|
||||
import { getCurrentUserId } from '@/lib/auth';
|
||||
|
||||
type RouteParams = { id: string };
|
||||
|
||||
// ⬇️ Next liefert params als Promise
|
||||
type RouteContext = { params: Promise<RouteParams> };
|
||||
|
||||
export async function GET(
|
||||
_req: Request,
|
||||
ctx: { params: Promise<RouteParams> }, // 👈 params ist ein Promise
|
||||
ctx: RouteContext,
|
||||
) {
|
||||
const { id } = await ctx.params; // 👈 Promise auflösen
|
||||
// ⬇️ Promise auflösen
|
||||
const { id } = await ctx.params;
|
||||
|
||||
if (!id) {
|
||||
return NextResponse.json({ error: 'MISSING_ID' }, { status: 400 });
|
||||
@ -40,6 +45,7 @@ export async function GET(
|
||||
ipv6Address: device.ipv6Address,
|
||||
macAddress: device.macAddress,
|
||||
username: device.username,
|
||||
// passwordHash gebe ich hier absichtlich nicht zurück
|
||||
group: device.group?.name ?? null,
|
||||
location: device.location?.name ?? null,
|
||||
createdAt: device.createdAt.toISOString(),
|
||||
@ -53,14 +59,27 @@ export async function GET(
|
||||
|
||||
export async function PATCH(
|
||||
req: Request,
|
||||
ctx: { params: Promise<RouteParams> }, // 👈 hier auch
|
||||
ctx: RouteContext,
|
||||
) {
|
||||
const { id } = await ctx.params; // 👈 Promise auflösen
|
||||
// ⬇️ Promise auflösen
|
||||
const { id } = await ctx.params;
|
||||
const body = await req.json();
|
||||
|
||||
if (!id) {
|
||||
return NextResponse.json({ error: 'MISSING_ID' }, { status: 400 });
|
||||
}
|
||||
|
||||
try {
|
||||
// User für updatedBy / History bestimmen
|
||||
const userId = await getCurrentUserId();
|
||||
|
||||
// aktuelles Gerät inkl. Relations laden (für "before"-Snapshot)
|
||||
const existing = await prisma.device.findUnique({
|
||||
where: { inventoryNumber: id },
|
||||
include: {
|
||||
group: true,
|
||||
location: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
@ -79,9 +98,17 @@ export async function PATCH(
|
||||
ipv6Address: body.ipv6Address,
|
||||
macAddress: body.macAddress,
|
||||
username: body.username,
|
||||
passwordHash: body.passwordHash,
|
||||
};
|
||||
|
||||
// Gruppe (per Name) – Name ist @unique in DeviceGroup
|
||||
// updatedBy setzen, wenn User da
|
||||
if (userId) {
|
||||
data.updatedBy = {
|
||||
connect: { id: userId },
|
||||
};
|
||||
}
|
||||
|
||||
// Gruppe (per Name) – DeviceGroup.name ist @unique
|
||||
if (body.group != null && body.group !== '') {
|
||||
data.group = {
|
||||
connectOrCreate: {
|
||||
@ -105,6 +132,7 @@ export async function PATCH(
|
||||
data.location = { disconnect: true };
|
||||
}
|
||||
|
||||
// Update durchführen (für "after"-Snapshot)
|
||||
const updated = await prisma.device.update({
|
||||
where: { inventoryNumber: id },
|
||||
data,
|
||||
@ -114,33 +142,106 @@ export async function PATCH(
|
||||
},
|
||||
});
|
||||
|
||||
const snapshot: Prisma.JsonObject = {
|
||||
inventoryNumber: updated.inventoryNumber,
|
||||
name: updated.name,
|
||||
manufacturer: updated.manufacturer,
|
||||
model: updated.model,
|
||||
serialNumber: updated.serialNumber,
|
||||
productNumber: updated.productNumber,
|
||||
comment: updated.comment,
|
||||
ipv4Address: updated.ipv4Address,
|
||||
ipv6Address: updated.ipv6Address,
|
||||
macAddress: updated.macAddress,
|
||||
username: updated.username,
|
||||
passwordHash: updated.passwordHash,
|
||||
group: updated.group?.name ?? null,
|
||||
location: updated.location?.name ?? null,
|
||||
createdAt: updated.createdAt.toISOString(),
|
||||
updatedAt: updated.updatedAt.toISOString(),
|
||||
};
|
||||
const trackedFields = [
|
||||
'name',
|
||||
'manufacturer',
|
||||
'model',
|
||||
'serialNumber',
|
||||
'productNumber',
|
||||
'comment',
|
||||
'ipv4Address',
|
||||
'ipv6Address',
|
||||
'macAddress',
|
||||
'username',
|
||||
'passwordHash',
|
||||
] as const;
|
||||
|
||||
await prisma.deviceHistory.create({
|
||||
data: {
|
||||
deviceId: updated.inventoryNumber,
|
||||
changeType: 'UPDATED',
|
||||
snapshot,
|
||||
changedById: null,
|
||||
},
|
||||
});
|
||||
type TrackedField = (typeof trackedFields)[number];
|
||||
|
||||
const changes: {
|
||||
field: TrackedField | 'group' | 'location';
|
||||
before: string | null;
|
||||
after: string | null;
|
||||
}[] = [];
|
||||
|
||||
for (const field of trackedFields) {
|
||||
const before = (existing as any)[field] as string | null;
|
||||
const after = (updated as any)[field] as string | null;
|
||||
if (before !== after) {
|
||||
changes.push({ field, before, after });
|
||||
}
|
||||
}
|
||||
|
||||
const beforeGroup = (existing.group?.name ?? null) as string | null;
|
||||
const afterGroup = (updated.group?.name ?? null) as string | null;
|
||||
if (beforeGroup !== afterGroup) {
|
||||
changes.push({
|
||||
field: 'group',
|
||||
before: beforeGroup,
|
||||
after: afterGroup,
|
||||
});
|
||||
}
|
||||
|
||||
const beforeLocation = (existing.location?.name ?? null) as string | null;
|
||||
const afterLocation = (updated.location?.name ?? null) as string | null;
|
||||
if (beforeLocation !== afterLocation) {
|
||||
changes.push({
|
||||
field: 'location',
|
||||
before: beforeLocation,
|
||||
after: afterLocation,
|
||||
});
|
||||
}
|
||||
|
||||
if (changes.length > 0) {
|
||||
const snapshot: Prisma.JsonObject = {
|
||||
before: {
|
||||
inventoryNumber: existing.inventoryNumber,
|
||||
name: existing.name,
|
||||
manufacturer: existing.manufacturer,
|
||||
model: existing.model,
|
||||
serialNumber: existing.serialNumber,
|
||||
productNumber: existing.productNumber,
|
||||
comment: existing.comment,
|
||||
ipv4Address: existing.ipv4Address,
|
||||
ipv6Address: existing.ipv6Address,
|
||||
macAddress: existing.macAddress,
|
||||
username: existing.username,
|
||||
passwordHash: existing.passwordHash,
|
||||
group: existing.group?.name ?? null,
|
||||
location: existing.location?.name ?? null,
|
||||
createdAt: existing.createdAt.toISOString(),
|
||||
updatedAt: existing.updatedAt.toISOString(),
|
||||
},
|
||||
after: {
|
||||
inventoryNumber: updated.inventoryNumber,
|
||||
name: updated.name,
|
||||
manufacturer: updated.manufacturer,
|
||||
model: updated.model,
|
||||
serialNumber: updated.serialNumber,
|
||||
productNumber: updated.productNumber,
|
||||
comment: updated.comment,
|
||||
ipv4Address: updated.ipv4Address,
|
||||
ipv6Address: updated.ipv6Address,
|
||||
macAddress: updated.macAddress,
|
||||
username: updated.username,
|
||||
passwordHash: updated.passwordHash,
|
||||
group: updated.group?.name ?? null,
|
||||
location: updated.location?.name ?? null,
|
||||
createdAt: updated.createdAt.toISOString(),
|
||||
updatedAt: updated.updatedAt.toISOString(),
|
||||
},
|
||||
changes,
|
||||
};
|
||||
|
||||
await prisma.deviceHistory.create({
|
||||
data: {
|
||||
deviceId: updated.inventoryNumber,
|
||||
changeType: 'UPDATED',
|
||||
snapshot,
|
||||
changedById: userId ?? null,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
inventoryNumber: updated.inventoryNumber,
|
||||
|
||||
75
lib/auth.ts
Normal file
75
lib/auth.ts
Normal file
@ -0,0 +1,75 @@
|
||||
// lib/auth.ts
|
||||
'use server';
|
||||
|
||||
import { headers } from 'next/headers';
|
||||
import { prisma } from './prisma';
|
||||
|
||||
/**
|
||||
* Liefert die aktuelle User-ID oder null,
|
||||
* falls kein User ermittelt werden kann.
|
||||
*
|
||||
* Reihenfolge:
|
||||
* 1. HTTP-Header: x-user-id
|
||||
* 2. HTTP-Header: x-user-email
|
||||
* 3. Fallback: DEFAULT_USER_EMAIL env oder "user@domain.local"
|
||||
*/
|
||||
export async function getCurrentUserId(): Promise<string | null> {
|
||||
const h = await headers();
|
||||
|
||||
const headerUserId = h.get('x-user-id');
|
||||
const headerUserEmail = h.get('x-user-email');
|
||||
|
||||
// 1) Direkt über ID (Header)
|
||||
if (headerUserId) {
|
||||
const userById = await prisma.user.findUnique({
|
||||
where: { id: headerUserId },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (userById) {
|
||||
return userById.id;
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Über Email (Header)
|
||||
if (headerUserEmail) {
|
||||
const userByEmail = await prisma.user.findUnique({
|
||||
where: { email: headerUserEmail },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (userByEmail) {
|
||||
return userByEmail.id;
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Fallback: Standard-User (z.B. dein Test-User)
|
||||
const fallbackEmail =
|
||||
process.env.DEFAULT_USER_EMAIL ?? 'user@domain.local';
|
||||
|
||||
const fallbackUser = await prisma.user.findUnique({
|
||||
where: { email: fallbackEmail },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
return fallbackUser?.id ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional: kompletten User holen (falls du später mehr brauchst)
|
||||
*/
|
||||
export async function getCurrentUser() {
|
||||
const userId = await getCurrentUserId();
|
||||
if (!userId) return null;
|
||||
|
||||
return prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
include: {
|
||||
roles: {
|
||||
include: {
|
||||
role: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
BIN
prisma/dev.db
BIN
prisma/dev.db
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user