/home/techb158/balavpn.abdallabala.com/src/services
Edit: /home/techb158/balavpn.abdallabala.com/src/services/project-service.js (5973B)
const { prisma } = require("../lib/prisma");
const { buildEntitlements, assertWithinLimit } = require("../domain/entitlements");
const { writeAuditEvent } = require("./audit-service");
async function getLatestSubscription(organizationId) {
return prisma.subscription.findFirst({
where: { organizationId },
orderBy: { createdAt: "desc" }
});
}
async function listWorkspaceProjects(workspaceId) {
return prisma.project.findMany({
where: { workspaceId },
orderBy: { createdAt: "desc" }
});
}
async function createProject({ workspace, actor, payload, request }) {
const subscription = await getLatestSubscription(workspace.organizationId);
const organization = await prisma.organization.findUnique({ where: { id: workspace.organizationId } });
const entitlements = buildEntitlements(subscription, organization);
const currentCount = await prisma.project.count({ where: { workspace: { organizationId: workspace.organizationId } } });
assertWithinLimit(entitlements, "projects", currentCount, 1);
const project = await prisma.project.create({
data: {
workspaceId: workspace.id,
name: payload.name,
subtitle: payload.subtitle || "COSMIC AI risk measurement workspace",
projectType: payload.projectType || "AI-Enabler",
currentLifecyclePhase: payload.currentLifecyclePhase || "Needs identification",
riskAppetite: Number(payload.riskAppetite || 50),
assessmentDate: payload.assessmentDate ? new Date(payload.assessmentDate) : new Date(),
status: "Active",
ownerDisplayName: payload.ownerDisplayName || actor.displayName,
description: payload.description || "",
thresholds: payload.thresholds || {},
reviews: payload.reviews || {}
}
});
await writeAuditEvent({
organizationId: workspace.organizationId,
workspaceId: workspace.id,
projectId: project.id,
actorUserId: actor.id,
entityType: "Project",
entityId: project.id,
action: "create",
afterJson: project,
request
});
return project;
}
async function getProject(projectId) {
const project = await prisma.project.findUnique({
where: { id: projectId },
include: {
lifecyclePhases: { orderBy: { sequence: "asc" } },
risks: { include: { mitigations: true, evidence: true } },
indicators: true,
experiments: { include: { metrics: true } }
}
});
if (!project) {
const error = new Error(`Project not found: ${projectId}`);
error.status = 404;
throw error;
}
return project;
}
async function updateProject({ projectScope, actor, payload, request }) {
const before = await prisma.project.findUnique({ where: { id: projectScope.project.id } });
const project = await prisma.project.update({
where: { id: projectScope.project.id },
data: {
name: payload.name ?? before.name,
subtitle: payload.subtitle ?? before.subtitle,
projectType: payload.projectType ?? before.projectType,
currentLifecyclePhase: payload.currentLifecyclePhase ?? before.currentLifecyclePhase,
riskAppetite: payload.riskAppetite !== undefined ? Number(payload.riskAppetite) : before.riskAppetite,
status: payload.status ?? before.status,
ownerDisplayName: payload.ownerDisplayName ?? before.ownerDisplayName,
description: payload.description ?? before.description,
thresholds: payload.thresholds ?? before.thresholds,
reviews: payload.reviews ?? before.reviews
}
});
await writeAuditEvent({
organizationId: projectScope.organization.id,
workspaceId: projectScope.project.workspaceId,
projectId: projectScope.project.id,
actorUserId: actor.id,
entityType: "Project",
entityId: project.id,
action: "update",
beforeJson: before,
afterJson: project,
request
});
return project;
}
async function deleteProject({ projectScope, projectId, actor, request }) {
const before = await prisma.project.findUnique({ where: { id: projectId } });
if (!before) {
const error = new Error(`Project not found: ${projectId}`);
error.status = 404;
throw error;
}
await writeAuditEvent({
organizationId: projectScope.organization.id,
workspaceId: projectScope.project.workspaceId,
projectId,
actorUserId: actor.id,
entityType: "Project",
entityId: projectId,
action: "delete",
beforeJson: before,
request
}).catch(() => {});
const expIds = (await prisma.experiment.findMany({ where: { projectId }, select: { id: true } })).map(e => e.id);
await prisma.modelMetric.deleteMany({ where: { experimentId: { in: expIds } } });
await prisma.auditEvent.deleteMany({ where: { projectId } });
await prisma.reportExport.deleteMany({ where: { projectId } });
await prisma.externalWorkItemMapping.deleteMany({ where: { projectId } });
await prisma.integrationSyncRun.deleteMany({ where: { projectId } });
const gateIds = (await prisma.gateEvaluation.findMany({ where: { projectId }, select: { id: true } })).map(g => g.id);
await prisma.gateDecision.deleteMany({ where: { gateId: { in: gateIds } } });
await prisma.gateCriterion.deleteMany({ where: { gateId: { in: gateIds } } });
await prisma.gateEvaluation.deleteMany({ where: { projectId } });
await prisma.evidence.deleteMany({ where: { projectId } });
const riskIds = (await prisma.risk.findMany({ where: { projectId }, select: { id: true } })).map(r => r.id);
await prisma.mitigation.deleteMany({ where: { riskId: { in: riskIds } } });
await prisma.risk.deleteMany({ where: { projectId } });
await prisma.experiment.deleteMany({ where: { projectId } });
await prisma.lifecyclePhase.deleteMany({ where: { projectId } });
await prisma.indicator.deleteMany({ where: { projectId } });
await prisma.project.delete({ where: { id: projectId } });
return { deleted: true };
}
module.exports = {
getLatestSubscription,
listWorkspaceProjects,
createProject,
getProject,
updateProject,
deleteProject
};