mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-02 16:13:40 +00:00
60 lines
2.3 KiB
TypeScript
60 lines
2.3 KiB
TypeScript
import {
|
|
registerDecorator,
|
|
ValidationArguments,
|
|
ValidationOptions,
|
|
ValidatorConstraint,
|
|
ValidatorConstraintInterface,
|
|
} from 'class-validator';
|
|
import { isValidPhoneNumber, parsePhoneNumberFromString } from 'libphonenumber-js';
|
|
|
|
/**
|
|
* Country-aware phone validation. The value is expected as a full international
|
|
* number (E.164, e.g. "+251911223344"), so the country is derived from the
|
|
* value itself — no separate country field needed.
|
|
*/
|
|
@ValidatorConstraint({ name: 'IsValidPhone', async: false })
|
|
export class IsValidPhoneConstraint implements ValidatorConstraintInterface {
|
|
validate(value: unknown): boolean {
|
|
// Empty is allowed here; pair with @IsOptional / @IsNotEmpty as needed.
|
|
if (value === undefined || value === null || value === '') return true;
|
|
if (typeof value !== 'string') return false;
|
|
return isValidPhoneNumber(value);
|
|
}
|
|
|
|
defaultMessage(args: ValidationArguments): string {
|
|
return `${args.property} must be a valid international phone number (E.164, e.g. +251911223344)`;
|
|
}
|
|
}
|
|
|
|
/** Class-validator decorator wrapping the country-aware phone constraint. */
|
|
export function IsValidPhone(validationOptions?: ValidationOptions) {
|
|
return function (object: object, propertyName: string) {
|
|
registerDecorator({
|
|
target: object.constructor,
|
|
propertyName,
|
|
options: validationOptions,
|
|
constraints: [],
|
|
validator: IsValidPhoneConstraint,
|
|
});
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Normalize a phone string to canonical E.164. Returns the canonical form when
|
|
* parseable, otherwise the trimmed original (tolerant — never throws), or the
|
|
* value unchanged when empty/nullish.
|
|
*
|
|
* Defaults the country to Ethiopia so bare local numbers (no "+", e.g. eTrade's
|
|
* "0355235416") resolve the same way the frontend's own toEthiopianE164 already
|
|
* assumes — without this hint, libphonenumber can't infer a country for a
|
|
* number with no "+" prefix and silently falls through to the untouched local
|
|
* string, which then never matches the "+251…" form submitted by the client.
|
|
*/
|
|
export function normalizeE164(
|
|
value: string | null | undefined,
|
|
): string | null | undefined {
|
|
if (value === undefined || value === null || value === '') return value;
|
|
const parsed = parsePhoneNumberFromString(value, 'ET');
|
|
return parsed?.isValid() ? parsed.number : value.trim();
|
|
}
|