feat: Add additional checks for website, NS records and personal emails
This commit is contained in:
@@ -6,6 +6,9 @@ import {redis} from '../database/redis.js';
|
|||||||
const DISPOSABLE_DOMAINS_URL =
|
const DISPOSABLE_DOMAINS_URL =
|
||||||
'https://raw.githubusercontent.com/disposable-email-domains/disposable-email-domains/main/disposable_email_blocklist.conf';
|
'https://raw.githubusercontent.com/disposable-email-domains/disposable-email-domains/main/disposable_email_blocklist.conf';
|
||||||
const DISPOSABLE_DOMAINS_CACHE_KEY = 'email:disposable_domains';
|
const DISPOSABLE_DOMAINS_CACHE_KEY = 'email:disposable_domains';
|
||||||
|
const PERSONAL_DOMAINS_URL =
|
||||||
|
'https://gist.githubusercontent.com/ammarshah/f5c2624d767f91a7cbdc4e54db8dd0bf/raw/660fd949eba09c0b86574d9d3aa0f2137161fc7c/all_email_provider_domains.txt';
|
||||||
|
const PERSONAL_DOMAINS_CACHE_KEY = 'email:personal_domains';
|
||||||
const CACHE_TTL_SECONDS = 24 * 60 * 60; // 24 hours (list updates daily)
|
const CACHE_TTL_SECONDS = 24 * 60 * 60; // 24 hours (list updates daily)
|
||||||
|
|
||||||
// Known email forwarding/alias services
|
// Known email forwarding/alias services
|
||||||
@@ -39,12 +42,15 @@ const FORWARDING_DOMAINS = new Set([
|
|||||||
|
|
||||||
export class EmailVerificationService {
|
export class EmailVerificationService {
|
||||||
private static disposableDomainsSet: Set<string> | null = null;
|
private static disposableDomainsSet: Set<string> | null = null;
|
||||||
|
private static personalDomainsSet: Set<string> | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify an email address
|
* Verify an email address
|
||||||
|
* - Checks for NS records (proves domain exists in DNS)
|
||||||
* - Checks for MX records (required for receiving email)
|
* - Checks for MX records (required for receiving email)
|
||||||
* - Checks if domain exists (DNS A/AAAA records) - informational only
|
* - Checks for A/AAAA records (informational - indicates if domain has a website)
|
||||||
* - Detects disposable email addresses
|
* - Detects disposable email addresses
|
||||||
|
* - Detects personal/free email providers (Gmail, Hotmail, etc.)
|
||||||
* - Detects forwarding/alias email addresses
|
* - Detects forwarding/alias email addresses
|
||||||
* - Suggests corrections for common typos
|
* - Suggests corrections for common typos
|
||||||
*/
|
*/
|
||||||
@@ -56,7 +62,9 @@ export class EmailVerificationService {
|
|||||||
isAlias: false,
|
isAlias: false,
|
||||||
isTypo: false,
|
isTypo: false,
|
||||||
isPlusAddressed: false,
|
isPlusAddressed: false,
|
||||||
|
isPersonalEmail: false,
|
||||||
domainExists: false,
|
domainExists: false,
|
||||||
|
hasWebsite: false,
|
||||||
hasMxRecords: false,
|
hasMxRecords: false,
|
||||||
reasons: [],
|
reasons: [],
|
||||||
};
|
};
|
||||||
@@ -74,6 +82,9 @@ export class EmailVerificationService {
|
|||||||
// Check if email is from a disposable domain using GitHub list
|
// Check if email is from a disposable domain using GitHub list
|
||||||
result.isDisposable = await this.isDisposableDomain(domain);
|
result.isDisposable = await this.isDisposableDomain(domain);
|
||||||
|
|
||||||
|
// Check if email is from a personal/free email provider
|
||||||
|
result.isPersonalEmail = await this.isPersonalEmailDomain(domain);
|
||||||
|
|
||||||
// Check if email is from a known forwarding/alias service
|
// Check if email is from a known forwarding/alias service
|
||||||
result.isAlias = this.isForwardingDomain(domain);
|
result.isAlias = this.isForwardingDomain(domain);
|
||||||
|
|
||||||
@@ -88,34 +99,46 @@ export class EmailVerificationService {
|
|||||||
result.isTypo = true;
|
result.isTypo = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check MX records first - this is what matters for email delivery
|
// Step 1: Check NS records - proves the domain exists in DNS
|
||||||
// A domain can receive email with only MX records, no A/AAAA records needed
|
try {
|
||||||
|
const nsRecords = await dns.resolveNs(domain);
|
||||||
|
result.domainExists = nsRecords && nsRecords.length > 0;
|
||||||
|
} catch {
|
||||||
|
result.domainExists = false;
|
||||||
|
result.valid = false;
|
||||||
|
result.reasons.push('Domain does not exist (no nameservers found)');
|
||||||
|
// If domain doesn't exist, no point checking MX/A records
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Check MX records - required for receiving email
|
||||||
try {
|
try {
|
||||||
const mxRecords = await dns.resolveMx(domain);
|
const mxRecords = await dns.resolveMx(domain);
|
||||||
result.hasMxRecords = mxRecords && mxRecords.length > 0;
|
result.hasMxRecords = mxRecords && mxRecords.length > 0;
|
||||||
if (!result.hasMxRecords) {
|
if (!result.hasMxRecords) {
|
||||||
result.valid = false;
|
result.valid = false;
|
||||||
result.reasons.push('No MX records found for domain');
|
result.reasons.push('Domain cannot receive email (no MX records found)');
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
result.hasMxRecords = false;
|
result.hasMxRecords = false;
|
||||||
result.valid = false;
|
result.valid = false;
|
||||||
result.reasons.push('No MX records found for domain');
|
result.reasons.push('Domain cannot receive email (no MX records found)');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if domain exists (has A/AAAA records) - informational only
|
// Step 3: Check if domain has A/AAAA records - informational only
|
||||||
// This doesn't affect validity since email delivery only requires MX records
|
// This indicates if the domain has a website/web server
|
||||||
|
// Doesn't affect email validity since email only requires MX records
|
||||||
try {
|
try {
|
||||||
await dns.resolve(domain, 'A');
|
await dns.resolve(domain, 'A');
|
||||||
result.domainExists = true;
|
result.hasWebsite = true;
|
||||||
} catch {
|
} catch {
|
||||||
// Try AAAA records if A records fail
|
// Try AAAA records if A records fail
|
||||||
try {
|
try {
|
||||||
await dns.resolve(domain, 'AAAA');
|
await dns.resolve(domain, 'AAAA');
|
||||||
result.domainExists = true;
|
result.hasWebsite = true;
|
||||||
} catch {
|
} catch {
|
||||||
// Domain doesn't have A/AAAA records, but this is OK if it has MX records
|
// Domain doesn't have A/AAAA records (no website), but this is OK for email
|
||||||
result.domainExists = false;
|
result.hasWebsite = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,4 +209,57 @@ export class EmailVerificationService {
|
|||||||
private static isForwardingDomain(domain: string): boolean {
|
private static isForwardingDomain(domain: string): boolean {
|
||||||
return FORWARDING_DOMAINS.has(domain.toLowerCase());
|
return FORWARDING_DOMAINS.has(domain.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch and cache the personal email domains list from GitHub
|
||||||
|
* Uses Redis for caching with 24-hour TTL
|
||||||
|
* Falls back to in-memory cache if Redis fails
|
||||||
|
*/
|
||||||
|
private static async getPersonalEmailDomains(): Promise<Set<string>> {
|
||||||
|
// Return in-memory cache if available
|
||||||
|
if (this.personalDomainsSet) {
|
||||||
|
return this.personalDomainsSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Try to get from Redis cache first
|
||||||
|
const cached = await redis.get(PERSONAL_DOMAINS_CACHE_KEY);
|
||||||
|
if (cached) {
|
||||||
|
const domains = JSON.parse(cached) as string[];
|
||||||
|
this.personalDomainsSet = new Set(domains);
|
||||||
|
return this.personalDomainsSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch from GitHub if not in cache
|
||||||
|
const response = await fetch(PERSONAL_DOMAINS_URL);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to fetch personal email domains: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = await response.text();
|
||||||
|
const domains = text
|
||||||
|
.split('\n')
|
||||||
|
.map(line => line.trim())
|
||||||
|
.filter(line => line && !line.startsWith('#')); // Filter empty lines and comments
|
||||||
|
|
||||||
|
// Cache in Redis
|
||||||
|
await redis.set(PERSONAL_DOMAINS_CACHE_KEY, JSON.stringify(domains), 'EX', CACHE_TTL_SECONDS);
|
||||||
|
|
||||||
|
// Cache in memory
|
||||||
|
this.personalDomainsSet = new Set(domains);
|
||||||
|
return this.personalDomainsSet;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching personal email domains:', error);
|
||||||
|
// Return empty set as fallback - don't block email verification
|
||||||
|
return new Set<string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a domain is a personal/free email provider
|
||||||
|
*/
|
||||||
|
private static async isPersonalEmailDomain(domain: string): Promise<boolean> {
|
||||||
|
const personalDomains = await this.getPersonalEmailDomains();
|
||||||
|
return personalDomains.has(domain.toLowerCase());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,16 @@ import {
|
|||||||
AlertCircle,
|
AlertCircle,
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
|
Forward,
|
||||||
Info,
|
Info,
|
||||||
Mail,
|
Mail,
|
||||||
Server,
|
Server,
|
||||||
Shield,
|
Shield,
|
||||||
Trash2,
|
Trash2,
|
||||||
|
User,
|
||||||
XCircle,
|
XCircle,
|
||||||
Forward,
|
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import type {EmailVerificationResult as VerificationResult} from '../../lib/emailVerification';
|
import type {EmailVerificationResult as VerificationResult} from '@plunk/types';
|
||||||
|
|
||||||
interface EmailVerificationResultProps {
|
interface EmailVerificationResultProps {
|
||||||
result: VerificationResult;
|
result: VerificationResult;
|
||||||
@@ -52,7 +53,7 @@ export function EmailVerificationResult({result}: EmailVerificationResultProps)
|
|||||||
<Server className="h-5 w-5 text-neutral-600" />
|
<Server className="h-5 w-5 text-neutral-600" />
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium text-neutral-900">Domain Exists</p>
|
<p className="font-medium text-neutral-900">Domain Exists</p>
|
||||||
<p className="text-sm text-neutral-600">DNS A/AAAA records found</p>
|
<p className="text-sm text-neutral-600">Domain has nameservers (NS records)</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{result.domainExists ? (
|
{result.domainExists ? (
|
||||||
@@ -78,6 +79,22 @@ export function EmailVerificationResult({result}: EmailVerificationResultProps)
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Website/A Records */}
|
||||||
|
<div className="flex items-center justify-between px-6 py-4">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Server className="h-5 w-5 text-neutral-600" />
|
||||||
|
<div>
|
||||||
|
<p className="font-medium text-neutral-900">Has Website</p>
|
||||||
|
<p className="text-sm text-neutral-600">DNS A/AAAA records found</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{result.hasWebsite ? (
|
||||||
|
<CheckCircle className="h-5 w-5 text-green-600" />
|
||||||
|
) : (
|
||||||
|
<Info className="h-5 w-5 text-neutral-400" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Disposable Email */}
|
{/* Disposable Email */}
|
||||||
<div className="flex items-center justify-between px-6 py-4">
|
<div className="flex items-center justify-between px-6 py-4">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
@@ -94,6 +111,22 @@ export function EmailVerificationResult({result}: EmailVerificationResultProps)
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Personal Email */}
|
||||||
|
<div className="flex items-center justify-between px-6 py-4">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<User className="h-5 w-5 text-neutral-600" />
|
||||||
|
<div>
|
||||||
|
<p className="font-medium text-neutral-900">Personal Email</p>
|
||||||
|
<p className="text-sm text-neutral-600">Free email provider (Gmail, Hotmail, etc.)</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{result.isPersonalEmail ? (
|
||||||
|
<Info className="h-5 w-5 text-blue-600" />
|
||||||
|
) : (
|
||||||
|
<span className="text-sm text-neutral-500">No</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Alias/Forwarding Email */}
|
{/* Alias/Forwarding Email */}
|
||||||
<div className="flex items-center justify-between px-6 py-4">
|
<div className="flex items-center justify-between px-6 py-4">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
|
|||||||
@@ -1,15 +1,4 @@
|
|||||||
export interface EmailVerificationResult {
|
import type {EmailVerificationResult} from '@plunk/types';
|
||||||
email: string;
|
|
||||||
valid: boolean;
|
|
||||||
isDisposable: boolean;
|
|
||||||
isAlias: boolean;
|
|
||||||
isTypo: boolean;
|
|
||||||
isPlusAddressed: boolean;
|
|
||||||
domainExists: boolean;
|
|
||||||
hasMxRecords: boolean;
|
|
||||||
suggestedEmail?: string;
|
|
||||||
reasons: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function verifyEmail(email: string): Promise<EmailVerificationResult> {
|
export async function verifyEmail(email: string): Promise<EmailVerificationResult> {
|
||||||
const response = await fetch('/api/verify-email', {
|
const response = await fetch('/api/verify-email', {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {CheckCircle, Mail, Search, Shield} from 'lucide-react';
|
import {CheckCircle, Mail, Search, Shield, User} from 'lucide-react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Educational content for the email verification tool
|
* Educational content for the email verification tool
|
||||||
@@ -29,6 +29,11 @@ export const EMAIL_VERIFICATION_FEATURES = [
|
|||||||
description: 'Identify temporary email addresses that are often used for spam or fake signups.',
|
description: 'Identify temporary email addresses that are often used for spam or fake signups.',
|
||||||
icon: Mail,
|
icon: Mail,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Personal Email Detection',
|
||||||
|
description: 'Detect personal/free email providers like Gmail, Hotmail, Yahoo for B2B validation.',
|
||||||
|
icon: User,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'Plus Addressing',
|
title: 'Plus Addressing',
|
||||||
description: 'Detect plus-addressed emails ([email protected]) which can be useful for tracking.',
|
description: 'Detect plus-addressed emails ([email protected]) which can be useful for tracking.',
|
||||||
|
|||||||
@@ -1,24 +1,16 @@
|
|||||||
import type {NextApiRequest, NextApiResponse} from 'next';
|
import type {NextApiRequest, NextApiResponse} from 'next';
|
||||||
|
import type {EmailVerificationResult} from '@plunk/types';
|
||||||
import {UtilitySchemas} from '@plunk/shared';
|
import {UtilitySchemas} from '@plunk/shared';
|
||||||
import {API_URI} from '../../lib/constants';
|
import {API_URI} from '../../lib/constants';
|
||||||
|
|
||||||
interface VerifyEmailResponse {
|
|
||||||
email: string;
|
|
||||||
valid: boolean;
|
|
||||||
isDisposable: boolean;
|
|
||||||
isTypo: boolean;
|
|
||||||
isPlusAddressed: boolean;
|
|
||||||
domainExists: boolean;
|
|
||||||
hasMxRecords: boolean;
|
|
||||||
suggestedEmail?: string;
|
|
||||||
reasons: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ErrorResponse {
|
interface ErrorResponse {
|
||||||
error: string;
|
error: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<VerifyEmailResponse | ErrorResponse>) {
|
export default async function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse<EmailVerificationResult | ErrorResponse>,
|
||||||
|
) {
|
||||||
// Only allow POST requests
|
// Only allow POST requests
|
||||||
if (req.method !== 'POST') {
|
if (req.method !== 'POST') {
|
||||||
return res.status(405).json({error: 'Method not allowed'});
|
return res.status(405).json({error: 'Method not allowed'});
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import {DASHBOARD_URI} from '../../lib/constants';
|
|||||||
import React, {useState} from 'react';
|
import React, {useState} from 'react';
|
||||||
import {NextSeo} from 'next-seo';
|
import {NextSeo} from 'next-seo';
|
||||||
import {ArrowRight, CheckCircle, Loader2, Search} from 'lucide-react';
|
import {ArrowRight, CheckCircle, Loader2, Search} from 'lucide-react';
|
||||||
import {type EmailVerificationResult as VerificationResult, verifyEmail} from '../../lib/emailVerification';
|
import type {EmailVerificationResult as VerificationResult} from '@plunk/types';
|
||||||
|
import {verifyEmail} from '../../lib/emailVerification';
|
||||||
import {EmailVerificationResult} from '../../components/tools/EmailVerificationResult';
|
import {EmailVerificationResult} from '../../components/tools/EmailVerificationResult';
|
||||||
import {Button, Input} from '@plunk/ui';
|
import {Button, Input} from '@plunk/ui';
|
||||||
import {EMAIL_VERIFICATION_FEATURES} from '../../lib/toolsContent';
|
import {EMAIL_VERIFICATION_FEATURES} from '../../lib/toolsContent';
|
||||||
|
|||||||
+22
-3
@@ -596,7 +596,7 @@
|
|||||||
"post": {
|
"post": {
|
||||||
"tags": ["Public API"],
|
"tags": ["Public API"],
|
||||||
"summary": "Verify email address",
|
"summary": "Verify email address",
|
||||||
"description": "Verify an email address for validity, check if it's from a disposable domain, verify MX records, and detect potential typos with suggestions.",
|
"description": "Verify an email address for validity, check if it's from a disposable domain or personal email provider, verify MX records, and detect potential typos with suggestions.",
|
||||||
"operationId": "verifyEmail",
|
"operationId": "verifyEmail",
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
"required": true,
|
"required": true,
|
||||||
@@ -676,9 +676,17 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Whether the email uses plus addressing (contains a + in the local part)"
|
"description": "Whether the email uses plus addressing (contains a + in the local part)"
|
||||||
},
|
},
|
||||||
|
"isPersonalEmail": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether the email is from a personal/free email provider (Gmail, Hotmail, Yahoo, etc.)"
|
||||||
|
},
|
||||||
"domainExists": {
|
"domainExists": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Whether the domain exists (has DNS A or AAAA records)"
|
"description": "Whether the domain exists in DNS (has NS records)"
|
||||||
|
},
|
||||||
|
"hasWebsite": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether the domain has a website (has DNS A or AAAA records) - informational only"
|
||||||
},
|
},
|
||||||
"hasMxRecords": {
|
"hasMxRecords": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@@ -705,7 +713,9 @@
|
|||||||
"isAlias",
|
"isAlias",
|
||||||
"isTypo",
|
"isTypo",
|
||||||
"isPlusAddressed",
|
"isPlusAddressed",
|
||||||
|
"isPersonalEmail",
|
||||||
"domainExists",
|
"domainExists",
|
||||||
|
"hasWebsite",
|
||||||
"hasMxRecords",
|
"hasMxRecords",
|
||||||
"reasons"
|
"reasons"
|
||||||
]
|
]
|
||||||
@@ -724,7 +734,9 @@
|
|||||||
"isAlias": false,
|
"isAlias": false,
|
||||||
"isTypo": false,
|
"isTypo": false,
|
||||||
"isPlusAddressed": false,
|
"isPlusAddressed": false,
|
||||||
|
"isPersonalEmail": true,
|
||||||
"domainExists": true,
|
"domainExists": true,
|
||||||
|
"hasWebsite": true,
|
||||||
"hasMxRecords": true,
|
"hasMxRecords": true,
|
||||||
"reasons": ["Email appears to be valid"]
|
"reasons": ["Email appears to be valid"]
|
||||||
}
|
}
|
||||||
@@ -741,10 +753,15 @@
|
|||||||
"isAlias": false,
|
"isAlias": false,
|
||||||
"isTypo": true,
|
"isTypo": true,
|
||||||
"isPlusAddressed": false,
|
"isPlusAddressed": false,
|
||||||
|
"isPersonalEmail": false,
|
||||||
"domainExists": false,
|
"domainExists": false,
|
||||||
|
"hasWebsite": false,
|
||||||
"hasMxRecords": false,
|
"hasMxRecords": false,
|
||||||
"suggestedEmail": "[email protected]",
|
"suggestedEmail": "[email protected]",
|
||||||
"reasons": ["Possible typo detected, did you mean gmail.com?", "Domain does not exist"]
|
"reasons": [
|
||||||
|
"Possible typo detected, did you mean gmail.com?",
|
||||||
|
"Domain does not exist (no nameservers found)"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -759,7 +776,9 @@
|
|||||||
"isAlias": false,
|
"isAlias": false,
|
||||||
"isTypo": false,
|
"isTypo": false,
|
||||||
"isPlusAddressed": false,
|
"isPlusAddressed": false,
|
||||||
|
"isPersonalEmail": false,
|
||||||
"domainExists": true,
|
"domainExists": true,
|
||||||
|
"hasWebsite": true,
|
||||||
"hasMxRecords": true,
|
"hasMxRecords": true,
|
||||||
"reasons": ["Email appears to be valid"]
|
"reasons": ["Email appears to be valid"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,10 @@ export interface EmailVerificationResult {
|
|||||||
isAlias: boolean;
|
isAlias: boolean;
|
||||||
isTypo: boolean;
|
isTypo: boolean;
|
||||||
isPlusAddressed: boolean;
|
isPlusAddressed: boolean;
|
||||||
domainExists: boolean;
|
isPersonalEmail: boolean; // Email is from a personal/free provider (Gmail, Hotmail, etc.)
|
||||||
hasMxRecords: boolean;
|
domainExists: boolean; // Domain exists in DNS (has NS records)
|
||||||
|
hasWebsite: boolean; // Domain has A/AAAA records (informational - not required for email)
|
||||||
|
hasMxRecords: boolean; // Domain has MX records (required for receiving email)
|
||||||
suggestedEmail?: string;
|
suggestedEmail?: string;
|
||||||
reasons: string[];
|
reasons: string[];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user