add auth roles and guards 050220

This commit is contained in:
2026-05-11 12:40:42 +03:30
parent 03ac02bee9
commit 3e5543a548
29 changed files with 924 additions and 30 deletions
+66 -6
View File
@@ -1,5 +1,6 @@
import {
BadRequestException,
ForbiddenException,
Injectable,
NotFoundException,
} from '@nestjs/common';
@@ -9,7 +10,8 @@ import { Repository } from 'typeorm';
import { BookingEntity, BookingStatus } from '../../entities/booking.entity';
import { BusinessServiceEntity } from '../../entities/business-service.entity';
import { BusinessEntity } from '../../entities/business.entity';
import { UserEntity } from '../../entities/user.entity';
import { UserEntity, UserRole } from '../../entities/user.entity';
import { AuthUser } from '../auth/auth.types';
import { CreateBookingDto } from './dto/create-booking.dto';
import { UpdateBookingDto } from './dto/update-booking.dto';
@@ -39,7 +41,10 @@ export class BookingsService {
this.minLeadMinutes = Number.isFinite(parsed) && parsed >= 0 ? parsed : 60;
}
async findAll(filters: FindAllFilters): Promise<BookingEntity[]> {
async findAll(
filters: FindAllFilters,
actor?: AuthUser,
): Promise<BookingEntity[]> {
const qb = this.repo
.createQueryBuilder('b')
.leftJoinAndSelect('b.customer', 'customer')
@@ -48,7 +53,15 @@ export class BookingsService {
.leftJoinAndSelect('businessService.serviceTemplate', 'template')
.orderBy('b.scheduledAt', 'ASC');
if (filters.customerId) {
if (actor?.role === UserRole.CUSTOMER) {
qb.andWhere('b.customerId = :actorCustomerId', {
actorCustomerId: actor.id,
});
} else if (actor?.role === UserRole.BUSINESS_OWNER) {
qb.andWhere('business.ownerId = :actorOwnerId', {
actorOwnerId: actor.id,
});
} else if (filters.customerId) {
qb.andWhere('b.customerId = :customerId', {
customerId: filters.customerId,
});
@@ -65,7 +78,7 @@ export class BookingsService {
return qb.getMany();
}
async findOne(id: string): Promise<BookingEntity> {
async findOne(id: string, actor?: AuthUser): Promise<BookingEntity> {
const row = await this.repo.findOne({
where: { id },
relations: {
@@ -77,10 +90,18 @@ export class BookingsService {
if (!row) {
throw new NotFoundException('Booking not found');
}
this.assertCanViewBooking(row, actor);
return row;
}
async create(dto: CreateBookingDto): Promise<BookingEntity> {
async create(dto: CreateBookingDto, actor?: AuthUser): Promise<BookingEntity> {
if (
actor?.role === UserRole.CUSTOMER &&
dto.customerId !== actor.id
) {
throw new ForbiddenException('Customers can only book for themselves');
}
const customer = await this.userRepo.findOne({
where: { id: dto.customerId },
});
@@ -124,8 +145,13 @@ export class BookingsService {
return this.repo.save(entity);
}
async update(id: string, dto: UpdateBookingDto): Promise<BookingEntity> {
async update(
id: string,
dto: UpdateBookingDto,
actor?: AuthUser,
): Promise<BookingEntity> {
const row = await this.findOne(id);
this.assertCanManageBooking(row, actor);
if (dto.scheduledAt !== undefined) {
const scheduledAt = this.parseScheduledAt(dto.scheduledAt);
@@ -168,4 +194,38 @@ export class BookingsService {
);
}
}
private assertCanViewBooking(row: BookingEntity, actor?: AuthUser): void {
if (!actor || actor.role === UserRole.ADMIN) {
return;
}
if (actor.role === UserRole.CUSTOMER && row.customerId === actor.id) {
return;
}
if (
actor.role === UserRole.BUSINESS_OWNER &&
row.business?.ownerId === actor.id
) {
return;
}
throw new ForbiddenException('You cannot access this booking');
}
private assertCanManageBooking(row: BookingEntity, actor?: AuthUser): void {
if (!actor || actor.role === UserRole.ADMIN) {
return;
}
if (
actor.role === UserRole.BUSINESS_OWNER &&
row.business?.ownerId === actor.id
) {
return;
}
throw new ForbiddenException('You can only manage your own business bookings');
}
}