english version update
This commit is contained in:
171
ospabhost/frontend/src/utils/emailValidation.ts
Normal file
171
ospabhost/frontend/src/utils/emailValidation.ts
Normal file
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* Email validation service
|
||||
* Uses multiple validation methods for reliable email checking
|
||||
*/
|
||||
|
||||
// Regex for basic email format validation
|
||||
const EMAIL_REGEX = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||
|
||||
// Common disposable email domains (partial list)
|
||||
const DISPOSABLE_DOMAINS = new Set([
|
||||
'10minutemail.com',
|
||||
'tempmail.com',
|
||||
'throwaway.email',
|
||||
'guerrillamail.com',
|
||||
'mailinator.com',
|
||||
'temp-mail.org',
|
||||
'fakeinbox.com',
|
||||
'trashmail.com',
|
||||
'maildrop.cc',
|
||||
'yopmail.com',
|
||||
'sharklasers.com',
|
||||
'grr.la',
|
||||
'getairmail.com',
|
||||
'dispostable.com',
|
||||
]);
|
||||
|
||||
// Common typos in email domains
|
||||
const DOMAIN_TYPOS: Record<string, string> = {
|
||||
'gmial.com': 'gmail.com',
|
||||
'gmal.com': 'gmail.com',
|
||||
'gmil.com': 'gmail.com',
|
||||
'gmail.co': 'gmail.com',
|
||||
'gamil.com': 'gmail.com',
|
||||
'gmaill.com': 'gmail.com',
|
||||
'gnail.com': 'gmail.com',
|
||||
'yahooo.com': 'yahoo.com',
|
||||
'yaho.com': 'yahoo.com',
|
||||
'hotmal.com': 'hotmail.com',
|
||||
'hotmai.com': 'hotmail.com',
|
||||
'hotmial.com': 'hotmail.com',
|
||||
'outlok.com': 'outlook.com',
|
||||
'outloo.com': 'outlook.com',
|
||||
'yandx.ru': 'yandex.ru',
|
||||
'yanex.ru': 'yandex.ru',
|
||||
'mail.r': 'mail.ru',
|
||||
'mal.ru': 'mail.ru',
|
||||
};
|
||||
|
||||
export type EmailValidationResult = {
|
||||
isValid: boolean;
|
||||
error?: string;
|
||||
suggestion?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Basic email format validation
|
||||
*/
|
||||
export function validateEmailFormat(email: string): boolean {
|
||||
return EMAIL_REGEX.test(email.trim().toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if email domain is disposable
|
||||
*/
|
||||
export function isDisposableEmail(email: string): boolean {
|
||||
const domain = email.split('@')[1]?.toLowerCase();
|
||||
return domain ? DISPOSABLE_DOMAINS.has(domain) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for common typos and suggest corrections
|
||||
*/
|
||||
export function suggestEmailCorrection(email: string): string | null {
|
||||
const parts = email.split('@');
|
||||
if (parts.length !== 2) return null;
|
||||
|
||||
const domain = parts[1].toLowerCase();
|
||||
const suggestion = DOMAIN_TYPOS[domain];
|
||||
|
||||
if (suggestion) {
|
||||
return `${parts[0]}@${suggestion}`;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate email with all checks
|
||||
*/
|
||||
export function validateEmail(email: string, locale: 'ru' | 'en' = 'ru'): EmailValidationResult {
|
||||
const trimmedEmail = email.trim().toLowerCase();
|
||||
|
||||
// Check if empty
|
||||
if (!trimmedEmail) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: locale === 'en' ? 'Email is required' : 'Email обязателен',
|
||||
};
|
||||
}
|
||||
|
||||
// Check format
|
||||
if (!validateEmailFormat(trimmedEmail)) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: locale === 'en' ? 'Invalid email format' : 'Неверный формат email',
|
||||
};
|
||||
}
|
||||
|
||||
// Check for disposable emails
|
||||
if (isDisposableEmail(trimmedEmail)) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: locale === 'en'
|
||||
? 'Disposable email addresses are not allowed'
|
||||
: 'Одноразовые email-адреса не допускаются',
|
||||
};
|
||||
}
|
||||
|
||||
// Check for typos and suggest
|
||||
const suggestion = suggestEmailCorrection(trimmedEmail);
|
||||
if (suggestion) {
|
||||
return {
|
||||
isValid: true,
|
||||
suggestion: locale === 'en'
|
||||
? `Did you mean ${suggestion}?`
|
||||
: `Возможно, вы имели в виду ${suggestion}?`,
|
||||
};
|
||||
}
|
||||
|
||||
return { isValid: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Async email validation with API check (optional)
|
||||
* Uses abstract API for deep validation if API key is provided
|
||||
*/
|
||||
export async function validateEmailAsync(
|
||||
email: string,
|
||||
locale: 'ru' | 'en' = 'ru'
|
||||
): Promise<EmailValidationResult> {
|
||||
// First do basic validation
|
||||
const basicResult = validateEmail(email, locale);
|
||||
if (!basicResult.isValid) {
|
||||
return basicResult;
|
||||
}
|
||||
|
||||
// Optional: Use Abstract API for deeper validation
|
||||
// const apiKey = import.meta.env.VITE_ABSTRACT_EMAIL_API_KEY;
|
||||
// if (apiKey) {
|
||||
// try {
|
||||
// const response = await fetch(
|
||||
// `https://emailvalidation.abstractapi.com/v1/?api_key=${apiKey}&email=${encodeURIComponent(email)}`
|
||||
// );
|
||||
// const data = await response.json();
|
||||
//
|
||||
// if (!data.deliverability || data.deliverability === 'UNDELIVERABLE') {
|
||||
// return {
|
||||
// isValid: false,
|
||||
// error: locale === 'en'
|
||||
// ? 'This email address appears to be invalid'
|
||||
// : 'Этот email-адрес недействителен',
|
||||
// };
|
||||
// }
|
||||
// } catch (err) {
|
||||
// // If API fails, continue with basic validation result
|
||||
// console.warn('Email API validation failed:', err);
|
||||
// }
|
||||
// }
|
||||
|
||||
return basicResult;
|
||||
}
|
||||
Reference in New Issue
Block a user