import { CanActivate, ExecutionContext, ForbiddenException, Injectable } from '@nestjs/common'; import { parseUserTreasuryAccess, treasurySectionAllowed, type TreasurySectionKey, } from '@mento/shared'; import type { Actor } from '../common/record-scope'; import { PrismaService } from '../prisma/prisma.service'; /** * حقوق فقط با treasury_payroll. * بقیه مسیرها با treasury باز می‌شوند؛ اگر بخش ریز انتخاب شده باشد فقط همان‌ها. */ @Injectable() export class TreasuryAccessGuard implements CanActivate { constructor(private readonly prisma: PrismaService) {} async canActivate(context: ExecutionContext): Promise { const req = context.switchToHttp().getRequest<{ user?: Actor; originalUrl?: string; url?: string }>(); const actor = req.user; if (!actor) throw new ForbiddenException('دسترسی مالی مجاز نیست.'); if (actor.role === 'owner' || actor.role === 'admin') return true; const membership = await this.prisma.membership.findFirst({ where: { userId: actor.userId, organizationId: actor.organizationId, isActive: true }, select: { orgRole: { select: { permissions: true } }, treasuryAccessOverride: true, }, }); const rolePermissions = Array.isArray(membership?.orgRole?.permissions) ? membership.orgRole.permissions.map(String) : []; const permissions = permissionsForTreasury( rolePermissions, parseUserTreasuryAccess(membership?.treasuryAccessOverride), ); const url = req.originalUrl || req.url || ''; if (!treasurySectionAllowed(actor.role, permissions, sectionForUrl(url))) { throw new ForbiddenException('به این بخش مدیریت مالی دسترسی ندارید.'); } return true; } } function permissionsForTreasury( rolePermissions: string[], override: ReturnType, ): string[] { if (!override) return rolePermissions; const rest = rolePermissions.filter((key) => key !== 'treasury' && !key.startsWith('treasury_')); if (!override.enabled) return rest; return ['treasury', ...override.sections, ...rest]; } function sectionForUrl(url: string): TreasurySectionKey | 'treasury_documents' { if (url.includes('/treasury/payroll')) return 'treasury_payroll'; if (url.includes('/treasury/receivables') || url.includes('/treasury/installments')) { return 'treasury_receivables'; } if (url.includes('/treasury/recurring')) return 'treasury_recurring'; if (url.includes('/treasury/reports')) return 'treasury_reports'; if (url.includes('/treasury/audit')) return 'treasury_audit'; if (url.includes('/treasury/categories')) return 'treasury_categories'; if ( url.includes('/treasury/accounts') || url.includes('/treasury/transfers') || url.includes('/treasury/reconciliations') || url.includes('/treasury/adjustments') ) { return 'treasury_accounts'; } if (url.includes('/treasury/dashboard')) return 'treasury_dashboard'; return 'treasury_documents'; }