Fix mobile validation, cascade delete for businesses/categories, and DB error handling.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -12,6 +12,10 @@ import { JwtService } from '@nestjs/jwt';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { createHash, randomInt } from 'crypto';
|
||||
import { IsNull, MoreThan, Repository } from 'typeorm';
|
||||
import {
|
||||
isValidIranMobile,
|
||||
normalizeIranMobile,
|
||||
} from '../../common/utils/iran-mobile.util';
|
||||
import { AuthOtpEntity } from '../../entities/auth-otp.entity';
|
||||
import { UserEntity, UserRole } from '../../entities/user.entity';
|
||||
import { JwtPayload } from './auth.types';
|
||||
@@ -200,8 +204,8 @@ export class AuthService {
|
||||
}
|
||||
|
||||
private normalizeMobile(mobile: string): string {
|
||||
const normalized = String(mobile ?? '').trim();
|
||||
if (!/^(\+?\d{10,15}|09\d{9})$/.test(normalized)) {
|
||||
const normalized = normalizeIranMobile(mobile);
|
||||
if (!isValidIranMobile(normalized)) {
|
||||
throw new BadRequestException('Invalid mobile number');
|
||||
}
|
||||
return normalized;
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString, Matches } from 'class-validator';
|
||||
import { IsString } from 'class-validator';
|
||||
import { IsIranMobile } from '../../../common/decorators/is-iran-mobile.decorator';
|
||||
|
||||
export class RequestOtpDto {
|
||||
@ApiProperty({ example: '09122222222' })
|
||||
@IsString()
|
||||
@Matches(/^(\+?\d{10,15}|09\d{9})$/, {
|
||||
message: 'mobile must be a valid mobile number',
|
||||
})
|
||||
@IsIranMobile()
|
||||
mobile!: string;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString, Matches } from 'class-validator';
|
||||
import { IsIranMobile } from '../../../common/decorators/is-iran-mobile.decorator';
|
||||
|
||||
export class VerifyOtpDto {
|
||||
@ApiProperty({ example: '09122222222' })
|
||||
@IsString()
|
||||
@Matches(/^(\+?\d{10,15}|09\d{9})$/, {
|
||||
message: 'mobile must be a valid mobile number',
|
||||
})
|
||||
@IsIranMobile()
|
||||
mobile!: string;
|
||||
|
||||
@ApiProperty({ example: '123456' })
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { BusinessCategoryEntity } from '../../entities/business-category.entity';
|
||||
import { BusinessEntity } from '../../entities/business.entity';
|
||||
import { AuthModule } from '../auth/auth.module';
|
||||
import { BusinessCategoriesController } from './business-categories.controller';
|
||||
import { BusinessCategoriesService } from './business-categories.service';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([BusinessCategoryEntity]), AuthModule],
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([BusinessCategoryEntity, BusinessEntity]),
|
||||
AuthModule,
|
||||
],
|
||||
controllers: [BusinessCategoriesController],
|
||||
providers: [BusinessCategoriesService],
|
||||
})
|
||||
|
||||
@@ -4,8 +4,10 @@ import {
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
import { deleteCategoryCascade } from '../../common/utils/business-cascade.util';
|
||||
import { BusinessCategoryEntity } from '../../entities/business-category.entity';
|
||||
import { BusinessEntity } from '../../entities/business.entity';
|
||||
import { CreateBusinessCategoryDto } from './dto/create-business-category.dto';
|
||||
import { UpdateBusinessCategoryDto } from './dto/update-business-category.dto';
|
||||
|
||||
@@ -14,6 +16,9 @@ export class BusinessCategoriesService {
|
||||
constructor(
|
||||
@InjectRepository(BusinessCategoryEntity)
|
||||
private readonly repo: Repository<BusinessCategoryEntity>,
|
||||
@InjectRepository(BusinessEntity)
|
||||
private readonly businessRepo: Repository<BusinessEntity>,
|
||||
private readonly dataSource: DataSource,
|
||||
) {}
|
||||
|
||||
findAll(): Promise<BusinessCategoryEntity[]> {
|
||||
@@ -62,9 +67,19 @@ export class BusinessCategoriesService {
|
||||
}
|
||||
|
||||
async remove(id: string): Promise<void> {
|
||||
const result = await this.repo.delete(id);
|
||||
if (!result.affected) {
|
||||
throw new NotFoundException('Business category not found');
|
||||
}
|
||||
await this.findOne(id);
|
||||
|
||||
const businesses = await this.businessRepo.find({
|
||||
where: { categoryId: id },
|
||||
select: ['id'],
|
||||
});
|
||||
|
||||
await this.dataSource.transaction(async (manager) => {
|
||||
await deleteCategoryCascade(
|
||||
manager,
|
||||
id,
|
||||
businesses.map((b) => b.id),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ import {
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
import { deleteBusinessCascade } from '../../common/utils/business-cascade.util';
|
||||
import { BusinessCategoryEntity } from '../../entities/business-category.entity';
|
||||
import { BusinessEntity } from '../../entities/business.entity';
|
||||
import { UserEntity, UserRole } from '../../entities/user.entity';
|
||||
@@ -29,6 +30,7 @@ export class BusinessesService {
|
||||
private readonly categoryRepo: Repository<BusinessCategoryEntity>,
|
||||
@InjectRepository(UserEntity)
|
||||
private readonly userRepo: Repository<UserEntity>,
|
||||
private readonly dataSource: DataSource,
|
||||
) {}
|
||||
|
||||
async findAll(filters: FindAllFilters): Promise<BusinessEntity[]> {
|
||||
@@ -111,10 +113,11 @@ export class BusinessesService {
|
||||
}
|
||||
|
||||
async remove(id: string): Promise<void> {
|
||||
const result = await this.repo.delete(id);
|
||||
if (!result.affected) {
|
||||
throw new NotFoundException('Business not found');
|
||||
}
|
||||
await this.findOne(id);
|
||||
|
||||
await this.dataSource.transaction(async (manager) => {
|
||||
await deleteBusinessCascade(manager, id);
|
||||
});
|
||||
}
|
||||
|
||||
async assignOwner(
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsOptional, IsString, Matches, MaxLength } from 'class-validator';
|
||||
import { IsOptional, IsString, MaxLength } from 'class-validator';
|
||||
import { IsIranMobile } from '../../../common/decorators/is-iran-mobile.decorator';
|
||||
|
||||
export class AssignBusinessOwnerDto {
|
||||
@ApiProperty({ example: '09111111111' })
|
||||
@IsString()
|
||||
@Matches(/^(\+?\d{10,15}|09\d{9})$/, {
|
||||
message: 'mobile must be a valid mobile number',
|
||||
})
|
||||
@IsIranMobile()
|
||||
mobile!: string;
|
||||
|
||||
@ApiPropertyOptional({ example: 'Business Admin', nullable: true })
|
||||
|
||||
Reference in New Issue
Block a user