1219 lines
45 KiB
JavaScript
1219 lines
45 KiB
JavaScript
// whois.js - 域名信息查询页面功能
|
|
|
|
// 全局变量
|
|
let whoisChart = null;
|
|
let currentDomain = '';
|
|
let isLoading = false;
|
|
let currentPageSize = 10; // 当前每页显示数量
|
|
|
|
// IP 地理位置缓存(与 logs.js 共享)
|
|
if (typeof ipGeolocationCache === 'undefined') {
|
|
var ipGeolocationCache = {};
|
|
var ipGeolocationCacheOrder = [];
|
|
var GEOLOCATION_CACHE_MAX_SIZE = 1000;
|
|
var GEOLOCATION_CACHE_EXPIRY = 24 * 60 * 60 * 1000;
|
|
}
|
|
|
|
// LRU 缓存辅助函数:更新缓存顺序,将指定 IP 移到最后(最近使用)
|
|
function updateCacheOrder(ip) {
|
|
// 确保变量存在
|
|
if (typeof ipGeolocationCacheOrder === 'undefined') {
|
|
window.ipGeolocationCacheOrder = [];
|
|
}
|
|
const index = ipGeolocationCacheOrder.indexOf(ip);
|
|
if (index > -1) {
|
|
// 已存在,移除旧位置
|
|
ipGeolocationCacheOrder.splice(index, 1);
|
|
}
|
|
// 添加到末尾(最近使用)
|
|
ipGeolocationCacheOrder.push(ip);
|
|
}
|
|
|
|
// LRU 缓存辅助函数:淘汰最少使用的缓存项
|
|
function evictLRUCache() {
|
|
// 确保变量存在
|
|
if (typeof ipGeolocationCacheOrder === 'undefined' || typeof GEOLOCATION_CACHE_MAX_SIZE === 'undefined') {
|
|
return; // 变量未初始化,直接返回
|
|
}
|
|
while (ipGeolocationCacheOrder.length >= GEOLOCATION_CACHE_MAX_SIZE) {
|
|
// 淘汰最久未使用的(数组第一个)
|
|
const oldestIP = ipGeolocationCacheOrder.shift();
|
|
if (oldestIP) {
|
|
delete ipGeolocationCache[oldestIP];
|
|
}
|
|
}
|
|
}
|
|
|
|
// LRU 缓存辅助函数:清理过期缓存
|
|
function cleanupExpiredCache() {
|
|
// 简化处理:缓存直接存储 API 返回的数据对象,不检查过期时间
|
|
// 仅通过 LRU 机制在 evictLRUCache 中控制缓存大小
|
|
}
|
|
|
|
// 模拟的 ICP 备案信息(作为备用)
|
|
function generateMockICPInfo(domain) {
|
|
const mockICP = [
|
|
{
|
|
company: '华为软件技术有限公司',
|
|
license: '苏 ICP 备 17040376 号 -24',
|
|
type: '企业',
|
|
date: '2025-04-22 09:32:23'
|
|
},
|
|
{
|
|
company: '腾讯科技(深圳)有限公司',
|
|
license: '粤 ICP 备 10045678 号 -12',
|
|
type: '企业',
|
|
date: '2024-12-15 14:20:10'
|
|
},
|
|
{
|
|
company: '阿里巴巴云计算(北京)有限公司',
|
|
license: '京 ICP 备 15023456 号 -8',
|
|
type: '企业',
|
|
date: '2025-01-08 11:45:30'
|
|
},
|
|
{
|
|
company: '百度在线网络技术有限公司',
|
|
license: '京 ICP 备 12034567 号 -5',
|
|
type: '企业',
|
|
date: '2024-11-20 16:15:45'
|
|
}
|
|
];
|
|
|
|
// 根据域名选择一个 ICP 信息
|
|
const index = domain.length % mockICP.length;
|
|
return mockICP[index];
|
|
}
|
|
|
|
// 模拟的 DNS 解析结果(作为备用)
|
|
function generateMockDNSResults(domain) {
|
|
const locations = ['中国 - 江苏省', '中国 - 广东省', '中国 - 浙江省', '中国 - 上海市', '中国 - 北京市'];
|
|
const carriers = ['中国电信', '中国联通', '中国移动', '中国教育网', '中国科技网'];
|
|
|
|
const results = [];
|
|
const count = 16 + Math.floor(Math.random() * 10);
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
const ip = `${Math.floor(Math.random() * 256)}.${Math.floor(Math.random() * 256)}.${Math.floor(Math.random() * 256)}.${Math.floor(Math.random() * 256)}`;
|
|
const location = locations[Math.floor(Math.random() * locations.length)];
|
|
const carrier = carriers[Math.floor(Math.random() * carriers.length)];
|
|
|
|
results.push({
|
|
ip: ip,
|
|
location: location,
|
|
carrier: carrier
|
|
});
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
// 模拟的 WHOIS 信息(作为备用)
|
|
function generateMockWhoisInfo(domain) {
|
|
return {
|
|
registrant: '该域名已开通隐私保护',
|
|
created: '2020-01-01 00:00:00',
|
|
email: '该域名已开通隐私保护',
|
|
expires: '2027-01-01 00:00:00',
|
|
registrar: '未知注册商',
|
|
nameservers: 'ns1.example.com, ns2.example.com'
|
|
};
|
|
}
|
|
|
|
// 模拟的访问趋势数据
|
|
function generateMockTrendData() {
|
|
const labels = [];
|
|
const totalData = [];
|
|
const threatData = [];
|
|
const blockData = [];
|
|
|
|
// 生成最近 30 天的数据
|
|
const now = new Date();
|
|
for (let i = 29; i >= 0; i--) {
|
|
const date = new Date(now);
|
|
date.setDate(date.getDate() - i);
|
|
labels.push(`${date.getMonth() + 1}-${date.getDate()}`);
|
|
|
|
// 生成随机数据
|
|
const total = Math.floor(Math.random() * 100000) + 50000;
|
|
const threat = Math.floor(Math.random() * 1000);
|
|
const block = Math.floor(Math.random() * 5000);
|
|
|
|
totalData.push(total);
|
|
threatData.push(threat);
|
|
blockData.push(block);
|
|
}
|
|
|
|
return {
|
|
labels: labels,
|
|
totalData: totalData,
|
|
threatData: threatData,
|
|
blockData: blockData
|
|
};
|
|
}
|
|
|
|
// 显示加载状态
|
|
function showLoading() {
|
|
isLoading = true;
|
|
const searchBtn = document.getElementById('whois-search-btn');
|
|
if (searchBtn) {
|
|
searchBtn.disabled = true;
|
|
searchBtn.innerHTML = '<i class="fa fa-spinner fa-spin mr-2"></i>查询中...';
|
|
}
|
|
|
|
// 显示加载提示
|
|
const loadingDiv = document.getElementById('whois-loading');
|
|
if (loadingDiv) {
|
|
loadingDiv.classList.remove('hidden');
|
|
}
|
|
}
|
|
|
|
// 隐藏加载状态
|
|
function hideLoading() {
|
|
isLoading = false;
|
|
const searchBtn = document.getElementById('whois-search-btn');
|
|
if (searchBtn) {
|
|
searchBtn.disabled = false;
|
|
searchBtn.innerHTML = '查询';
|
|
}
|
|
|
|
// 隐藏加载提示
|
|
const loadingDiv = document.getElementById('whois-loading');
|
|
if (loadingDiv) {
|
|
loadingDiv.classList.add('hidden');
|
|
}
|
|
}
|
|
|
|
// 调用 WHOIS API
|
|
async function fetchWhoisInfo(domain) {
|
|
try {
|
|
const response = await fetch(`https://uapis.cn/api/v1/network/whois?format=json&domain=${encodeURIComponent(domain)}`, {
|
|
method: 'GET',
|
|
headers: {
|
|
'Authorization': 'Bearer uapi-pnhxbxhkCYUhyS-3r45avJLjTB-qEB8HaNMDzmrT'
|
|
},
|
|
timeout: 10000 // 10秒超时
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
return data;
|
|
} catch (error) {
|
|
console.error('获取 WHOIS 信息失败:', error);
|
|
// 返回 null 而不是抛出错误,让上层处理
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// 调用 ICP 备案信息 API
|
|
async function fetchICPInfo(domain) {
|
|
try {
|
|
const response = await fetch(`https://uapis.cn/api/v1/network/icp?domain=${encodeURIComponent(domain)}&format=json`, {
|
|
method: 'GET',
|
|
headers: {
|
|
'Authorization': 'Bearer uapi-pnhxbxhkCYUhyS-3r45avJLjTB-qEB8HaNMDzmrT'
|
|
},
|
|
timeout: 10000 // 10秒超时
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
return data;
|
|
} catch (error) {
|
|
console.error('获取 ICP 备案信息失败:', error);
|
|
// 返回 null 而不是抛出错误,让上层处理
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// 调用 IP 地理位置 API
|
|
async function fetchIPInfo(ip) {
|
|
try {
|
|
// 确保缓存变量已初始化
|
|
if (typeof ipGeolocationCache === 'undefined') {
|
|
console.log('缓存变量未初始化,正在初始化...');
|
|
window.ipGeolocationCache = {};
|
|
window.ipGeolocationCacheOrder = [];
|
|
window.GEOLOCATION_CACHE_MAX_SIZE = 1000;
|
|
window.GEOLOCATION_CACHE_EXPIRY = 24 * 60 * 60 * 1000;
|
|
}
|
|
|
|
console.log('缓存变量状态:', {
|
|
cache: typeof ipGeolocationCache,
|
|
order: typeof ipGeolocationCacheOrder,
|
|
orderValue: ipGeolocationCacheOrder
|
|
});
|
|
|
|
// 清理过期缓存
|
|
cleanupExpiredCache();
|
|
|
|
// 检查缓存(缓存直接存储数据对象)
|
|
if (ipGeolocationCache[ip]) {
|
|
console.log('缓存命中:', ip);
|
|
updateCacheOrder(ip);
|
|
// 直接返回缓存的数据对象(扁平结构)
|
|
return ipGeolocationCache[ip];
|
|
}
|
|
|
|
console.log('缓存未命中,请求 API:', ip);
|
|
const response = await fetch(`https://uapis.cn/api/v1/network/ipinfo?ip=${encodeURIComponent(ip)}&source=commercial`, {
|
|
method: 'GET',
|
|
headers: {
|
|
'Authorization': 'Bearer uapi-pnhxbxhkCYUhyS-3r45avJLjTB-qEB8HaNMDzmrT'
|
|
},
|
|
timeout: 5000 // 5 秒超时
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
console.log('API 返回数据:', data);
|
|
|
|
// 存储到缓存(直接存储数据对象,不嵌套)
|
|
ipGeolocationCache[ip] = data;
|
|
console.log('存储缓存前 order:', ipGeolocationCacheOrder);
|
|
updateCacheOrder(ip);
|
|
console.log('存储缓存后 order:', ipGeolocationCacheOrder);
|
|
evictLRUCache();
|
|
|
|
return data;
|
|
} catch (error) {
|
|
console.error(`获取 IP 地理位置信息失败 (${ip}):`, error);
|
|
// 返回默认值(扁平结构)
|
|
return {
|
|
region: '未知',
|
|
isp: '未知'
|
|
};
|
|
}
|
|
}
|
|
|
|
// 获取最近 7 天的 DNS 解析记录
|
|
async function fetchDNSLogs(domain) {
|
|
try {
|
|
// 构建请求 URL
|
|
const endpoint = `/api/logs/query?search=${encodeURIComponent(domain)}&limit=1000`;
|
|
|
|
console.log('请求 DNS 日志:', endpoint);
|
|
|
|
// 发送请求
|
|
const response = await fetch(endpoint, {
|
|
timeout: 10000 // 10 秒超时
|
|
});
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
const logs = await response.json();
|
|
console.log('获取到 DNS 日志:', logs.length, '条');
|
|
|
|
// 过滤出包含 IP 地址的记录
|
|
const ipRecords = [];
|
|
const seenIPs = new Set();
|
|
|
|
// 遍历日志记录
|
|
for (const log of logs) {
|
|
// 检查是否包含 answers 字段
|
|
if (log.answers && Array.isArray(log.answers)) {
|
|
// 遍历 answers 数组
|
|
for (const answer of log.answers) {
|
|
// 检查 answer 是否有 value 字段
|
|
if (answer.value) {
|
|
// 提取 IP 地址
|
|
const ipMatches = answer.value.match(/\b(?:\d{1,3}\.){3}\d{1,3}\b/g);
|
|
if (ipMatches) {
|
|
console.log(`从 answer.value 提取到 IP:`, ipMatches);
|
|
for (const ip of ipMatches) {
|
|
if (!seenIPs.has(ip)) {
|
|
seenIPs.add(ip);
|
|
ipRecords.push({
|
|
ip: ip,
|
|
timestamp: log.timestamp
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log('最终提取到 IP 记录:', ipRecords.length, '个');
|
|
return ipRecords;
|
|
} catch (error) {
|
|
console.error('获取 DNS 解析记录失败:', error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
// 解析字符串格式的 WHOIS 信息
|
|
function parseWhoisString(whoisString) {
|
|
const result = {
|
|
registrant: '-',
|
|
created: '-',
|
|
email: '-',
|
|
expires: '-',
|
|
registrar: '-',
|
|
nameservers: '-'
|
|
};
|
|
|
|
if (!whoisString) {
|
|
return result;
|
|
}
|
|
|
|
const lines = whoisString.split('\n');
|
|
|
|
for (const line of lines) {
|
|
const trimmedLine = line.trim();
|
|
|
|
// 注册者
|
|
if (trimmedLine.startsWith('Registrant: ')) {
|
|
const value = trimmedLine.substring('Registrant: '.length).trim();
|
|
if (value) {
|
|
result.registrant = value;
|
|
}
|
|
}
|
|
|
|
// 注册邮箱
|
|
else if (trimmedLine.startsWith('Registrant Contact Email: ')) {
|
|
const value = trimmedLine.substring('Registrant Contact Email: '.length).trim();
|
|
if (value) {
|
|
result.email = value;
|
|
}
|
|
}
|
|
|
|
// 注册时间(多种格式)
|
|
else if (trimmedLine.startsWith('Registration Time: ') ||
|
|
trimmedLine.startsWith('Creation Date: ') ||
|
|
trimmedLine.startsWith('Created Date: ')) {
|
|
let prefix = '';
|
|
if (trimmedLine.startsWith('Registration Time: ')) {
|
|
prefix = 'Registration Time: ';
|
|
} else if (trimmedLine.startsWith('Creation Date: ')) {
|
|
prefix = 'Creation Date: ';
|
|
} else if (trimmedLine.startsWith('Created Date: ')) {
|
|
prefix = 'Created Date: ';
|
|
}
|
|
|
|
let value = trimmedLine.substring(prefix.length).trim();
|
|
if (value) {
|
|
// 处理 ISO 格式的时间 (YYYY-MM-DDTHH:MM:SSZ 或 YYYY-MM-DDTHH:MM:SS+0000)
|
|
if (value.includes('T')) {
|
|
value = value.replace('T', ' ');
|
|
if (value.includes('Z')) {
|
|
value = value.replace('Z', '');
|
|
} else if (value.includes('+')) {
|
|
value = value.split('+')[0];
|
|
}
|
|
}
|
|
result.created = value;
|
|
}
|
|
}
|
|
|
|
// 过期时间(多种格式)
|
|
else if (trimmedLine.startsWith('Expiration Time: ') ||
|
|
trimmedLine.startsWith('Registry Expiry Date: ') ||
|
|
trimmedLine.startsWith('Registrar Registration Expiration Date: ') ||
|
|
trimmedLine.startsWith('Expiry Date: ')) {
|
|
let prefix = '';
|
|
if (trimmedLine.startsWith('Expiration Time: ')) {
|
|
prefix = 'Expiration Time: ';
|
|
} else if (trimmedLine.startsWith('Registry Expiry Date: ')) {
|
|
prefix = 'Registry Expiry Date: ';
|
|
} else if (trimmedLine.startsWith('Registrar Registration Expiration Date: ')) {
|
|
prefix = 'Registrar Registration Expiration Date: ';
|
|
} else if (trimmedLine.startsWith('Expiry Date: ')) {
|
|
prefix = 'Expiry Date: ';
|
|
}
|
|
|
|
let value = trimmedLine.substring(prefix.length).trim();
|
|
if (value) {
|
|
// 处理 ISO 格式的时间 (YYYY-MM-DDTHH:MM:SSZ 或 YYYY-MM-DDTHH:MM:SS+0000)
|
|
if (value.includes('T')) {
|
|
value = value.replace('T', ' ');
|
|
if (value.includes('Z')) {
|
|
value = value.replace('Z', '');
|
|
} else if (value.includes('+')) {
|
|
value = value.split('+')[0];
|
|
}
|
|
}
|
|
result.expires = value;
|
|
}
|
|
}
|
|
|
|
// 域名服务商
|
|
else if (trimmedLine.startsWith('Sponsoring Registrar: ')) {
|
|
const value = trimmedLine.substring('Sponsoring Registrar: '.length).trim();
|
|
if (value) {
|
|
result.registrar = value;
|
|
}
|
|
}
|
|
|
|
// 域名服务器
|
|
else if (trimmedLine.startsWith('Name Server: ')) {
|
|
const value = trimmedLine.substring('Name Server: '.length).trim();
|
|
if (value) {
|
|
if (result.nameservers === '-') {
|
|
result.nameservers = value;
|
|
} else {
|
|
result.nameservers += ', ' + value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// 格式化 WHOIS 信息
|
|
function formatWhoisInfo(whoisData) {
|
|
const result = {
|
|
registrant: '-',
|
|
created: '-',
|
|
email: '-',
|
|
expires: '-',
|
|
registrar: '-',
|
|
nameservers: '-'
|
|
};
|
|
|
|
// 处理 API 返回字符串的情况
|
|
if (!whoisData) {
|
|
return result;
|
|
}
|
|
|
|
// 如果 whois 字段是字符串,说明返回的是文本格式的 WHOIS 数据
|
|
if (typeof whoisData.whois === 'string') {
|
|
return parseWhoisString(whoisData.whois);
|
|
}
|
|
|
|
if (!whoisData.whois) {
|
|
return result;
|
|
}
|
|
|
|
const whois = whoisData.whois;
|
|
const domain = whois.domain || {};
|
|
const registrar = whois.registrar || {};
|
|
const registrant = whois.registrant || {};
|
|
|
|
// 处理注册者信息(优先使用 registrant,没有则显示域名服务商)
|
|
if (registrant.name) {
|
|
if (registrant.name.includes('REDACTED FOR PRIVACY') || registrant.name === 'REDACTED FOR PRIVACY') {
|
|
result.registrant = '该域名已开通隐私保护';
|
|
} else {
|
|
result.registrant = registrant.name;
|
|
}
|
|
} else if (registrant.organization) {
|
|
if (registrant.organization.includes('REDACTED FOR PRIVACY') || registrant.organization === 'REDACTED FOR PRIVACY') {
|
|
result.registrant = '该域名已开通隐私保护';
|
|
} else {
|
|
result.registrant = registrant.organization;
|
|
}
|
|
} else {
|
|
// 如果没有注册者信息,默认显示隐私保护
|
|
result.registrant = '该域名已开通隐私保护';
|
|
}
|
|
|
|
// 处理注册时间
|
|
if (domain.created_date_in_time) {
|
|
result.created = domain.created_date_in_time.replace('T', ' ').replace('Z', '');
|
|
} else if (domain.created_date) {
|
|
result.created = domain.created_date.replace('T', ' ').replace('Z', '');
|
|
}
|
|
|
|
// 处理注册邮箱(优先使用 registrant 的邮箱,没有则使用 registrar 的邮箱)
|
|
if (registrant.email) {
|
|
if (registrant.email.includes('REDACTED FOR PRIVACY') || registrant.email === 'REDACTED FOR PRIVACY') {
|
|
result.email = '该域名已开通隐私保护';
|
|
} else {
|
|
result.email = registrant.email;
|
|
}
|
|
} else if (registrar.email) {
|
|
result.email = registrar.email;
|
|
}
|
|
|
|
// 处理过期时间
|
|
if (domain.expiration_date_in_time) {
|
|
result.expires = domain.expiration_date_in_time.replace('T', ' ').replace('Z', '');
|
|
} else if (domain.expiration_date) {
|
|
result.expires = domain.expiration_date.replace('T', ' ').replace('Z', '');
|
|
}
|
|
|
|
// 处理域名服务商
|
|
if (registrar.name) {
|
|
result.registrar = registrar.name;
|
|
}
|
|
|
|
// 处理域名服务器
|
|
if (domain.name_servers && Array.isArray(domain.name_servers)) {
|
|
result.nameservers = domain.name_servers.join(', ');
|
|
} else if (domain.name_servers && typeof domain.name_servers === 'string') {
|
|
result.nameservers = domain.name_servers;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// 格式化 ICP 备案信息
|
|
function formatICPInfo(icpData) {
|
|
const result = {
|
|
company: '-',
|
|
license: '-',
|
|
type: '-',
|
|
date: '-'
|
|
};
|
|
|
|
if (!icpData || icpData.code !== '200') {
|
|
return result;
|
|
}
|
|
|
|
if (icpData.unitName) {
|
|
result.company = icpData.unitName;
|
|
}
|
|
|
|
if (icpData.serviceLicence) {
|
|
result.license = icpData.serviceLicence;
|
|
}
|
|
|
|
if (icpData.natureName) {
|
|
result.type = icpData.natureName;
|
|
}
|
|
|
|
// API 没有返回审核时间,使用当前时间作为默认值
|
|
const now = new Date();
|
|
result.date = now.toISOString().slice(0, 19).replace('T', ' ');
|
|
|
|
return result;
|
|
}
|
|
|
|
// 更新 WHOIS 信息显示
|
|
function updateWhoisInfo(info) {
|
|
document.getElementById('whois-registrant').textContent = info.registrant || '-';
|
|
document.getElementById('whois-created').textContent = info.created || '-';
|
|
document.getElementById('whois-email').textContent = info.email || '-';
|
|
document.getElementById('whois-expires').textContent = info.expires || '-';
|
|
document.getElementById('whois-registrar').textContent = info.registrar || '-';
|
|
document.getElementById('whois-nameservers').textContent = info.nameservers || '-';
|
|
}
|
|
|
|
// 更新 ICP 备案信息
|
|
function updateICPInfo(icpInfo) {
|
|
document.getElementById('icp-company').textContent = icpInfo.company || '-';
|
|
document.getElementById('icp-license').textContent = icpInfo.license || '-';
|
|
document.getElementById('icp-type').textContent = icpInfo.type || '-';
|
|
document.getElementById('icp-date').textContent = icpInfo.date || '-';
|
|
}
|
|
|
|
// 更新 DNS 解析结果表格
|
|
function updateDNSResults(results, page = 1, pageSize = 10) {
|
|
const tbody = document.getElementById('dns-results-body');
|
|
if (!tbody) return;
|
|
|
|
// 如果没有数据,显示一行"-"
|
|
if (!results || results.length === 0) {
|
|
tbody.innerHTML = `
|
|
<tr>
|
|
<td class="px-4 py-3 text-sm text-gray-900 dark:text-white text-center" colspan="3">-</td>
|
|
</tr>
|
|
`;
|
|
document.getElementById('dns-results-total').textContent = '共计 0 条';
|
|
|
|
// 清空分页
|
|
const pagination = document.getElementById('dns-results-pagination');
|
|
if (pagination) {
|
|
pagination.innerHTML = '';
|
|
}
|
|
return;
|
|
}
|
|
|
|
const totalPages = Math.ceil(results.length / pageSize);
|
|
const start = (page - 1) * pageSize;
|
|
const end = start + pageSize;
|
|
const pageResults = results.slice(start, end);
|
|
|
|
tbody.innerHTML = '';
|
|
|
|
pageResults.forEach(result => {
|
|
const tr = document.createElement('tr');
|
|
tr.innerHTML = `
|
|
<td class="px-4 py-3 text-sm text-gray-900 dark:text-white">${result.ip}</td>
|
|
<td class="px-4 py-3 text-sm text-gray-900 dark:text-white">${result.location}</td>
|
|
<td class="px-4 py-3 text-sm text-gray-900 dark:text-white">${result.carrier}</td>
|
|
`;
|
|
tbody.appendChild(tr);
|
|
});
|
|
|
|
// 更新总数
|
|
document.getElementById('dns-results-total').textContent = `共计 ${results.length} 条`;
|
|
|
|
// 更新分页
|
|
updateDNSPagination(results, page, pageSize);
|
|
}
|
|
|
|
// 更新 DNS 解析结果分页
|
|
function updateDNSPagination(results, currentPage, pageSize) {
|
|
const pagination = document.getElementById('dns-results-pagination');
|
|
if (!pagination) return;
|
|
|
|
const totalPages = Math.ceil(results.length / pageSize);
|
|
|
|
pagination.innerHTML = '';
|
|
|
|
// 创建分页容器(使用 flex 布局)
|
|
const container = document.createElement('div');
|
|
container.className = 'flex items-center space-x-2 flex-wrap gap-2';
|
|
|
|
// 每页数量筛选器
|
|
const pageSizeLabel = document.createElement('span');
|
|
pageSizeLabel.className = 'text-sm text-gray-600 dark:text-gray-400';
|
|
pageSizeLabel.textContent = '每页:';
|
|
container.appendChild(pageSizeLabel);
|
|
|
|
const pageSizeSelect = document.createElement('select');
|
|
pageSizeSelect.className = 'px-2 py-1 text-sm border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white rounded-md focus:outline-none focus:ring-2 focus:ring-primary';
|
|
pageSizeSelect.value = pageSize;
|
|
[3, 5, 10, 20, 50, 100].forEach(size => {
|
|
const option = document.createElement('option');
|
|
option.value = size;
|
|
option.textContent = size;
|
|
pageSizeSelect.appendChild(option);
|
|
});
|
|
pageSizeSelect.onchange = () => {
|
|
const newSize = parseInt(pageSizeSelect.value);
|
|
console.log('改变每页数量:', currentPageSize, '->', newSize);
|
|
console.log('当前结果数量:', results ? results.length : 0);
|
|
currentPageSize = newSize;
|
|
updateDNSResults(results, 1, currentPageSize);
|
|
};
|
|
container.appendChild(pageSizeSelect);
|
|
|
|
// 分隔符
|
|
const separator = document.createElement('span');
|
|
separator.className = 'text-sm text-gray-600 dark:text-gray-400 mx-2';
|
|
separator.textContent = '|';
|
|
container.appendChild(separator);
|
|
|
|
// 上一页按钮
|
|
const prevBtn = document.createElement('button');
|
|
prevBtn.className = `px-3 py-1 rounded-md ${currentPage === 1 ? 'text-gray-400 cursor-not-allowed' : 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'}`;
|
|
prevBtn.textContent = '<';
|
|
prevBtn.disabled = currentPage === 1;
|
|
prevBtn.onclick = () => {
|
|
if (currentPage > 1) {
|
|
updateDNSResults(results, currentPage - 1, pageSize);
|
|
}
|
|
};
|
|
container.appendChild(prevBtn);
|
|
|
|
// 页码按钮(智能显示,避免过多)
|
|
const maxVisiblePages = 5; // 最多显示的页码数
|
|
let startPage = Math.max(1, currentPage - Math.floor(maxVisiblePages / 2));
|
|
let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1);
|
|
|
|
// 调整起始页,确保始终显示 maxVisiblePages 个页码(如果可能)
|
|
if (endPage - startPage + 1 < maxVisiblePages) {
|
|
startPage = Math.max(1, endPage - maxVisiblePages + 1);
|
|
}
|
|
|
|
// 显示第一页
|
|
if (startPage > 1) {
|
|
const firstBtn = document.createElement('button');
|
|
firstBtn.className = `px-3 py-1 rounded-md ${1 === currentPage ? 'bg-primary text-white' : 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'}`;
|
|
firstBtn.textContent = '1';
|
|
firstBtn.onclick = () => {
|
|
updateDNSResults(results, 1, pageSize);
|
|
};
|
|
container.appendChild(firstBtn);
|
|
|
|
// 显示省略号
|
|
if (startPage > 2) {
|
|
const ellipsis = document.createElement('span');
|
|
ellipsis.className = 'text-gray-600 dark:text-gray-400 px-2';
|
|
ellipsis.textContent = '...';
|
|
container.appendChild(ellipsis);
|
|
}
|
|
}
|
|
|
|
// 显示中间的页码
|
|
for (let i = startPage; i <= endPage; i++) {
|
|
const pageBtn = document.createElement('button');
|
|
pageBtn.className = `px-3 py-1 rounded-md ${i === currentPage ? 'bg-primary text-white' : 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'}`;
|
|
pageBtn.textContent = i;
|
|
pageBtn.onclick = () => {
|
|
updateDNSResults(results, i, pageSize);
|
|
};
|
|
container.appendChild(pageBtn);
|
|
}
|
|
|
|
// 显示最后页
|
|
if (endPage < totalPages) {
|
|
// 显示省略号
|
|
if (endPage < totalPages - 1) {
|
|
const ellipsis = document.createElement('span');
|
|
ellipsis.className = 'text-gray-600 dark:text-gray-400 px-2';
|
|
ellipsis.textContent = '...';
|
|
container.appendChild(ellipsis);
|
|
}
|
|
|
|
const lastBtn = document.createElement('button');
|
|
lastBtn.className = `px-3 py-1 rounded-md ${totalPages === currentPage ? 'bg-primary text-white' : 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'}`;
|
|
lastBtn.textContent = totalPages;
|
|
lastBtn.onclick = () => {
|
|
updateDNSResults(results, totalPages, pageSize);
|
|
};
|
|
container.appendChild(lastBtn);
|
|
}
|
|
|
|
// 下一页按钮
|
|
const nextBtn = document.createElement('button');
|
|
nextBtn.className = `px-3 py-1 rounded-md ${currentPage === totalPages ? 'text-gray-400 cursor-not-allowed' : 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'}`;
|
|
nextBtn.textContent = '>';
|
|
nextBtn.disabled = currentPage === totalPages;
|
|
nextBtn.onclick = () => {
|
|
if (currentPage < totalPages) {
|
|
updateDNSResults(results, currentPage + 1, pageSize);
|
|
}
|
|
};
|
|
container.appendChild(nextBtn);
|
|
|
|
// 跳转输入框
|
|
const jumpLabel = document.createElement('span');
|
|
jumpLabel.className = 'text-sm text-gray-600 dark:text-gray-400 mx-2';
|
|
jumpLabel.textContent = '跳转:';
|
|
container.appendChild(jumpLabel);
|
|
|
|
const jumpInput = document.createElement('input');
|
|
jumpInput.type = 'number';
|
|
jumpInput.min = '1';
|
|
jumpInput.max = totalPages;
|
|
jumpInput.className = 'w-16 px-2 py-1 text-sm border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white rounded-md focus:outline-none focus:ring-2 focus:ring-primary';
|
|
jumpInput.value = currentPage;
|
|
jumpInput.onkeypress = (e) => {
|
|
if (e.key === 'Enter') {
|
|
const pageNum = parseInt(jumpInput.value);
|
|
if (pageNum >= 1 && pageNum <= totalPages) {
|
|
updateDNSResults(results, pageNum, pageSize);
|
|
} else {
|
|
jumpInput.value = currentPage;
|
|
}
|
|
}
|
|
};
|
|
container.appendChild(jumpInput);
|
|
|
|
const jumpBtn = document.createElement('button');
|
|
jumpBtn.className = 'px-3 py-1 text-sm bg-primary text-white rounded-md hover:bg-primary/90';
|
|
jumpBtn.textContent = '确定';
|
|
jumpBtn.onclick = () => {
|
|
const pageNum = parseInt(jumpInput.value);
|
|
if (pageNum >= 1 && pageNum <= totalPages) {
|
|
updateDNSResults(results, pageNum, pageSize);
|
|
} else {
|
|
jumpInput.value = currentPage;
|
|
}
|
|
};
|
|
container.appendChild(jumpBtn);
|
|
|
|
pagination.appendChild(container);
|
|
}
|
|
|
|
// 更新访问趋势图表
|
|
function updateTrendChart(domain) {
|
|
const ctx = document.getElementById('whois-trend-chart');
|
|
if (!ctx) return;
|
|
|
|
const mockData = generateMockTrendData();
|
|
|
|
if (whoisChart) {
|
|
whoisChart.destroy();
|
|
}
|
|
|
|
whoisChart = new Chart(ctx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: mockData.labels,
|
|
datasets: [
|
|
{
|
|
label: '全部 DNS 请求',
|
|
data: mockData.totalData,
|
|
borderColor: '#3b82f6',
|
|
backgroundColor: 'rgba(59, 130, 246, 0.1)',
|
|
tension: 0.4,
|
|
fill: true
|
|
},
|
|
{
|
|
label: '威胁告警',
|
|
data: mockData.threatData,
|
|
borderColor: '#f59e0b',
|
|
backgroundColor: 'rgba(245, 158, 11, 0.1)',
|
|
tension: 0.4,
|
|
fill: false
|
|
},
|
|
{
|
|
label: '拦截',
|
|
data: mockData.blockData,
|
|
borderColor: '#ef4444',
|
|
backgroundColor: 'rgba(239, 68, 68, 0.1)',
|
|
tension: 0.4,
|
|
fill: false
|
|
}
|
|
]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: {
|
|
legend: {
|
|
display: true,
|
|
position: 'top'
|
|
},
|
|
tooltip: {
|
|
mode: 'index',
|
|
intersect: false
|
|
}
|
|
},
|
|
scales: {
|
|
y: {
|
|
beginAtZero: true,
|
|
ticks: {
|
|
precision: 0
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// 显示错误信息
|
|
function showWhoisError(message) {
|
|
const errorDiv = document.getElementById('whois-error');
|
|
const errorSpan = errorDiv.querySelector('span');
|
|
|
|
errorSpan.textContent = message;
|
|
errorDiv.classList.remove('hidden');
|
|
|
|
setTimeout(() => {
|
|
errorDiv.classList.add('hidden');
|
|
}, 5000);
|
|
}
|
|
|
|
// 提取主域名(去除所有子域名)
|
|
function extractMainDomain(domain) {
|
|
if (!domain) return domain;
|
|
|
|
const parts = domain.split('.');
|
|
|
|
// 如果只有两部分(如 example.com),直接返回
|
|
if (parts.length <= 2) {
|
|
return domain;
|
|
}
|
|
|
|
// 常见的子域前缀(更多类型)
|
|
const commonSubdomains = [
|
|
// 常见子域
|
|
'www', 'mail', 'news', 'map', 'image', 'video', 'cdn', 'api', 'blog', 'shop',
|
|
'cloud', 'docs', 'help', 'support', 'dev', 'test', 'staging', 'm', 'mobile',
|
|
'admin', 'static', 'img', 'assets', 'web', 'app', 'store', 'service',
|
|
// 日志和监控相关
|
|
'log', 'logs', 'event', 'events', 'eventlog', 'beacon', 'beacons', 'tracking',
|
|
'track', 'analytics', 'stat', 'stats', 'statistics', 'metric', 'metrics',
|
|
'monitor', 'monitoring', 'sentry', 'ingest', 'collector', 'collect',
|
|
// 服务和功能相关
|
|
'auth', 'login', 'sso', 'oauth', 'account', 'user', 'users', 'profile',
|
|
'payment', 'pay', 'billing', 'order', 'orders', 'cart', 'checkout',
|
|
'search', 'find', 'discovery', 'recommend', 'push', 'notification',
|
|
// 内容相关
|
|
'content', 'file', 'files', 'download', 'upload', 'resource', 'resources',
|
|
'data', 'db', 'database', 'cache', 'redis', 'memcache', 'storage',
|
|
// 网络和基础设施
|
|
'proxy', 'gateway', 'router', 'switch', 'lb', 'nlb', 'alb', 'elb',
|
|
'edge', 'node', 'server', 'host', 'vm', 'container', 'k8s', 'kubernetes',
|
|
// 开发相关
|
|
'git', 'gitlab', 'github', 'jenkins', 'ci', 'cd', 'build', 'release',
|
|
'npm', 'yarn', 'pkg', 'package', 'registry', 'repo', 'repository',
|
|
// 其他常见子域
|
|
'beta', 'alpha', 'preview', 'demo', 'sandbox', 'uat', 'prod', 'production',
|
|
'internal', 'external', 'public', 'private', 'secure', 'ssl', 'tls',
|
|
'old', 'new', 'v1', 'v2', 'v3', 'api1', 'api2', 'api3'
|
|
];
|
|
|
|
// 处理特殊情况,如 co.uk, co.jp 等(复合顶级域)
|
|
const compoundTLDs = [
|
|
'co.uk', 'org.uk', 'me.uk', 'ltd.uk', 'plc.uk', 'net.uk',
|
|
'co.jp', 'ne.jp', 'or.jp', 'ac.jp', 'go.jp', 'ad.jp', 'ed.jp', 'gr.jp', 'lg.jp',
|
|
'co.kr', 'ne.kr', 'or.kr', 'ac.kr', 'go.kr', 'ms.kr',
|
|
'co.in', 'co.in', 'firm.in', 'net.in', 'org.in', 'gen.in', 'ind.in',
|
|
'co.ca', 'net.ca', 'org.ca',
|
|
'com.au', 'net.au', 'org.au', 'edu.au', 'gov.au', 'asn.au', 'id.au',
|
|
'co.nz', 'net.nz', 'org.nz', 'geek.nz', 'maori.nz', 'ac.nz', 'school.nz', 'govt.nz', 'mil.nz',
|
|
'co.th', 'ac.th', 'go.th', 'or.th', 'net.th', 'in.th',
|
|
'com.sg', 'net.sg', 'org.sg', 'gov.sg', 'edu.sg', 'per.sg',
|
|
'com.my', 'net.my', 'org.my', 'gov.my', 'edu.my', 'mil.my', 'name.my',
|
|
'co.id', 'ac.id', 'go.id', 'mil.id', 'or.id', 'net.id', 'web.id', 'sch.id', 'my.id',
|
|
'co.za', 'net.za', 'org.za', 'web.za', 'school.za', 'gov.za', 'mil.za',
|
|
'com.cn', 'net.cn', 'org.cn', 'edu.cn', 'gov.cn', 'mil.cn', 'ac.cn',
|
|
'org.cn', 'net.cn', 'firm.cn', 'store.cn', 'arts.cn', 'rec.cn', 'info.cn', 'nom.cn',
|
|
'com.hk', 'net.hk', 'org.hk', 'edu.hk', 'gov.hk', 'idv.hk',
|
|
'com.tw', 'net.tw', 'org.tw', 'edu.tw', 'gov.tw', 'mil.tw', 'idv.tw', 'game.tw', 'ebiz.tw', 'club.tw',
|
|
'com.vn', 'net.vn', 'org.vn', 'edu.vn', 'gov.vn', 'int.vn', 'ac.vn', 'biz.vn', 'info.vn', 'name.vn', 'pro.vn', 'health.vn'
|
|
];
|
|
|
|
// 检查是否是复合顶级域
|
|
for (const compoundTLD of compoundTLDs) {
|
|
if (domain.endsWith('.' + compoundTLD)) {
|
|
const tldParts = compoundTLD.split('.');
|
|
// 保留主域名 + 复合顶级域(2 部分)
|
|
const keepParts = tldParts.length + 1;
|
|
return parts.slice(-keepParts).join('.');
|
|
}
|
|
}
|
|
|
|
// 检查第一部分是否是常见子域
|
|
const firstPart = parts[0].toLowerCase();
|
|
if (commonSubdomains.includes(firstPart)) {
|
|
// 去掉第一个子域,递归处理
|
|
return extractMainDomain(parts.slice(1).join('.'));
|
|
}
|
|
|
|
// 检查最后两部分是否是有效的顶级域组合
|
|
const tld = parts[parts.length - 1].toLowerCase();
|
|
const sld = parts[parts.length - 2].toLowerCase();
|
|
const validTLDs = ['com', 'net', 'org', 'io', 'xyz', 'cn', 'info', 'biz', 'me', 'tv', 'cc', 'top',
|
|
'vip', 'pro', 'club', 'site', 'online', 'store', 'tech', 'app', 'dev', 'ai',
|
|
'cloud', 'live', 'world', 'today', 'news', 'blog', 'shop', 'life', 'love',
|
|
'work', 'design', 'art', 'music', 'game', 'games', 'video', 'photo', 'pics',
|
|
'image', 'images', 'media', 'space', 'website', 'link', 'click', 'email',
|
|
'mail', 'tel', 'phone', 'mobi', 'mobile', 'asia', 'eu', 'us', 'uk', 'de',
|
|
'fr', 'jp', 'ru', 'br', 'in', 'au', 'ca', 'it', 'es', 'nl', 'pl', 'se',
|
|
'no', 'fi', 'dk', 'ch', 'at', 'be', 'pt', 'gr', 'cz', 'ie', 'nz', 'za',
|
|
'mx', 'ar', 'cl', 'co', 'pe', 've', 'sg', 'hk', 'tw', 'kr', 'th', 'my',
|
|
'id', 'ph', 'vn', 'ae', 'sa', 'il', 'tr', 'ua', 'ro', 'hu', 'sk', 'si',
|
|
'hr', 'rs', 'bg', 'lt', 'lv', 'ee', 'name', 'cat', 'tel', 'travel', 'pro',
|
|
'aero', 'coop', 'museum', 'mil', 'int', 'gov', 'edu'];
|
|
|
|
// 如果最后两部分是有效的顶级域组合,保留它们和前面的主域名
|
|
if (validTLDs.includes(tld)) {
|
|
// 检查二级域名是否是常见的商业/组织前缀
|
|
const commonSLD = ['co', 'com', 'net', 'org', 'edu', 'gov', 'mil', 'ac', 'ne', 'or', 'go', 'ad', 'gr', 'lg', 'firm', 'store', 'arts', 'rec', 'info', 'nom', 'idv', 'game', 'ebiz', 'club', 'health', 'web', 'my', 'biz'];
|
|
if (commonSLD.includes(sld) && sld !== tld) {
|
|
// 这是一个复合结构,如 example.co.uk,保留最后 3 部分
|
|
if (parts.length >= 3) {
|
|
return parts.slice(-3).join('.');
|
|
}
|
|
}
|
|
// 普通情况,保留最后 2 部分(主域名 + 顶级域)
|
|
return parts.slice(-2).join('.');
|
|
}
|
|
|
|
// 默认情况:如果超过 2 部分,去掉第一部分,递归处理
|
|
if (parts.length > 2) {
|
|
return extractMainDomain(parts.slice(1).join('.'));
|
|
}
|
|
|
|
return domain;
|
|
}
|
|
|
|
// 查询域名信息
|
|
async function searchDomainInfo() {
|
|
const domainInput = document.getElementById('whois-domain-input');
|
|
const domain = domainInput.value.trim();
|
|
|
|
if (!domain) {
|
|
showWhoisError('请输入要查询的域名');
|
|
return;
|
|
}
|
|
|
|
// 显示加载状态
|
|
showLoading();
|
|
|
|
// 自动裁剪顶级域,提取主域名
|
|
const mainDomain = extractMainDomain(domain);
|
|
currentDomain = mainDomain;
|
|
|
|
console.log('查询域名:', mainDomain);
|
|
|
|
// 隐藏所有信息区域
|
|
document.getElementById('whois-info-section').classList.add('hidden');
|
|
document.getElementById('icp-info-section').classList.add('hidden');
|
|
document.getElementById('dns-results-section').classList.add('hidden');
|
|
document.getElementById('trend-section').classList.add('hidden');
|
|
|
|
try {
|
|
// 并行获取 WHOIS 和 ICP 信息,以及 DNS 日志
|
|
const [whoisData, icpData, dnsLogs] = await Promise.all([
|
|
fetchWhoisInfo(mainDomain),
|
|
fetchICPInfo(mainDomain),
|
|
fetchDNSLogs(mainDomain)
|
|
]);
|
|
|
|
console.log('API 返回数据:', { whoisData, icpData, dnsLogs });
|
|
|
|
// 处理 WHOIS 信息
|
|
let whoisInfo;
|
|
if (!whoisData || !whoisData.whois) {
|
|
console.log('未找到 WHOIS 信息,显示为 -');
|
|
// 没有数据时显示为 -
|
|
whoisInfo = {
|
|
registrant: '-',
|
|
created: '-',
|
|
email: '-',
|
|
expires: '-',
|
|
registrar: '-',
|
|
nameservers: '-'
|
|
};
|
|
} else {
|
|
whoisInfo = formatWhoisInfo(whoisData);
|
|
console.log('格式化后的 WHOIS 信息:', whoisInfo);
|
|
}
|
|
|
|
// 处理 ICP 备案信息
|
|
let icpInfo;
|
|
if (!icpData || icpData.code !== '200') {
|
|
console.log('未找到 ICP 备案信息,显示为 -');
|
|
// 没有数据时显示为 -
|
|
icpInfo = {
|
|
company: '-',
|
|
license: '-',
|
|
type: '-',
|
|
date: '-'
|
|
};
|
|
} else {
|
|
icpInfo = formatICPInfo(icpData);
|
|
console.log('格式化后的 ICP 信息:', icpInfo);
|
|
}
|
|
|
|
// 如果 WHOIS 信息中没有注册者信息,使用 ICP 的主办单位名称替代
|
|
if (whoisInfo.registrant === '该域名已开通隐私保护' || whoisInfo.registrant === '-') {
|
|
if (icpInfo.company && icpInfo.company !== '-') {
|
|
whoisInfo.registrant = icpInfo.company;
|
|
console.log('使用 ICP 主办单位名称作为注册者信息:', icpInfo.company);
|
|
}
|
|
}
|
|
|
|
// 处理 DNS 解析结果
|
|
let dnsResults;
|
|
if (dnsLogs && dnsLogs.length > 0) {
|
|
console.log('找到 DNS 解析记录:', dnsLogs.length, '条');
|
|
// 获取每个 IP 的地理位置信息
|
|
const ipInfoPromises = dnsLogs.map(async (record) => {
|
|
const ipInfo = await fetchIPInfo(record.ip);
|
|
console.log(`IP ${record.ip} 的地理位置信息:`, ipInfo);
|
|
let location = '-';
|
|
let carrier = '-';
|
|
|
|
// 检查 API 返回的数据
|
|
if (ipInfo && ipInfo.region) {
|
|
// 直接使用 ipInfo 作为数据对象(API 直接返回数据)
|
|
// 使用 API 返回的 region 字段作为地理位置
|
|
location = ipInfo.region;
|
|
// 使用 API 返回的 isp 字段作为运营商
|
|
if (ipInfo.isp) {
|
|
carrier = ipInfo.isp;
|
|
}
|
|
} else {
|
|
console.log(`IP ${record.ip} 没有 region 字段,ipInfo:`, ipInfo);
|
|
}
|
|
|
|
return {
|
|
ip: record.ip,
|
|
location: location,
|
|
carrier: carrier
|
|
};
|
|
});
|
|
|
|
dnsResults = await Promise.all(ipInfoPromises);
|
|
console.log('处理后的 DNS 解析结果:', dnsResults);
|
|
} else {
|
|
// 没有查询到 DNS 解析记录,显示空数组
|
|
dnsResults = [];
|
|
console.log('未找到 DNS 解析记录');
|
|
}
|
|
|
|
// 更新显示
|
|
updateWhoisInfo(whoisInfo);
|
|
document.getElementById('whois-info-section').classList.remove('hidden');
|
|
|
|
updateICPInfo(icpInfo);
|
|
document.getElementById('icp-info-section').classList.remove('hidden');
|
|
|
|
// 显示 DNS 解析结果
|
|
updateDNSResults(dnsResults, 1, currentPageSize);
|
|
document.getElementById('dns-results-section').classList.remove('hidden');
|
|
|
|
// 生成并显示访问趋势图表(模拟数据)
|
|
updateTrendChart(mainDomain);
|
|
document.getElementById('trend-section').classList.remove('hidden');
|
|
|
|
} catch (error) {
|
|
console.error('查询域名信息失败:', error);
|
|
showWhoisError('查询失败,请稍后重试');
|
|
|
|
// 使用模拟数据作为备用
|
|
const whoisInfo = generateMockWhoisInfo(mainDomain);
|
|
const icpInfo = generateMockICPInfo(mainDomain);
|
|
const dnsResults = generateMockDNSResults(mainDomain);
|
|
|
|
updateWhoisInfo(whoisInfo);
|
|
document.getElementById('whois-info-section').classList.remove('hidden');
|
|
|
|
updateICPInfo(icpInfo);
|
|
document.getElementById('icp-info-section').classList.remove('hidden');
|
|
|
|
updateDNSResults(dnsResults, 1, currentPageSize);
|
|
document.getElementById('dns-results-section').classList.remove('hidden');
|
|
|
|
updateTrendChart(mainDomain);
|
|
document.getElementById('trend-section').classList.remove('hidden');
|
|
} finally {
|
|
// 隐藏加载状态
|
|
hideLoading();
|
|
}
|
|
}
|
|
|
|
// 初始化域名信息查询页面
|
|
function initWhoisPage() {
|
|
// 绑定搜索按钮事件
|
|
const searchBtn = document.getElementById('whois-search-btn');
|
|
if (searchBtn) {
|
|
searchBtn.addEventListener('click', searchDomainInfo);
|
|
}
|
|
|
|
// 绑定输入框回车事件
|
|
const domainInput = document.getElementById('whois-domain-input');
|
|
if (domainInput) {
|
|
domainInput.addEventListener('keypress', (e) => {
|
|
if (e.key === 'Enter') {
|
|
searchDomainInfo();
|
|
}
|
|
});
|
|
}
|
|
|
|
// 检查是否有域名参数
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const domainParam = urlParams.get('domain');
|
|
if (domainParam && domainInput) {
|
|
domainInput.value = domainParam;
|
|
// 自动查询
|
|
setTimeout(searchDomainInfo, 500);
|
|
}
|
|
}
|
|
|
|
// 页面加载时自动初始化
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
// 检查当前 hash 是否为 whois
|
|
if (window.location.hash === '#whois') {
|
|
initWhoisPage();
|
|
}
|
|
});
|
|
|
|
// 监听 hash 变化
|
|
window.addEventListener('hashchange', () => {
|
|
if (window.location.hash === '#whois') {
|
|
initWhoisPage();
|
|
}
|
|
});
|