INSIGHT-MVP/packages/crm-service/src/visibility/visibility.controller.ts
Thomas Reitz de4af77c5c feat: CRM Berechtigungsmodell — konfigurierbares Sichtbarkeitsmodell (OWN/TEAM/ALL)
Implementiert pro-Entity Sichtbarkeitssteuerung für Companies, Contacts, Deals
und Activities mit Rollen-basierter Zugriffskontrolle (ADMIN sieht alles,
TEAM_LEAD mindestens Team-Sicht, READONLY nur Lesezugriff).

- JWT Payload um tenantRole + department erweitert (Core + CRM)
- Team-Members-Endpoint im Core Service (GET /users/team-members)
- VisibilityModule mit Redis-Cache (CRM Service)
- ReadonlyGuard als globaler Guard (CRM Service)
- buildVisibilityFilter Utility für Prisma WHERE-Filterung
- Admin-Einstellungsseite /admin/crm-settings (Frontend)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 22:20:53 +01:00

68 lines
2.1 KiB
TypeScript

import {
Controller,
Get,
Put,
Param,
Body,
UseGuards,
ForbiddenException,
} from '@nestjs/common';
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
import { TenantGuard } from '../auth/guards/tenant.guard';
import { CurrentUser } from '../common/decorators/current-user.decorator';
import { JwtPayload } from '../common/decorators/current-user.decorator';
import { VisibilityService } from './visibility.service';
import { SetVisibilityDto } from './dto/set-visibility.dto';
const VALID_ENTITIES = ['COMPANY', 'CONTACT', 'DEAL', 'ACTIVITY'];
@ApiTags('Visibility Settings')
@ApiBearerAuth()
@UseGuards(TenantGuard)
@Controller('visibility-settings')
export class VisibilityController {
constructor(private readonly visibilityService: VisibilityService) {}
/**
* Prueft ob der User Tenant-Admin ist (tenantRole oder Platform-Admin).
*/
private assertTenantAdmin(user: JwtPayload): void {
const isAdmin =
user.role === 'PLATFORM_ADMIN' ||
user.role === 'TENANT_ADMIN' ||
user.tenantRole === 'ADMIN';
if (!isAdmin) {
throw new ForbiddenException(
'Nur Tenant-Administratoren koennen Sichtbarkeitseinstellungen aendern.',
);
}
}
@Get()
@ApiOperation({ summary: 'Alle Sichtbarkeitseinstellungen abrufen' })
async getAll(@CurrentUser() user: JwtPayload) {
this.assertTenantAdmin(user);
const levels = await this.visibilityService.getAllLevels(user.tenantId!);
return { data: levels };
}
@Put(':entity')
@ApiOperation({ summary: 'Sichtbarkeitslevel fuer eine Entity setzen' })
async setLevel(
@CurrentUser() user: JwtPayload,
@Param('entity') entity: string,
@Body() dto: SetVisibilityDto,
) {
this.assertTenantAdmin(user);
const entityUpper = entity.toUpperCase();
if (!VALID_ENTITIES.includes(entityUpper)) {
throw new ForbiddenException(
`Ungueltige Entity: ${entity}. Erlaubt: ${VALID_ENTITIES.join(', ')}`,
);
}
await this.visibilityService.setLevel(user.tenantId!, entityUpper, dto.level);
return { data: { entity: entityUpper, level: dto.level } };
}
}