feat: add field validators for username and password, and integrate them into DTOs

This commit is contained in:
2026-05-19 20:34:05 +03:30
parent d526f6ed2c
commit 98099e97e7
17 changed files with 136 additions and 33 deletions
@@ -0,0 +1,14 @@
import { Matches, MinLength } from 'class-validator'
const MIN_LENGTH = 6
const REGEX = /^[a-zA-Z0-9]*$/
export const FiscalIdFieldValidator = () =>
function (target: object, propertyKey: string) {
MinLength(MIN_LENGTH, {
message: `شناسه یکتا باید حداقل ${MIN_LENGTH} کاراکتر باشد`,
})(target, propertyKey)
Matches(REGEX, {
message: 'شناسه یکتا فقط می‌تواند شامل حروف و اعداد باشد',
})(target, propertyKey)
}
@@ -0,0 +1,3 @@
export { FiscalIdFieldValidator } from './fiscalId-validator'
export { PasswordFieldValidator, PASSWORD_MIN_LENGTH } from './password'
export { UsernameFieldValidator } from './username'
@@ -0,0 +1,10 @@
import { MinLength } from 'class-validator'
export const PASSWORD_MIN_LENGTH = 6
export const PasswordFieldValidator = () =>
function (target: object, propertyKey: string) {
MinLength(PASSWORD_MIN_LENGTH, {
message: `رمز عبور باید حداقل ${PASSWORD_MIN_LENGTH} کاراکتر باشد`,
})(target, propertyKey)
}
@@ -0,0 +1,14 @@
import { Matches, MinLength } from 'class-validator'
const USERNAME_REGEX = /^[a-zA-Z0-9_-]*$/
const USERNAME_MIN_LENGTH = 6
export const UsernameFieldValidator = () =>
function (target: object, propertyKey: string) {
MinLength(USERNAME_MIN_LENGTH, {
message: `نام کاربری باید حداقل ${USERNAME_MIN_LENGTH} کاراکتر باشد`,
})(target, propertyKey)
Matches(USERNAME_REGEX, {
message: 'نام کاربری فقط می‌تواند شامل حروف، اعداد، زیرخط و خط تیره باشد',
})(target, propertyKey)
}
+1
View File
@@ -1,4 +1,5 @@
export * from './enum-translator.util' export * from './enum-translator.util'
export * from './field-validator.util'
export * from './http-client.util' export * from './http-client.util'
export * from './jwt-user.util' export * from './jwt-user.util'
export * from './mappers/consumer_mappers.util' export * from './mappers/consumer_mappers.util'
@@ -1,13 +1,16 @@
import { PasswordFieldValidator, UsernameFieldValidator } from '@/common/utils'
import { ApiProperty, PartialType } from '@nestjs/swagger' import { ApiProperty, PartialType } from '@nestjs/swagger'
import { IsEnum, IsOptional, IsString } from 'class-validator' import { IsEnum, IsOptional, IsString } from 'class-validator'
import { AccountStatus, ConsumerRole } from 'generated/prisma/enums' import { AccountStatus, ConsumerRole } from 'generated/prisma/enums'
export class CreateConsumerAccountDto { export class CreateConsumerAccountDto {
@IsString() @IsString()
@UsernameFieldValidator()
@ApiProperty({ required: true }) @ApiProperty({ required: true })
username: string username: string
@IsString() @IsString()
@PasswordFieldValidator()
@ApiProperty({ required: true }) @ApiProperty({ required: true })
password: string password: string
@@ -1,3 +1,4 @@
import { PasswordFieldValidator, UsernameFieldValidator } from '@/common/utils'
import { ConsumerStatus } from '@/generated/prisma/enums' import { ConsumerStatus } from '@/generated/prisma/enums'
import { ApiProperty, OmitType, PartialType } from '@nestjs/swagger' import { ApiProperty, OmitType, PartialType } from '@nestjs/swagger'
import { IsEnum, IsOptional, IsString } from 'class-validator' import { IsEnum, IsOptional, IsString } from 'class-validator'
@@ -16,10 +17,12 @@ export class CreateConsumerDto {
last_name: string last_name: string
@IsString() @IsString()
@UsernameFieldValidator()
@ApiProperty({ required: true }) @ApiProperty({ required: true })
username: string username: string
@IsString() @IsString()
@PasswordFieldValidator()
@ApiProperty({ required: true }) @ApiProperty({ required: true })
password: string password: string
@@ -1,3 +1,4 @@
import { PasswordFieldValidator, UsernameFieldValidator } from '@/common/utils'
import { PartnerStatus, TspProviderType } from '@/generated/prisma/enums' import { PartnerStatus, TspProviderType } from '@/generated/prisma/enums'
import { ApiProperty, PartialType } from '@nestjs/swagger' import { ApiProperty, PartialType } from '@nestjs/swagger'
import { IsEnum, IsNumber, IsOptional, IsString } from 'class-validator' import { IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'
@@ -21,10 +22,12 @@ export class CreatePartnerDto {
// license_quota?: number // license_quota?: number
@IsString() @IsString()
@UsernameFieldValidator()
@ApiProperty({ required: true }) @ApiProperty({ required: true })
username: string username: string
@IsString() @IsString()
@PasswordFieldValidator()
@ApiProperty({ required: true }) @ApiProperty({ required: true })
password: string password: string
@@ -1,3 +1,4 @@
import { PasswordFieldValidator, UsernameFieldValidator } from '@/common/utils'
import { ApiProperty, PartialType } from '@nestjs/swagger' import { ApiProperty, PartialType } from '@nestjs/swagger'
import { IsEnum, IsOptional, IsString } from 'class-validator' import { IsEnum, IsOptional, IsString } from 'class-validator'
import { PartnerRole } from 'generated/prisma/enums' import { PartnerRole } from 'generated/prisma/enums'
@@ -5,9 +6,11 @@ import { PartnerRole } from 'generated/prisma/enums'
export class CreatePartnerAccountDto { export class CreatePartnerAccountDto {
@IsString() @IsString()
@ApiProperty({}) @ApiProperty({})
@UsernameFieldValidator()
username: string username: string
@IsString() @IsString()
@PasswordFieldValidator()
@ApiProperty({}) @ApiProperty({})
password: string password: string
} }
@@ -1,13 +1,16 @@
import { PasswordFieldValidator, UsernameFieldValidator } from '@/common/utils'
import { ApiProperty, PartialType } from '@nestjs/swagger' import { ApiProperty, PartialType } from '@nestjs/swagger'
import { IsEnum, IsOptional, IsString } from 'class-validator' import { IsEnum, IsOptional, IsString } from 'class-validator'
import { AccountStatus, AccountType } from 'generated/prisma/enums' import { AccountStatus, AccountType } from 'generated/prisma/enums'
export class CreateAccountDto { export class CreateAccountDto {
@IsString() @IsString()
@UsernameFieldValidator()
@ApiProperty() @ApiProperty()
username: string username: string
@IsString() @IsString()
@PasswordFieldValidator()
@ApiProperty() @ApiProperty()
password: string password: string
+5 -2
View File
@@ -2,11 +2,14 @@ import { ApiProperty } from '@nestjs/swagger'
import { IsBoolean, IsOptional, IsString } from 'class-validator' import { IsBoolean, IsOptional, IsString } from 'class-validator'
export class LoginDto { export class LoginDto {
@ApiProperty({ description: 'Mobile number used as username', example: '09120258156' }) @ApiProperty({
description: 'you can use your mobile number as username',
example: '09120258156',
})
@IsString() @IsString()
username: string username: string
@ApiProperty({ description: 'OTP password (one-time password)', example: '11111' }) @ApiProperty({ example: '11111' })
@IsString() @IsString()
password: string password: string
+6 -12
View File
@@ -52,20 +52,14 @@ export class CatalogsService {
async getSKU(search: string) { async getSKU(search: string) {
const items = await this.prisma.stockKeepingUnits.findMany({ const items = await this.prisma.stockKeepingUnits.findMany({
// where: {
// OR: [{ code: { contains: search } }, { name: { contains: search } }],
// },
select: {
id: true,
code: true,
name: true,
is_domestic: true,
is_public: true,
VAT: true,
},
take: 100, take: 100,
}) })
return ResponseMapper.list(items)
const mappedItems = items.map(item => ({
...item,
fullname: `${item.code} - ${item.name}`,
}))
return ResponseMapper.list(mappedItems)
} }
async getGuildGoodCategories(guild_id: string) { async getGuildGoodCategories(guild_id: string) {
@@ -1,13 +1,16 @@
import { PasswordFieldValidator, UsernameFieldValidator } from '@/common/utils'
import { ApiProperty, PartialType } from '@nestjs/swagger' import { ApiProperty, PartialType } from '@nestjs/swagger'
import { IsEnum, IsOptional, IsString } from 'class-validator' import { IsEnum, IsOptional, IsString } from 'class-validator'
import { AccountStatus } from 'generated/prisma/enums' import { AccountStatus } from 'generated/prisma/enums'
export class CreateConsumerAccountDto { export class CreateConsumerAccountDto {
@IsString() @IsString()
@UsernameFieldValidator()
@ApiProperty({ required: true }) @ApiProperty({ required: true })
username: string username: string
@IsString() @IsString()
@PasswordFieldValidator()
@ApiProperty({ required: true }) @ApiProperty({ required: true })
password: string password: string
@@ -1,13 +1,16 @@
import { PasswordFieldValidator, UsernameFieldValidator } from '@/common/utils'
import { ApiProperty, PartialType } from '@nestjs/swagger' import { ApiProperty, PartialType } from '@nestjs/swagger'
import { IsEnum, IsOptional, IsString } from 'class-validator' import { IsEnum, IsOptional, IsString } from 'class-validator'
import { AccountStatus, ConsumerRole } from 'generated/prisma/enums' import { AccountStatus, ConsumerRole } from 'generated/prisma/enums'
export class CreateConsumerAccountDto { export class CreateConsumerAccountDto {
@IsString() @IsString()
@UsernameFieldValidator()
@ApiProperty({ required: true }) @ApiProperty({ required: true })
username: string username: string
@IsString() @IsString()
@PasswordFieldValidator()
@ApiProperty({ required: true }) @ApiProperty({ required: true })
password: string password: string
@@ -1,9 +1,12 @@
import { RedisKeyMaker } from '@/common/utils' import { RedisKeyMaker } from '@/common/utils'
import { BusinessActivitySelect } from '@/generated/prisma/models' import {
BusinessActivityCreateInput,
BusinessActivitySelect,
} from '@/generated/prisma/models'
import { getPartnerFirstRemainingLicense } from '@/modules/partners/utils/getPartnerRemainingLicenses.util' import { getPartnerFirstRemainingLicense } from '@/modules/partners/utils/getPartnerRemainingLicenses.util'
import { PrismaService } from '@/prisma/prisma.service' import { PrismaService } from '@/prisma/prisma.service'
import { RedisService } from '@/redis/redis.service' import { RedisService } from '@/redis/redis.service'
import { BadRequestException, Injectable } from '@nestjs/common' import { Injectable } from '@nestjs/common'
import { ResponseMapper } from 'common/response/response-mapper' import { ResponseMapper } from 'common/response/response-mapper'
import { PartnersCacheInvalidationService } from '../../cache/partners-cache-invalidation.service' import { PartnersCacheInvalidationService } from '../../cache/partners-cache-invalidation.service'
import { CreateBusinessActivitiesDto } from './dto/create-business-activities.dto' import { CreateBusinessActivitiesDto } from './dto/create-business-activities.dto'
@@ -158,6 +161,37 @@ export class BusinessActivitiesService {
const license = await getPartnerFirstRemainingLicense(tx, { partner_id }) const license = await getPartnerFirstRemainingLicense(tx, { partner_id })
const { guild_id, license_starts_at, expires_at, ...rest } = data const { guild_id, license_starts_at, expires_at, ...rest } = data
const dataToCreate: BusinessActivityCreateInput = {
...rest,
economic_code: String(data.economic_code),
guild: {
connect: {
id: guild_id,
},
},
consumer: {
connect: {
id: consumer_id,
},
},
}
if (license_starts_at) {
dataToCreate.license_activation = {
create: {
starts_at: license_starts_at ?? new Date(),
expires_at:
expires_at ?? this.setExpireDate(new Date(license_starts_at || '')),
license: {
connect: {
id: license.id,
},
},
},
}
}
const createdBusinessActivity = await tx.businessActivity.create({ const createdBusinessActivity = await tx.businessActivity.create({
data: { data: {
...rest, ...rest,
@@ -196,25 +230,23 @@ export class BusinessActivitiesService {
}) })
const activationId = createdBusinessActivity.license_activation?.id const activationId = createdBusinessActivity.license_activation?.id
if (!activationId) { if (activationId) {
throw new BadRequestException('لایسنس برای این فعالیت تجاری ایجاد نشد.') const licenseAllocationCreation = Array.from({
} length: license.accounts_limit,
}).map(() =>
const licenseAllocationCreation = Array.from({ tx.licenseAccountAllocation.create({
length: license.accounts_limit, data: {
}).map(() => license_activation: {
tx.licenseAccountAllocation.create({ connect: {
data: { id: activationId,
license_activation: { },
connect: {
id: activationId,
}, },
}, },
}, }),
}), )
)
await Promise.all(licenseAllocationCreation) await Promise.all(licenseAllocationCreation)
}
return tx.businessActivity.findUniqueOrThrow({ return tx.businessActivity.findUniqueOrThrow({
where: { where: {
@@ -1,5 +1,6 @@
import { FiscalIdFieldValidator } from '@/common/utils'
import { ApiProperty, PartialType } from '@nestjs/swagger' import { ApiProperty, PartialType } from '@nestjs/swagger'
import { IsDateString, IsOptional, IsString } from 'class-validator' import { IsDateString, IsNumber, IsOptional, IsString, Max, Min } from 'class-validator'
export class CreateBusinessActivitiesDto { export class CreateBusinessActivitiesDto {
@IsString() @IsString()
@@ -11,6 +12,7 @@ export class CreateBusinessActivitiesDto {
economic_code: string economic_code: string
@IsString() @IsString()
@FiscalIdFieldValidator()
@ApiProperty({ required: true }) @ApiProperty({ required: true })
fiscal_id: string fiscal_id: string
@@ -22,6 +24,12 @@ export class CreateBusinessActivitiesDto {
@ApiProperty({ required: true }) @ApiProperty({ required: true })
guild_id: string guild_id: string
@ApiProperty({ required: false, default: '1' })
@IsNumber()
@Min(0)
@Max(1_000_000_000)
invoice_number_sequence: number
@IsDateString() @IsDateString()
@IsOptional() @IsOptional()
@ApiProperty({ required: false }) @ApiProperty({ required: false })
@@ -1,3 +1,4 @@
import { PasswordFieldValidator, UsernameFieldValidator } from '@/common/utils'
import { ConsumerIndividual, ConsumerLegal } from '@/generated/prisma/client' import { ConsumerIndividual, ConsumerLegal } from '@/generated/prisma/client'
import { ConsumerStatus, ConsumerType } from '@/generated/prisma/enums' import { ConsumerStatus, ConsumerType } from '@/generated/prisma/enums'
import { ApiProperty, OmitType, PartialType } from '@nestjs/swagger' import { ApiProperty, OmitType, PartialType } from '@nestjs/swagger'
@@ -9,10 +10,12 @@ export class CreateConsumerDto {
type: ConsumerType type: ConsumerType
@IsString() @IsString()
@UsernameFieldValidator()
@ApiProperty({ required: true }) @ApiProperty({ required: true })
username: string username: string
@IsString() @IsString()
@PasswordFieldValidator()
@ApiProperty({ required: true }) @ApiProperty({ required: true })
password: string password: string