xpeditis2.0/apps/backend/src/application/controllers/invitations.controller.ts
David a1e255e816
Some checks failed
CI/CD Pipeline / Discord Notification (Failure) (push) Blocked by required conditions
CI/CD Pipeline / Integration Tests (push) Blocked by required conditions
CI/CD Pipeline / Deployment Summary (push) Blocked by required conditions
CI/CD Pipeline / Deploy to Portainer (push) Blocked by required conditions
CI/CD Pipeline / Discord Notification (Success) (push) Blocked by required conditions
CI/CD Pipeline / Backend - Build, Test & Push (push) Failing after 1m20s
CI/CD Pipeline / Frontend - Build, Test & Push (push) Has been cancelled
fix v1.0.0
2025-12-23 11:49:57 +01:00

181 lines
5.1 KiB
TypeScript

import {
Controller,
Post,
Get,
Body,
UseGuards,
HttpCode,
HttpStatus,
Logger,
Param,
} from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth, ApiParam } from '@nestjs/swagger';
import { InvitationService } from '../services/invitation.service';
import { CreateInvitationDto, InvitationResponseDto } from '../dto/invitation.dto';
import { JwtAuthGuard } from '../guards/jwt-auth.guard';
import { RolesGuard } from '../guards/roles.guard';
import { Roles } from '../decorators/roles.decorator';
import { CurrentUser, UserPayload } from '../decorators/current-user.decorator';
import { Public } from '../decorators/public.decorator';
import { UserRole } from '@domain/entities/user.entity';
/**
* Invitations Controller
*
* Handles user invitation endpoints:
* - POST /invitations - Create invitation (admin/manager)
* - GET /invitations/verify/:token - Verify invitation (public)
* - GET /invitations - List organization invitations (admin/manager)
*/
@ApiTags('Invitations')
@Controller('invitations')
export class InvitationsController {
private readonly logger = new Logger(InvitationsController.name);
constructor(private readonly invitationService: InvitationService) {}
/**
* Create invitation and send email
*/
@Post()
@HttpCode(HttpStatus.CREATED)
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin', 'manager')
@ApiBearerAuth()
@ApiOperation({
summary: 'Create invitation',
description: 'Send an invitation email to a new user. Admin/manager only.',
})
@ApiResponse({
status: 201,
description: 'Invitation created successfully',
type: InvitationResponseDto,
})
@ApiResponse({
status: 403,
description: 'Forbidden - requires admin or manager role',
})
@ApiResponse({
status: 409,
description: 'Conflict - user or active invitation already exists',
})
async createInvitation(
@Body() dto: CreateInvitationDto,
@CurrentUser() user: UserPayload
): Promise<InvitationResponseDto> {
this.logger.log(`[User: ${user.email}] Creating invitation for: ${dto.email}`);
const invitation = await this.invitationService.createInvitation(
dto.email,
dto.firstName,
dto.lastName,
dto.role as unknown as UserRole,
user.organizationId,
user.id
);
return {
id: invitation.id,
token: invitation.token,
email: invitation.email,
firstName: invitation.firstName,
lastName: invitation.lastName,
role: invitation.role,
organizationId: invitation.organizationId,
expiresAt: invitation.expiresAt,
isUsed: invitation.isUsed,
usedAt: invitation.usedAt,
createdAt: invitation.createdAt,
};
}
/**
* Verify invitation token
*/
@Get('verify/:token')
@Public()
@ApiOperation({
summary: 'Verify invitation token',
description: 'Check if an invitation token is valid and not expired. Public endpoint.',
})
@ApiParam({
name: 'token',
description: 'Invitation token',
example: 'abc123def456',
})
@ApiResponse({
status: 200,
description: 'Invitation is valid',
type: InvitationResponseDto,
})
@ApiResponse({
status: 404,
description: 'Invitation not found',
})
@ApiResponse({
status: 400,
description: 'Invitation expired or already used',
})
async verifyInvitation(@Param('token') token: string): Promise<InvitationResponseDto> {
this.logger.log(`Verifying invitation token: ${token}`);
const invitation = await this.invitationService.verifyInvitation(token);
return {
id: invitation.id,
token: invitation.token,
email: invitation.email,
firstName: invitation.firstName,
lastName: invitation.lastName,
role: invitation.role,
organizationId: invitation.organizationId,
expiresAt: invitation.expiresAt,
isUsed: invitation.isUsed,
usedAt: invitation.usedAt,
createdAt: invitation.createdAt,
};
}
/**
* List organization invitations
*/
@Get()
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin', 'manager')
@ApiBearerAuth()
@ApiOperation({
summary: 'List invitations',
description: 'Get all invitations for the current organization. Admin/manager only.',
})
@ApiResponse({
status: 200,
description: 'Invitations retrieved successfully',
type: [InvitationResponseDto],
})
@ApiResponse({
status: 403,
description: 'Forbidden - requires admin or manager role',
})
async listInvitations(@CurrentUser() user: UserPayload): Promise<InvitationResponseDto[]> {
this.logger.log(`[User: ${user.email}] Listing invitations for organization`);
const invitations = await this.invitationService.getOrganizationInvitations(
user.organizationId
);
return invitations.map(invitation => ({
id: invitation.id,
token: invitation.token,
email: invitation.email,
firstName: invitation.firstName,
lastName: invitation.lastName,
role: invitation.role,
organizationId: invitation.organizationId,
expiresAt: invitation.expiresAt,
isUsed: invitation.isUsed,
usedAt: invitation.usedAt,
createdAt: invitation.createdAt,
}));
}
}