/home/techb158/cosmic-risk.abdallabala.com/src/services
NameSizeModeActions
audit-service.js6590644editdlrm
auth-service.js47790644editdlrm
dashboard-service.js11100644editdlrm
gate-service.js45120644editdlrm
import-service.js61950644editdlrm
integration-service.js166530644editdlrm
project-service.js59730644editdlrm
reporting-service.js86440644editdlrm
risk-service.js125050644editdlrm
sso-service.js31670644editdlrm
Edit: /home/techb158/cosmic-risk.abdallabala.com/src/services/risk-service.js (12505B)
const { prisma } = require("../lib/prisma"); const { scoreRisk } = require("../domain/risk-engine"); const { writeAuditEvent } = require("./audit-service"); function normalizeRiskStatus(value) { if (value === "In mitigation" || value === "IN_MITIGATION") return "IN_MITIGATION"; if (value === "Accepted" || value === "ACCEPTED") return "ACCEPTED"; if (value === "Closed" || value === "CLOSED") return "CLOSED"; return "OPEN"; } function normalizeApprovalStatus(value) { if (value === "Approved" || value === "APPROVED") return "APPROVED"; if (value === "Rejected" || value === "REJECTED") return "REJECTED"; if (value === "Accepted" || value === "ACCEPTED") return "ACCEPTED"; return "PENDING"; } function normalizeMitigationStatus(value) { if (value === "In progress" || value === "IN_PROGRESS") return "IN_PROGRESS"; if (value === "Done" || value === "DONE") return "DONE"; if (value === "Rejected" || value === "REJECTED") return "REJECTED"; return "NOT_STARTED"; } async function listRisks(projectId) { const risks = await prisma.risk.findMany({ where: { projectId }, include: { mitigations: true, evidence: true }, orderBy: { createdAt: "asc" } }); return risks.map(risk => scoreRisk(Object.assign({}, risk, { mitigationProgress: risk.mitigations.length ? Math.round(risk.mitigations.reduce((sum, item) => sum + item.progressPercent, 0) / risk.mitigations.length) : 0, mitigationEffectiveness: risk.mitigations.length ? Math.round(risk.mitigations.reduce((sum, item) => sum + item.effectivenessPercent, 0) / risk.mitigations.length) : 0 }))); } async function createRisk({ projectScope, actor, payload, request }) { if (!payload.title) throw new Error("title is required"); const risk = await prisma.risk.create({ data: { projectId: projectScope.project.id, ownerDisplayName: payload.ownerDisplayName || payload.owner || actor.displayName, title: payload.title, description: payload.description || "", dimension: payload.dimension || "Technical", domain: payload.domain || "Technical risks", lifecyclePhase: payload.lifecyclePhase || projectScope.project.currentLifecyclePhase || "Needs identification", probability: Number(payload.probability || 3), impact: Number(payload.impact || 3), detectability: Number(payload.detectability || 3), status: normalizeRiskStatus(payload.status), approvalStatus: normalizeApprovalStatus(payload.approvalStatus), dueDate: payload.dueDate ? new Date(payload.dueDate) : null, evidenceSummary: payload.evidenceSummary || payload.evidence || "" } }); if (payload.evidenceTitle || payload.evidenceSummary || payload.evidenceUri) { await prisma.evidence.create({ data: { projectId: projectScope.project.id, riskId: risk.id, type: payload.evidenceType || "Review", title: payload.evidenceTitle || payload.evidenceSummary || payload.evidenceUri, uri: payload.evidenceUri || null, createdById: actor.id } }); } await writeAuditEvent({ organizationId: projectScope.organization.id, workspaceId: projectScope.project.workspaceId, projectId: projectScope.project.id, actorUserId: actor.id, entityType: "Risk", entityId: risk.id, action: "create", afterJson: risk, request }); return scoreRisk(risk); } async function updateRisk({ projectScope, riskId, actor, payload, request }) { const before = await prisma.risk.findUnique({ where: { id: riskId } }); if (!before || before.projectId !== projectScope.project.id) { const error = new Error(`Risk not found: ${riskId}`); error.status = 404; throw error; } const risk = await prisma.risk.update({ where: { id: riskId }, data: { title: payload.title ?? before.title, description: payload.description ?? before.description, dimension: payload.dimension ?? before.dimension, domain: payload.domain ?? before.domain, lifecyclePhase: payload.lifecyclePhase ?? before.lifecyclePhase, probability: payload.probability !== undefined ? Number(payload.probability) : before.probability, impact: payload.impact !== undefined ? Number(payload.impact) : before.impact, detectability: payload.detectability !== undefined ? Number(payload.detectability) : before.detectability, status: payload.status !== undefined ? normalizeRiskStatus(payload.status) : before.status, approvalStatus: payload.approvalStatus !== undefined ? normalizeApprovalStatus(payload.approvalStatus) : before.approvalStatus, dueDate: payload.dueDate !== undefined ? payload.dueDate ? new Date(payload.dueDate) : null : before.dueDate, evidenceSummary: payload.evidenceSummary ?? before.evidenceSummary } }); await writeAuditEvent({ organizationId: projectScope.organization.id, workspaceId: projectScope.project.workspaceId, projectId: projectScope.project.id, actorUserId: actor.id, entityType: "Risk", entityId: risk.id, action: "update", beforeJson: before, afterJson: risk, request }); return scoreRisk(risk); } async function deleteRisk({ projectScope, riskId, actor, request }) { const before = await prisma.risk.findUnique({ where: { id: riskId } }); if (!before || before.projectId !== projectScope.project.id) { const error = new Error(`Risk not found: ${riskId}`); error.status = 404; throw error; } await prisma.risk.delete({ where: { id: riskId } }); await writeAuditEvent({ organizationId: projectScope.organization.id, workspaceId: projectScope.project.workspaceId, projectId: projectScope.project.id, actorUserId: actor.id, entityType: "Risk", entityId: riskId, action: "delete", beforeJson: before, request }); return { deleted: true }; } async function createMitigation({ projectScope, riskId, actor, payload, request }) { const risk = await prisma.risk.findUnique({ where: { id: riskId } }); if (!risk || risk.projectId !== projectScope.project.id) { const error = new Error(`Risk not found: ${riskId}`); error.status = 404; throw error; } const mitigation = await prisma.mitigation.create({ data: { riskId, ownerDisplayName: payload.ownerDisplayName || payload.owner || actor.displayName, title: payload.title, description: payload.description || "", status: normalizeMitigationStatus(payload.status), progressPercent: Number(payload.progressPercent ?? payload.progress_percent ?? 0), effectivenessPercent: Number(payload.effectivenessPercent ?? payload.effectiveness_percent ?? 0), dueDate: payload.dueDate ? new Date(payload.dueDate) : null, sequence: Number(payload.sequence || 1) } }); if (payload.evidenceTitle || payload.evidenceUri) { await prisma.evidence.create({ data: { projectId: projectScope.project.id, riskId, mitigationId: mitigation.id, type: payload.evidenceType || "Review", title: payload.evidenceTitle || payload.evidenceUri, uri: payload.evidenceUri || null, createdById: actor.id } }); } await writeAuditEvent({ organizationId: projectScope.organization.id, workspaceId: projectScope.project.workspaceId, projectId: projectScope.project.id, actorUserId: actor.id, entityType: "Mitigation", entityId: mitigation.id, action: "create", afterJson: mitigation, request }); return mitigation; } async function getMitigation(mitigationId) { const mitigation = await prisma.mitigation.findUnique({ where: { id: mitigationId }, include: { evidence: true, risk: { select: { id: true, title: true, dimension: true, projectId: true } } } }); if (!mitigation) { const error = new Error(`Mitigation not found: ${mitigationId}`); error.status = 404; throw error; } return mitigation; } async function listProjectMitigations(projectId) { return prisma.mitigation.findMany({ where: { risk: { projectId } }, include: { evidence: true, risk: { select: { id: true, title: true, dimension: true } } }, orderBy: { createdAt: "desc" } }); } async function updateMitigation({ projectScope, mitigationId, actor, payload, request }) { const before = await prisma.mitigation.findUnique({ where: { id: mitigationId }, include: { risk: { select: { projectId: true } } } }); if (!before || before.risk.projectId !== projectScope.project.id) { const error = new Error(`Mitigation not found: ${mitigationId}`); error.status = 404; throw error; } const mitigation = await prisma.mitigation.update({ where: { id: mitigationId }, data: { title: payload.title ?? before.title, description: payload.description ?? before.description, ownerDisplayName: payload.ownerDisplayName ?? payload.owner ?? before.ownerDisplayName, status: payload.status !== undefined ? normalizeMitigationStatus(payload.status) : before.status, progressPercent: payload.progressPercent !== undefined ? Number(payload.progressPercent) : before.progressPercent, effectivenessPercent: payload.effectivenessPercent !== undefined ? Number(payload.effectivenessPercent) : before.effectivenessPercent, dueDate: payload.dueDate !== undefined ? payload.dueDate ? new Date(payload.dueDate) : null : before.dueDate, sequence: payload.sequence !== undefined ? Number(payload.sequence) : before.sequence } }); if (payload.evidenceTitle || payload.evidenceUri) { await prisma.evidence.create({ data: { projectId: projectScope.project.id, riskId: before.riskId, mitigationId: mitigation.id, type: payload.evidenceType || "Review", title: payload.evidenceTitle || payload.evidenceUri, uri: payload.evidenceUri || null, createdById: actor.id } }); } await writeAuditEvent({ organizationId: projectScope.organization.id, workspaceId: projectScope.project.workspaceId, projectId: projectScope.project.id, actorUserId: actor.id, entityType: "Mitigation", entityId: mitigation.id, action: "update", beforeJson: before, afterJson: mitigation, request }); return mitigation; } async function deleteMitigation({ projectScope, mitigationId, actor, request }) { const before = await prisma.mitigation.findUnique({ where: { id: mitigationId }, include: { risk: { select: { projectId: true } } } }); if (!before || before.risk.projectId !== projectScope.project.id) { const error = new Error(`Mitigation not found: ${mitigationId}`); error.status = 404; throw error; } await prisma.mitigation.delete({ where: { id: mitigationId } }); await writeAuditEvent({ organizationId: projectScope.organization.id, workspaceId: projectScope.project.workspaceId, projectId: projectScope.project.id, actorUserId: actor.id, entityType: "Mitigation", entityId: mitigationId, action: "delete", beforeJson: before, request }); return { deleted: true }; } async function addEvidenceToMitigation({ projectScope, mitigationId, actor, payload, request }) { const mitigation = await prisma.mitigation.findUnique({ where: { id: mitigationId }, include: { risk: { select: { projectId: true } } } }); if (!mitigation || mitigation.risk.projectId !== projectScope.project.id) { const error = new Error(`Mitigation not found: ${mitigationId}`); error.status = 404; throw error; } const evidence = await prisma.evidence.create({ data: { projectId: projectScope.project.id, riskId: mitigation.riskId, mitigationId: mitigation.id, type: payload.type || "Review", title: payload.title, uri: payload.uri || null, createdById: actor.id } }); await writeAuditEvent({ organizationId: projectScope.organization.id, workspaceId: projectScope.project.workspaceId, projectId: projectScope.project.id, actorUserId: actor.id, entityType: "Evidence", entityId: evidence.id, action: "create", afterJson: evidence, request }); return evidence; } module.exports = { listRisks, createRisk, updateRisk, deleteRisk, createMitigation, getMitigation, listProjectMitigations, updateMitigation, deleteMitigation, addEvidenceToMitigation, normalizeRiskStatus, normalizeApprovalStatus };