76 lines
1.8 KiB
TypeScript
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;
|
|
}
|