/home/techb158/balavpn.abdallabala.com/src/services
Edit: /home/techb158/balavpn.abdallabala.com/src/services/gate-service.js (4512B)
const { prisma } = require("../lib/prisma");
const { getProjectAggregate } = require("./dashboard-service");
const { calculateDashboard } = require("../domain/risk-engine");
const { writeAuditEvent } = require("./audit-service");
function normalizeDecision(value) {
if (value === "Approved" || value === "APPROVED") return "APPROVED";
if (value === "Rejected" || value === "REJECTED") return "REJECTED";
if (value === "Accepted" || value === "ACCEPTED") return "ACCEPTED";
return "NEEDS_CHANGES";
}
async function evaluateAndPersistGate({ projectScope, actor, request }) {
const aggregate = await getProjectAggregate(projectScope.project.id);
const dashboard = calculateDashboard(aggregate);
const gate = await prisma.gateEvaluation.create({
data: {
projectId: projectScope.project.id,
status: dashboard.gate.status,
summary: dashboard.gate.message,
evaluatedByUserId: actor.id,
criteria: {
create: dashboard.gate.criteria.map(item => ({
name: item.name,
actualValue: String(item.actual),
expectedRule: item.required,
status: item.status,
blocking: item.blocking === true,
evidenceTitle: item.evidence
}))
}
},
include: { criteria: true, decisions: true }
});
await writeAuditEvent({
organizationId: projectScope.organization.id,
workspaceId: projectScope.project.workspaceId,
projectId: projectScope.project.id,
actorUserId: actor.id,
entityType: "GateEvaluation",
entityId: gate.id,
action: "evaluate",
afterJson: gate,
request
});
return gate;
}
async function addGateDecision({ projectScope, gateId, actor, payload, request }) {
const gate = await prisma.gateEvaluation.findUnique({ where: { id: gateId } });
if (!gate || gate.projectId !== projectScope.project.id) {
const error = new Error(`Gate not found: ${gateId}`);
error.status = 404;
throw error;
}
const decision = await prisma.gateDecision.create({
data: {
gateId,
decision: normalizeDecision(payload.decision),
reviewerName: payload.reviewerName || actor.displayName,
reason: payload.reason || "",
createdByUserId: actor.id
}
});
await prisma.gateEvaluation.update({
where: { id: gateId },
data: {
reviewStatus: decision.decision,
reviewedAt: new Date()
}
});
await writeAuditEvent({
organizationId: projectScope.organization.id,
workspaceId: projectScope.project.workspaceId,
projectId: projectScope.project.id,
actorUserId: actor.id,
entityType: "GateDecision",
entityId: decision.id,
action: "create",
afterJson: decision,
request
});
return decision;
}
async function updateGateCriterion({ projectScope, criterionId, actor, payload, request }) {
const criterion = await prisma.gateCriterion.findUnique({
where: { id: criterionId },
include: { gate: { select: { projectId: true } } }
});
if (!criterion || criterion.gate.projectId !== projectScope.project.id) {
const error = new Error(`Gate criterion not found: ${criterionId}`);
error.status = 404;
throw error;
}
const updated = await prisma.gateCriterion.update({
where: { id: criterionId },
data: {
reviewerStatus: payload.reviewerStatus ?? payload.status ?? criterion.reviewerStatus,
reviewerNote: payload.reviewerNote ?? payload.note ?? criterion.reviewerNote,
evidenceTitle: payload.evidenceTitle ?? criterion.evidenceTitle,
evidenceUri: payload.evidenceUri ?? criterion.evidenceUri
}
});
await writeAuditEvent({
organizationId: projectScope.organization.id,
workspaceId: projectScope.project.workspaceId,
projectId: projectScope.project.id,
actorUserId: actor.id,
entityType: "GateCriterion",
entityId: criterion.id,
action: "update",
beforeJson: criterion,
afterJson: updated,
request
});
return updated;
}
async function listGateHistory(projectId) {
return prisma.gateEvaluation.findMany({
where: { projectId },
include: { criteria: true, decisions: { orderBy: { decidedAt: "desc" } } },
orderBy: { evaluatedAt: "desc" }
});
}
async function listAuditEvents(projectId, limit = 80) {
return prisma.auditEvent.findMany({
where: { projectId },
orderBy: { createdAt: "desc" },
take: limit
});
}
module.exports = {
evaluateAndPersistGate,
addGateDecision,
updateGateCriterion,
listGateHistory,
listAuditEvents
};