import { ForbiddenException, Injectable, NotFoundException } from '@nestjs/common' import { ProjectsRepository } from './projects.repository' import { CreateProjectDto, UpdateProjectDto } from './dto/project.dto' import { OrgsService } from '../orgs/orgs.service' type OrgRole = 'org_admin' | 'project_manager' | 'viewer' @Injectable() export class ProjectsService { constructor( private readonly repo: ProjectsRepository, private readonly orgsService: OrgsService, ) {} private assertRole(role: OrgRole, allowed: OrgRole[]) { if (!allowed.includes(role)) { throw new ForbiddenException(`Role ${role} is not allowed for this action`) } } async create(dto: CreateProjectDto, userId: string) { const membership = await this.orgsService.ensureUserInOrg(userId, dto.orgId) this.assertRole(membership.role as OrgRole, ['org_admin', 'project_manager']) return this.repo.create(dto) } async findAll(userId: string, orgId?: string) { if (orgId) { await this.orgsService.ensureUserInOrg(userId, orgId) return this.repo.findAll(orgId) } const allowedOrgIds = await this.orgsService.getUserOrgIds(userId) if (allowedOrgIds.length === 0) return [] const all = await this.repo.findAll() return all.filter((p) => allowedOrgIds.includes(p.orgId)) } async findOne(id: string, userId: string) { const project = await this.repo.findById(id) if (!project) throw new NotFoundException(`Project ${id} not found`) await this.orgsService.ensureUserInOrg(userId, project.orgId) return project } async update(id: string, dto: UpdateProjectDto, userId: string) { const exists = await this.repo.findById(id) if (!exists) throw new NotFoundException(`Project ${id} not found`) const membership = await this.orgsService.ensureUserInOrg(userId, exists.orgId) this.assertRole(membership.role as OrgRole, ['org_admin', 'project_manager']) return this.repo.update(id, dto) } async remove(id: string, userId: string) { const exists = await this.repo.findById(id) if (!exists) throw new NotFoundException(`Project ${id} not found`) const membership = await this.orgsService.ensureUserInOrg(userId, exists.orgId) this.assertRole(membership.role as OrgRole, ['org_admin']) await this.repo.remove(id) return { success: true } } }