Unveränderter Stand von certvia/dev (a48c5fb) plus Craftvia-Spezifikation und Brandbook unter docs/craftvia/. ISMS-Module werden im Folgecommit entfernt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
97 lines
4.3 KiB
TypeScript
97 lines
4.3 KiB
TypeScript
"use server";
|
|
|
|
import { revalidatePath } from "next/cache";
|
|
import { redirect } from "next/navigation";
|
|
import { z } from "zod";
|
|
import { prisma } from "@/server/db";
|
|
import { moduleGuard } from "@/server/action-guard";
|
|
import { writeAuditLog } from "@/server/audit";
|
|
import { withQuery } from "@/lib/supplier";
|
|
|
|
const guard = moduleGuard("suppliers");
|
|
|
|
const level = z.coerce.number().int().min(1).max(4);
|
|
|
|
const softwareSchema = z.object({
|
|
name: z.string().trim().min(1).max(200),
|
|
confidentiality: level,
|
|
integrity: level,
|
|
availability: level,
|
|
criticality: level,
|
|
providerAssetId: z.string().optional(),
|
|
version: z.string().trim().max(100).optional(),
|
|
approvalStatus: z.enum(["BEANTRAGT", "FREIGEGEBEN", "GESPERRT"]),
|
|
approvedBy: z.string().trim().max(200).optional(),
|
|
nextReview: z.string().optional(),
|
|
notes: z.string().trim().max(5000).optional(),
|
|
});
|
|
|
|
function parse(formData: FormData) {
|
|
const p = softwareSchema.parse({
|
|
name: formData.get("name"),
|
|
confidentiality: formData.get("confidentiality"),
|
|
integrity: formData.get("integrity"),
|
|
availability: formData.get("availability"),
|
|
criticality: formData.get("criticality"),
|
|
providerAssetId: formData.get("providerAssetId") || undefined,
|
|
version: formData.get("version") || undefined,
|
|
approvalStatus: formData.get("approvalStatus") || "BEANTRAGT",
|
|
approvedBy: formData.get("approvedBy") || undefined,
|
|
nextReview: formData.get("nextReview") || undefined,
|
|
notes: formData.get("notes") || undefined,
|
|
});
|
|
return {
|
|
asset: { name: p.name, confidentiality: p.confidentiality, integrity: p.integrity, availability: p.availability },
|
|
profile: {
|
|
criticality: p.criticality,
|
|
providerAssetId: p.providerAssetId || null,
|
|
version: p.version || null,
|
|
approvalStatus: p.approvalStatus,
|
|
approvedBy: p.approvedBy || null,
|
|
nextReview: p.nextReview ? new Date(p.nextReview) : null,
|
|
notes: p.notes || null,
|
|
},
|
|
};
|
|
}
|
|
|
|
export async function createSoftware(formData: FormData) {
|
|
const { session, db } = await guard("supplier:write");
|
|
const { asset, profile } = parse(formData);
|
|
const created = await db.asset.create({
|
|
data: { ...asset, type: "SOFTWARE", tenantId: session.user.tenantId, createdBy: session.user.id },
|
|
});
|
|
const last = await prisma.softwareProfile.aggregate({ where: { tenantId: session.user.tenantId }, _max: { refNo: true } });
|
|
await db.softwareProfile.create({
|
|
data: { ...profile, assetId: created.id, refNo: (last._max.refNo ?? 0) + 1, tenantId: session.user.tenantId, createdBy: session.user.id },
|
|
});
|
|
await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "create", entity: "software", entityId: created.id, after: { ...asset, ...profile } });
|
|
revalidatePath("/suppliers");
|
|
revalidatePath("/assets");
|
|
redirect(`/suppliers?tab=software&edit=${created.id}`);
|
|
}
|
|
|
|
export async function updateSoftware(assetId: string, formData: FormData) {
|
|
const { session, db } = await guard("supplier:write");
|
|
const before = await db.asset.findUnique({ where: { id: assetId }, include: { softwareProfile: true } });
|
|
if (!before) throw new Error("Software nicht gefunden");
|
|
const { asset, profile } = parse(formData);
|
|
await db.asset.update({ where: { id: assetId }, data: asset });
|
|
await db.softwareProfile.update({ where: { assetId }, data: profile });
|
|
await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "update", entity: "software", entityId: assetId, before, after: { ...asset, ...profile } });
|
|
revalidatePath("/suppliers");
|
|
revalidatePath("/assets");
|
|
const returnTo = (formData.get("returnTo") as string) || "/suppliers?tab=software";
|
|
redirect(withQuery(returnTo, "detail", assetId));
|
|
}
|
|
|
|
export async function deleteSoftware(assetId: string, returnTo: string = "/suppliers?tab=software") {
|
|
const { session, db } = await guard("supplier:write");
|
|
const before = await db.asset.findUnique({ where: { id: assetId } });
|
|
if (!before) throw new Error("Software nicht gefunden");
|
|
await db.asset.delete({ where: { id: assetId } });
|
|
await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "delete", entity: "software", entityId: assetId, before });
|
|
revalidatePath("/suppliers");
|
|
revalidatePath("/assets");
|
|
redirect(returnTo);
|
|
}
|