Files
backend/src/modules/auth/presentation/auth.dto.ts
T

76 lines
1.8 KiB
TypeScript

import { ApiProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
import {
IsString,
MinLength,
Validate,
ValidatorConstraint,
} from 'class-validator';
const EMAIL_PART_COUNT = 2;
const FIRST_CHARACTER_INDEX = 0;
const MAX_EMAIL_LENGTH = 254;
const ASCII_SPACE = 32;
const ASCII_DELETE = 127;
const MIN_PASSWORD_LENGTH = 12;
const MAX_PASSWORD_BYTES = 72;
@ValidatorConstraint({ name: 'email' })
class EmailConstraint {
validate(value: unknown): boolean {
return (
typeof value === 'string' &&
value.length <= MAX_EMAIL_LENGTH &&
value.split('@').length === EMAIL_PART_COUNT &&
!value
.split('')
.some(
(character) =>
/\s/.test(character) ||
character.charCodeAt(FIRST_CHARACTER_INDEX) < ASCII_SPACE ||
character.charCodeAt(FIRST_CHARACTER_INDEX) === ASCII_DELETE,
) &&
value.split('@').every(Boolean)
);
}
defaultMessage(): string {
return 'must be a valid email address';
}
}
@ValidatorConstraint({ name: 'passwordBytes' })
class PasswordBytesConstraint {
validate(value: unknown): boolean {
return (
typeof value === 'string' &&
Buffer.byteLength(value, 'utf8') <= MAX_PASSWORD_BYTES
);
}
defaultMessage(): string {
return 'must not exceed 72 UTF-8 bytes';
}
}
export class CredentialsDto {
@ApiProperty({ example: 'user@example.com' })
@IsString()
@Transform(({ value }: { value: unknown }) =>
typeof value === 'string' ? value.trim().toLowerCase() : value,
)
@Validate(EmailConstraint)
declare email: string;
@ApiProperty({
minLength: MIN_PASSWORD_LENGTH,
maxLength: MAX_PASSWORD_BYTES,
example: 'a secure password',
})
@IsString()
@MinLength(MIN_PASSWORD_LENGTH)
@Validate(PasswordBytesConstraint)
declare password: string;
}