refactor(controllers): added ModuleAccessAllowed and Access decorators

This commit is contained in:
Matthieu Haineault 2025-12-01 16:03:48 -05:00
parent 0f509a920f
commit ebc1cd77d8
15 changed files with 156 additions and 169 deletions

View File

@ -463,6 +463,20 @@
] ]
} }
}, },
"/employees/personal-profile": {
"get": {
"operationId": "EmployeesController_findOwnProfile",
"parameters": [],
"responses": {
"200": {
"description": ""
}
},
"tags": [
"Employees"
]
}
},
"/employees/profile": { "/employees/profile": {
"get": { "get": {
"operationId": "EmployeesController_findProfile", "operationId": "EmployeesController_findProfile",

View File

@ -381,6 +381,17 @@ enum Roles {
@@map("roles") @@map("roles")
} }
enum Modules {
timesheets
timesheets_approval
employee_list
employee_management
personal_profile
dashboard
@@map("modules")
}
enum LeaveTypes { enum LeaveTypes {
SICK // maladie ou repos SICK // maladie ou repos
VACATION // paye VACATION // paye

View File

@ -0,0 +1,8 @@
import { SetMetadata } from '@nestjs/common';
import { Modules } from 'src/common/mappers/module-access.mapper';
export const MODULES_KEY = 'modules';
export const ModuleAccessAllowed = (...modules: Modules[]) =>
SetMetadata(MODULES_KEY, modules);

View File

@ -1,8 +0,0 @@
import { SetMetadata } from '@nestjs/common';
import { Roles } from '@prisma/client';
export const ROLES_KEY = 'roles';
export const RolesAllowed = (...roles: Roles[]) =>
SetMetadata(ROLES_KEY, roles);

View File

@ -0,0 +1,43 @@
import {
CanActivate,
ExecutionContext,
ForbiddenException,
Injectable,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { MODULES_KEY } from '../decorators/modules-guard.decorators';
import { Modules, Roles } from '.prisma/client';
interface RequestWithUser extends Request {
// TODO: Create an actual user model based on OAuth signin
user: any;
}
@Injectable()
export class ModulesGuard implements CanActivate {
constructor(private reflector: Reflector) { }
canActivate(ctx: ExecutionContext): boolean {
const requiredModules = this.reflector.getAllAndOverride<Modules[]>(
MODULES_KEY,
[ctx.getHandler(), ctx.getClass()],
);
//for "deny-by-default" when role is wrong or unavailable
if (!requiredModules || requiredModules.length === 0) {
return true;
}
const request = ctx.switchToHttp().getRequest<RequestWithUser>();
const user = request.user;
if (!user) {
return false;
}
if (!requiredModules.includes(user.role)) {
throw new ForbiddenException(
`The role ${user.role} is not authorized to access this resource.`,
);
}
return true;
}
}

View File

@ -1,61 +0,0 @@
import {
CanActivate,
ExecutionContext,
ForbiddenException,
Injectable,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ROLES_KEY } from '../decorators/roles.decorators';
import { Roles } from '.prisma/client';
interface RequestWithUser extends Request {
// TODO: Create an actual user model based on OAuth signin
user: any;
}
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) { }
/**
* @swagger
* @function canActivate
* @description
* Authorization guard that checks whether the current user has one of the required roles
* to access a specific route handler. It uses metadata defined by the `@Roles()` decorator
* and verifies the user's role accordingly.
*
* If no roles are specified for the route, access is granted by default.
* If the user is not authenticated or does not have a required role, access is denied.
*
* @param {ExecutionContext} ctx - The current execution context, which provides access
* to route metadata and the HTTP request.
*
* @returns {boolean} - Returns `true` if access is allowed, otherwise throws a `ForbiddenException`
* or returns `false` if the user is not authenticated.
*/
canActivate(ctx: ExecutionContext): boolean {
const requiredRoles = this.reflector.getAllAndOverride<Roles[]>(
ROLES_KEY,
[ctx.getHandler(), ctx.getClass()],
);
//for "deny-by-default" when role is wrong or unavailable
if (!requiredRoles || requiredRoles.length === 0) {
return true;
}
const request = ctx.switchToHttp().getRequest<RequestWithUser>();
const user = request.user;
if (!user) {
return false;
}
if (!requiredRoles.includes(user.role)) {
throw new ForbiddenException(
`The role ${user.role} is not authorized to access this resource.`,
);
}
return true;
}
}

View File

@ -4,46 +4,37 @@ import { Result } from "src/common/errors/result-error.factory";
import { EmployeeDetailedDto } from "src/identity-and-account/employees/dtos/employee-detailed.dto"; import { EmployeeDetailedDto } from "src/identity-and-account/employees/dtos/employee-detailed.dto";
import { EmployeeDto } from "src/identity-and-account/employees/dtos/employee.dto"; import { EmployeeDto } from "src/identity-and-account/employees/dtos/employee.dto";
import { EmployeesService } from "src/identity-and-account/employees/services/employees.service"; import { EmployeesService } from "src/identity-and-account/employees/services/employees.service";
import { AccessGetService } from "src/identity-and-account/user-module-access/services/module-access-get.service"; import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators";
import { Modules as ModulesEnum } from ".prisma/client";
//TODO: create a custom decorator to replace the findModuleAcces call function //TODO: create a custom decorator to replace the findModuleAcces call function
@Controller('employees') @Controller('employees')
export class EmployeesController { export class EmployeesController {
constructor( constructor(private readonly employeesService: EmployeesService) { }
private readonly employeesService: EmployeesService,
private readonly accessGetService: AccessGetService, @Get('personal-profile')
) { } @ModuleAccessAllowed(ModulesEnum.personal_profile)
async findOwnProfile(@Access('email') email: string): Promise<Result<Partial<EmployeeDetailedDto>, string>> {
return await this.employeesService.findOwnProfile(email);
}
@Get('profile') @Get('profile')
@ModuleAccessAllowed(ModulesEnum.employee_management)
async findProfile(@Access('email') email: string, @Query('employee_email') employee_email?: string, async findProfile(@Access('email') email: string, @Query('employee_email') employee_email?: string,
): Promise<Result<Partial<EmployeeDetailedDto>, string>> { ): Promise<Result<Partial<EmployeeDetailedDto>, string>> {
const granted_access = await this.accessGetService.findModuleAccess(email); return await this.employeesService.findOneDetailedProfile(email, employee_email);
if (!granted_access.success) return { success: false, error: 'INVALID_USER' };
if (!granted_access.data.employee_management) {
return await this.employeesService.findOwnProfile(email);
} else if (granted_access.data.employee_management) {
return await this.employeesService.findOneDetailedProfile(email,employee_email);
} else {
return { success: false, error: 'INVALID_USER' }
}
} }
@Get('employee-list') @Get('employee-list')
async findListEmployees(@Access('email') email: string @ModuleAccessAllowed(ModulesEnum.employee_list)
): Promise<Result<EmployeeDto[], string>> { async findListEmployees(): Promise<Result<EmployeeDto[], string>> {
const granted_access = await this.accessGetService.findModuleAccess(email);
if (!granted_access.success) return { success: false, error: 'INVALID_USER' };
if (!granted_access.data.employee_management) return { success: false, error: 'UNAUTHORIZED_ACCESS' };
return this.employeesService.findListEmployees(); return this.employeesService.findListEmployees();
} }
@Post() @Post()
async createEmployee(@Access('email') email: string, @Body() dto: EmployeeDetailedDto @ModuleAccessAllowed(ModulesEnum.employee_management)
): Promise<Result<boolean, string>> { async createEmployee(@Body() dto: EmployeeDetailedDto): Promise<Result<boolean, string>> {
const granted_access = await this.accessGetService.findModuleAccess(email);
if (!granted_access.success) return { success: false, error: 'INVALID_USER' };
if (!granted_access.data.employee_management) return { success: false, error: 'UNAUTHORIZED_ACCESS' };
return await this.employeesService.createEmployee(dto); return await this.employeesService.createEmployee(dto);
} }

View File

@ -1,18 +1,15 @@
import 'reflect-metadata'; import 'reflect-metadata';
//import and if case for @nestjs/schedule Cron jobs
import * as nodeCrypto from 'crypto'; import * as nodeCrypto from 'crypto';
if (!(globalThis as any).crypto) { if (!(globalThis as any).crypto) {
(globalThis as any).crypto = nodeCrypto; (globalThis as any).crypto = nodeCrypto;
} }
import { ensureAttachmentsTmpDir } from './config/attachment.fs'; import { ensureAttachmentsTmpDir } from './config/attachment.fs';
import { resolveAttachmentsRoot } from './config/attachment.config';// log to be removed post dev import { resolveAttachmentsRoot } from './config/attachment.config';// log to be removed post dev
import { ATT_TMP_DIR } from './config/attachment.config'; // log to be removed post dev import { ATT_TMP_DIR } from './config/attachment.config'; // log to be removed post dev
import { NestFactory, Reflector } from '@nestjs/core';
import { ModuleRef, NestFactory, Reflector } from '@nestjs/core';
import { AppModule } from './app.module'; import { AppModule } from './app.module';
// import { JwtAuthGuard } from './modules/authentication/guards/jwt-auth.guard'; // import { JwtAuthGuard } from './modules/authentication/guards/jwt-auth.guard';
import { RolesGuard } from './common/guards/roles.guard'; import { ModulesGuard } from './common/guards/modules.guard';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { writeFileSync } from 'fs'; import { writeFileSync } from 'fs';
import * as session from 'express-session'; import * as session from 'express-session';
@ -26,11 +23,11 @@ const SESSION_TOKEN_DURATION_MINUTES = 180
async function bootstrap() { async function bootstrap() {
const app = await NestFactory.create(AppModule); const app = await NestFactory.create(AppModule);
const reflector = app.get(Reflector); //setup Reflector for Roles() const reflector = app.get(Reflector);
app.useGlobalGuards( app.useGlobalGuards(
// new JwtAuthGuard(reflector), //Authentification JWT // new JwtAuthGuard(reflector), //Authentification JWT
new RolesGuard(reflector), //deny-by-default and Role-based Access Control new ModulesGuard(reflector), //deny-by-default and Module-based Access Control
); );
// Authentication and session // Authentication and session

View File

@ -1,11 +1,11 @@
import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from "@nestjs/common"; import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from "@nestjs/common";
import { BankCodesService } from "../services/bank-codes.service"; import { BankCodesService } from "../services/bank-codes.service";
import { BankCodeDto } from "../dtos/bank-code.dto"; import { BankCodeDto } from "../dtos/bank-code.dto";
import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators";
import { PAY_SERVICE } from "src/common/shared/role-groupes"; import { Modules as ModulesEnum } from ".prisma/client";
@Controller('bank-codes') @Controller('bank-codes')
@RolesAllowed(...PAY_SERVICE) @ModuleAccessAllowed(ModulesEnum.employee_management)
export class BankCodesControllers { export class BankCodesControllers {
constructor(private readonly bankCodesService: BankCodesService) {} constructor(private readonly bankCodesService: BankCodesService) {}
//_____________________________________________________________________________________________ //_____________________________________________________________________________________________

View File

@ -1,16 +1,15 @@
import { Controller, Get, Header, Query} from "@nestjs/common"; import { Controller, Get, Header, Query} from "@nestjs/common";
import { CsvExportService } from "../services/csv-exports.service"; import { CsvExportService } from "../services/csv-exports.service";
import { ExportCsvOptionsDto } from "../dtos/export-csv-options.dto"; import { ExportCsvOptionsDto } from "../dtos/export-csv-options.dto";
import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators";
import { PAY_SERVICE } from "src/common/shared/role-groupes"; import { Modules as ModulesEnum } from ".prisma/client";
@Controller('exports') @Controller('exports')
@RolesAllowed(...PAY_SERVICE)
export class CsvExportController { export class CsvExportController {
constructor(private readonly csvService: CsvExportService) {} constructor(private readonly csvService: CsvExportService) {}
@Get('csv') @Get('csv')
@ModuleAccessAllowed(ModulesEnum.employee_management)
@Header('Content-Type', 'text/csv; charset=utf-8') @Header('Content-Type', 'text/csv; charset=utf-8')
@Header('Content-Disposition', 'attachment; filename="export.csv"') @Header('Content-Disposition', 'attachment; filename="export.csv"')
async exportCsv(@Query() query: ExportCsvOptionsDto ): Promise<Buffer> { async exportCsv(@Query() query: ExportCsvOptionsDto ): Promise<Buffer> {

View File

@ -1,29 +1,30 @@
import { Controller, Post, Param, Body, Patch, Delete, Req, UnauthorizedException } from "@nestjs/common"; import { Controller, Post, Param, Body, Patch, Delete, Req, UnauthorizedException } from "@nestjs/common";
import { ExpenseUpsertService } from "src/time-and-attendance/expenses/services/expense-upsert.service"; import { ExpenseUpsertService } from "src/time-and-attendance/expenses/services/expense-upsert.service";
import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto"; import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto";
import { RolesAllowed } from "src/common/decorators/roles.decorators";
import { GLOBAL_CONTROLLER_ROLES } from "src/common/shared/role-groupes";
import { Result } from "src/common/errors/result-error.factory"; import { Result } from "src/common/errors/result-error.factory";
import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators";
import { Modules as ModulesEnum } from ".prisma/client";
import { Access } from "src/common/decorators/module-access.decorators";
@Controller('expense') @Controller('expense')
@RolesAllowed(...GLOBAL_CONTROLLER_ROLES)
export class ExpenseController { export class ExpenseController {
constructor(private readonly upsert_service: ExpenseUpsertService) { } constructor(private readonly upsert_service: ExpenseUpsertService) { }
@Post('create') @Post('create')
create(@Req() req, @Body() dto: ExpenseDto): Promise<Result<ExpenseDto, string>> { @ModuleAccessAllowed(ModulesEnum.timesheets)
const email = req.user?.email; create(@Access('email') email: string, @Body() dto: ExpenseDto): Promise<Result<ExpenseDto, string>> {
if (!email) throw new UnauthorizedException('Unauthorized User'); if (!email) throw new UnauthorizedException('Unauthorized User');
return this.upsert_service.createExpense(dto, email); return this.upsert_service.createExpense(dto, email);
} }
@Patch('update') @Patch('update')
update(@Body() dto: ExpenseDto, @Req() req): Promise<Result<ExpenseDto, string>> { @ModuleAccessAllowed(ModulesEnum.timesheets)
const email = req.user?.email; update(@Body() dto: ExpenseDto, @Access('email') email: string): Promise<Result<ExpenseDto, string>> {
return this.upsert_service.updateExpense(dto, email); return this.upsert_service.updateExpense(dto, email);
} }
@Delete('delete/:expense_id') @Delete('delete/:expense_id')
@ModuleAccessAllowed(ModulesEnum.timesheets)
remove(@Param('expense_id') expense_id: number): Promise<Result<number, string>> { remove(@Param('expense_id') expense_id: number): Promise<Result<number, string>> {
return this.upsert_service.deleteExpense(expense_id); return this.upsert_service.deleteExpense(expense_id);
} }

View File

@ -1,15 +1,15 @@
import { Body, Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Query, Req, UnauthorizedException } from "@nestjs/common"; import { Body, Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Query, UnauthorizedException } from "@nestjs/common";
import { PayPeriodOverviewDto } from "../dtos/overview-pay-period.dto"; import { PayPeriodOverviewDto } from "../dtos/overview-pay-period.dto";
import { PayPeriodsQueryService } from "../services/pay-periods-query.service"; import { PayPeriodsQueryService } from "../services/pay-periods-query.service";
import { RolesAllowed } from "src/common/decorators/roles.decorators";
import { PayPeriodsCommandService } from "../services/pay-periods-command.service"; import { PayPeriodsCommandService } from "../services/pay-periods-command.service";
import { PayPeriodBundleDto } from "../dtos/bundle-pay-period.dto"; import { PayPeriodBundleDto } from "../dtos/bundle-pay-period.dto";
import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto"; import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto";
import { GLOBAL_CONTROLLER_ROLES, MANAGER_ROLES } from "src/common/shared/role-groupes";
import { Result } from "src/common/errors/result-error.factory"; import { Result } from "src/common/errors/result-error.factory";
import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators";
import { Modules as ModulesEnum } from ".prisma/client";
import { Access } from "src/common/decorators/module-access.decorators";
@Controller('pay-periods') @Controller('pay-periods')
@RolesAllowed(...GLOBAL_CONTROLLER_ROLES)
export class PayPeriodsController { export class PayPeriodsController {
constructor( constructor(
@ -18,6 +18,7 @@ export class PayPeriodsController {
) { } ) { }
@Get('current-and-all') @Get('current-and-all')
@ModuleAccessAllowed(ModulesEnum.timesheets_approval)
async getCurrentAndAll(@Query('date') date?: string): Promise<Result<PayPeriodBundleDto, string>> { async getCurrentAndAll(@Query('date') date?: string): Promise<Result<PayPeriodBundleDto, string>> {
const current = await this.queryService.findCurrent(date); const current = await this.queryService.findCurrent(date);
if (!current.success) return { success: false, error: 'INVALID_PAY_PERIOD' }; if (!current.success) return { success: false, error: 'INVALID_PAY_PERIOD' };
@ -29,11 +30,13 @@ export class PayPeriodsController {
} }
@Get("date/:date") @Get("date/:date")
@ModuleAccessAllowed(ModulesEnum.timesheets_approval)
async findByDate(@Param("date") date: string) { async findByDate(@Param("date") date: string) {
return this.queryService.findByDate(date); return this.queryService.findByDate(date);
} }
@Get(":year/:periodNumber") @Get(":year/:periodNumber")
@ModuleAccessAllowed(ModulesEnum.timesheets_approval)
async findOneByYear( async findOneByYear(
@Param("year", ParseIntPipe) year: number, @Param("year", ParseIntPipe) year: number,
@Param("periodNumber", ParseIntPipe) period_no: number, @Param("periodNumber", ParseIntPipe) period_no: number,
@ -42,26 +45,25 @@ export class PayPeriodsController {
} }
@Patch("crew/pay-period-approval") @Patch("crew/pay-period-approval")
@RolesAllowed(...MANAGER_ROLES) @ModuleAccessAllowed(ModulesEnum.timesheets_approval)
async bulkApproval(@Req() req, @Body() dto: BulkCrewApprovalDto) { async bulkApproval(@Access('email') email:string, @Body() dto: BulkCrewApprovalDto) {
const email = req.user?.email;
if (!email) throw new UnauthorizedException(`Session infos not found`); if (!email) throw new UnauthorizedException(`Session infos not found`);
return this.commandService.bulkApproveCrew(email, dto); return this.commandService.bulkApproveCrew(email, dto);
} }
@Get('crew/:year/:periodNumber') @Get('crew/:year/:periodNumber')
@RolesAllowed(...MANAGER_ROLES) @ModuleAccessAllowed(ModulesEnum.timesheets_approval)
async getCrewOverview(@Req() req, async getCrewOverview(@Access('email') email:string,
@Param('year', ParseIntPipe) year: number, @Param('year', ParseIntPipe) year: number,
@Param('periodNumber', ParseIntPipe) period_no: number, @Param('periodNumber', ParseIntPipe) period_no: number,
@Query('includeSubtree', new ParseBoolPipe({ optional: true })) include_subtree = false, @Query('includeSubtree', new ParseBoolPipe({ optional: true })) include_subtree = false,
): Promise<Result<PayPeriodOverviewDto, string>> { ): Promise<Result<PayPeriodOverviewDto, string>> {
const email = req.user?.email;
if (!email) throw new UnauthorizedException(`Session infos not found`); if (!email) throw new UnauthorizedException(`Session infos not found`);
return this.queryService.getCrewOverview(year, period_no, email, include_subtree); return this.queryService.getCrewOverview(year, period_no, email, include_subtree);
} }
@Get('overview/:year/:periodNumber') @Get('overview/:year/:periodNumber')
@ModuleAccessAllowed(ModulesEnum.timesheets_approval)
async getOverviewByYear( async getOverviewByYear(
@Param('year', ParseIntPipe) year: number, @Param('year', ParseIntPipe) year: number,
@Param('periodNumber', ParseIntPipe) period_no: number, @Param('periodNumber', ParseIntPipe) period_no: number,

View File

@ -1,14 +1,14 @@
import { Controller, Param, Query, Body, Get, Post, ParseIntPipe, Delete, Patch, Req } from "@nestjs/common"; import { Controller, Param, Body, Get, Post, ParseIntPipe, Delete, Patch } from "@nestjs/common";
import { RolesAllowed } from "src/common/decorators/roles.decorators";
import { GLOBAL_CONTROLLER_ROLES, MANAGER_ROLES } from "src/common/shared/role-groupes";
import { SchedulePresetsDto } from "src/time-and-attendance/schedule-presets/dtos/create-schedule-presets.dto"; import { SchedulePresetsDto } from "src/time-and-attendance/schedule-presets/dtos/create-schedule-presets.dto";
import { SchedulePresetsApplyService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-apply.service"; import { SchedulePresetsApplyService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-apply.service";
import { SchedulePresetsGetService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-get.service"; import { SchedulePresetsGetService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-get.service";
import { SchedulePresetsCreateService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-create.service"; import { SchedulePresetsCreateService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-create.service";
import { SchedulePresetUpdateDeleteService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-update-delete.service"; import { SchedulePresetUpdateDeleteService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-update-delete.service";
import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators";
import { Modules as ModulesEnum } from ".prisma/client";
import { Access } from "src/common/decorators/module-access.decorators";
@Controller('schedule-presets') @Controller('schedule-presets')
@RolesAllowed(...GLOBAL_CONTROLLER_ROLES)
export class SchedulePresetsController { export class SchedulePresetsController {
constructor( constructor(
private readonly createService: SchedulePresetsCreateService, private readonly createService: SchedulePresetsCreateService,
@ -19,48 +19,39 @@ export class SchedulePresetsController {
// used to create a schedule preset // used to create a schedule preset
@Post('create') @Post('create')
@RolesAllowed(...MANAGER_ROLES) @ModuleAccessAllowed(ModulesEnum.employee_management)
async createPreset(@Req() req, @Body() dto: SchedulePresetsDto) { async createPreset(@Access('email') email: string, @Body() dto: SchedulePresetsDto) {
const email = req.user?.email;
return await this.createService.createPreset(email, dto); return await this.createService.createPreset(email, dto);
} }
//used to update an already existing schedule preset //used to update an already existing schedule preset
@Patch('update/:preset_id') @Patch('update/:preset_id')
@RolesAllowed(...MANAGER_ROLES) @ModuleAccessAllowed(ModulesEnum.employee_management)
async updatePreset( async updatePreset(
@Param('preset_id', ParseIntPipe) preset_id: number, @Param('preset_id', ParseIntPipe) preset_id: number, @Body() dto: SchedulePresetsDto, @Access('email') email: string) {
@Body() dto: SchedulePresetsDto,
@Req() req,
) {
const email = req.user?.email;
return await this.updateDeleteService.updatePreset(preset_id, dto, email); return await this.updateDeleteService.updatePreset(preset_id, dto, email);
} }
//used to delete a schedule preset //used to delete a schedule preset
@Delete('delete/:preset_id') @Delete('delete/:preset_id')
@RolesAllowed(...MANAGER_ROLES) @ModuleAccessAllowed(ModulesEnum.employee_management)
async deletePreset(@Param('preset_id', ParseIntPipe) preset_id: number, @Req() req) { async deletePreset(
const email = req.user?.email; @Param('preset_id', ParseIntPipe) preset_id: number, @Access('email') email: string) {
return await this.updateDeleteService.deletePreset(preset_id, email); return await this.updateDeleteService.deletePreset(preset_id, email);
} }
//used to show the list of available schedule presets //used to show the list of available schedule presets
@Get('find-list') @Get('find-list')
@RolesAllowed(...MANAGER_ROLES) @ModuleAccessAllowed(ModulesEnum.employee_management)
async findListById(@Req() req) { async findListById(@Access('email') email: string) {
const email = req.user?.email;
return this.getService.getSchedulePresets(email); return this.getService.getSchedulePresets(email);
} }
//used to apply a preset to a timesheet //used to apply a preset to a timesheet
@Post('apply-presets') @Post('apply-presets')
@ModuleAccessAllowed(ModulesEnum.timesheets)
async applyPresets( async applyPresets(
@Body('preset') preset_id: number, @Body('preset') preset_id: number, @Body('start') start_date: string, @Access('email') email: string) {
@Body('start') start_date: string,
@Req() req
) {
const email = req.user?.email;
return this.applyPresetsService.applyToTimesheet(email, preset_id, start_date); return this.applyPresetsService.applyToTimesheet(email, preset_id, start_date);
} }
} }

View File

@ -1,34 +1,34 @@
import { Body, Controller, Delete, Param, Patch, Post, Req } from "@nestjs/common"; import { Body, Controller, Delete, Param, Patch, Post } from "@nestjs/common";
import { RolesAllowed } from "src/common/decorators/roles.decorators";
import { GLOBAL_CONTROLLER_ROLES } from "src/common/shared/role-groupes";
import { Result } from "src/common/errors/result-error.factory"; import { Result } from "src/common/errors/result-error.factory";
import { ShiftDto } from "src/time-and-attendance/shifts/dtos/shift-create.dto"; import { ShiftDto } from "src/time-and-attendance/shifts/dtos/shift-create.dto";
import { ShiftsCreateService } from "src/time-and-attendance/shifts/services/shifts-create.service"; import { ShiftsCreateService } from "src/time-and-attendance/shifts/services/shifts-create.service";
import { ShiftsUpdateDeleteService } from "src/time-and-attendance/shifts/services/shifts-update-delete.service"; import { ShiftsUpdateDeleteService } from "src/time-and-attendance/shifts/services/shifts-update-delete.service";
import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators";
import { Modules as ModulesEnum } from ".prisma/client";
import { Access } from "src/common/decorators/module-access.decorators";
@Controller('shift') @Controller('shift')
@RolesAllowed(...GLOBAL_CONTROLLER_ROLES)
export class ShiftController { export class ShiftController {
constructor( constructor(
private readonly create_service: ShiftsCreateService, private readonly create_service: ShiftsCreateService,
private readonly update_delete_service: ShiftsUpdateDeleteService, private readonly update_delete_service: ShiftsUpdateDeleteService,
){} ) { }
@Post('create') @Post('create')
createBatch( @Req() req, @Body()dtos: ShiftDto[]): Promise<Result<ShiftDto[], string>> { @ModuleAccessAllowed(ModulesEnum.timesheets)
const email = req.user?.email; createBatch(@Access('email') email: string, @Body() dtos: ShiftDto[]): Promise<Result<ShiftDto[], string>> {
return this.create_service.createOneOrManyShifts(email, dtos) return this.create_service.createOneOrManyShifts(email, dtos)
} }
@Patch('update') @Patch('update')
updateBatch( @Body() dtos: ShiftDto[], @Req() req): Promise<Result<ShiftDto[], string>>{ @ModuleAccessAllowed(ModulesEnum.timesheets)
const email = req.user?.email; updateBatch(@Access('email') email: string, @Body() dtos: ShiftDto[]): Promise<Result<ShiftDto[], string>> {
return this.update_delete_service.updateOneOrManyShifts(dtos, email); return this.update_delete_service.updateOneOrManyShifts(dtos, email);
} }
@Delete(':shift_id') @Delete(':shift_id')
remove(@Param('shift_id') shift_id: number ): Promise<Result<number, string>> { @ModuleAccessAllowed(ModulesEnum.timesheets)
remove(@Param('shift_id') shift_id: number): Promise<Result<number, string>> {
return this.update_delete_service.deleteShift(shift_id); return this.update_delete_service.deleteShift(shift_id);
} }

View File

@ -1,12 +1,12 @@
import { Body, Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Query, Req, UnauthorizedException } from "@nestjs/common"; import { Body, Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Query, Req, UnauthorizedException } from "@nestjs/common";
import { RolesAllowed } from "src/common/decorators/roles.decorators";
import { GetTimesheetsOverviewService } from "src/time-and-attendance/timesheets/services/timesheet-get-overview.service"; import { GetTimesheetsOverviewService } from "src/time-and-attendance/timesheets/services/timesheet-get-overview.service";
import { TimesheetApprovalService } from "src/time-and-attendance/timesheets/services/timesheet-approval.service"; import { TimesheetApprovalService } from "src/time-and-attendance/timesheets/services/timesheet-approval.service";
import { GLOBAL_CONTROLLER_ROLES, MANAGER_ROLES } from "src/common/shared/role-groupes"; import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators";
import { Modules as ModulesEnum } from ".prisma/client";
import { Access } from "src/common/decorators/module-access.decorators";
@Controller('timesheets') @Controller('timesheets')
@RolesAllowed(...GLOBAL_CONTROLLER_ROLES)
export class TimesheetController { export class TimesheetController {
constructor( constructor(
private readonly timesheetOverview: GetTimesheetsOverviewService, private readonly timesheetOverview: GetTimesheetsOverviewService,
@ -14,19 +14,18 @@ export class TimesheetController {
) { } ) { }
@Get(':year/:period_number') @Get(':year/:period_number')
@ModuleAccessAllowed(ModulesEnum.timesheets_approval)
getTimesheetByPayPeriod( getTimesheetByPayPeriod(
@Req() req, @Access('email') email: string,
@Param('year', ParseIntPipe) year: number, @Param('year', ParseIntPipe) year: number,
@Param('period_number', ParseIntPipe) period_number: number, @Param('period_number', ParseIntPipe) period_number: number,
@Query('employee_email') employee_email?: string, @Query('employee_email') employee_email?: string,
) { ) {
const email = req.user?.email;
if (!email) throw new UnauthorizedException('Unauthorized User');
return this.timesheetOverview.getTimesheetsForEmployeeByPeriod(email, year, period_number, employee_email); return this.timesheetOverview.getTimesheetsForEmployeeByPeriod(email, year, period_number, employee_email);
} }
@Patch('timesheet-approval') @Patch('timesheet-approval')
@RolesAllowed(...MANAGER_ROLES) @ModuleAccessAllowed(ModulesEnum.timesheets_approval)
approveTimesheet( approveTimesheet(
@Body('timesheet_id', ParseIntPipe) timesheet_id: number, @Body('timesheet_id', ParseIntPipe) timesheet_id: number,
@Body('is_approved', ParseBoolPipe) is_approved: boolean, @Body('is_approved', ParseBoolPipe) is_approved: boolean,