实现日志详情域名信息显示
This commit is contained in:
@@ -19,6 +19,11 @@ let trackersDatabase = null;
|
||||
let trackersLoaded = false;
|
||||
let trackersLoading = false;
|
||||
|
||||
// 域名信息数据库缓存
|
||||
let domainInfoDatabase = null;
|
||||
let domainInfoLoaded = false;
|
||||
let domainInfoLoading = false;
|
||||
|
||||
// WebSocket连接和重连计时器
|
||||
let logsWsConnection = null;
|
||||
let logsWsReconnectTimer = null;
|
||||
@@ -37,7 +42,7 @@ async function loadTrackersDatabase() {
|
||||
trackersLoading = true;
|
||||
|
||||
try {
|
||||
const response = await fetch('/tracker/trackers.json');
|
||||
const response = await fetch('domain-info/tracker/trackers.json');
|
||||
if (!response.ok) {
|
||||
console.error('加载跟踪器数据库失败:', response.statusText);
|
||||
trackersDatabase = { trackers: {} };
|
||||
@@ -56,6 +61,53 @@ async function loadTrackersDatabase() {
|
||||
}
|
||||
}
|
||||
|
||||
// 加载域名信息数据库
|
||||
async function loadDomainInfoDatabase() {
|
||||
console.log('开始加载域名信息数据库');
|
||||
|
||||
if (domainInfoLoaded) {
|
||||
console.log('域名信息数据库已加载,直接返回');
|
||||
return domainInfoDatabase;
|
||||
}
|
||||
|
||||
if (domainInfoLoading) {
|
||||
console.log('域名信息数据库正在加载中,等待完成');
|
||||
// 等待正在进行的加载完成
|
||||
while (domainInfoLoading) {
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
}
|
||||
return domainInfoDatabase;
|
||||
}
|
||||
|
||||
domainInfoLoading = true;
|
||||
|
||||
try {
|
||||
console.log('发起请求获取域名信息数据库');
|
||||
const response = await fetch('domain-info/domains/domain-info.json');
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('加载域名信息数据库失败,HTTP状态:', response.status, response.statusText);
|
||||
console.error('请求URL:', response.url);
|
||||
domainInfoDatabase = { domains: {}, categories: {} };
|
||||
return domainInfoDatabase;
|
||||
}
|
||||
|
||||
console.log('域名信息数据库请求成功,开始解析JSON');
|
||||
domainInfoDatabase = await response.json();
|
||||
console.log('域名信息数据库解析成功,包含', Object.keys(domainInfoDatabase.domains || {}).length, '个公司');
|
||||
domainInfoLoaded = true;
|
||||
return domainInfoDatabase;
|
||||
} catch (error) {
|
||||
console.error('加载域名信息数据库失败,错误信息:', error.message);
|
||||
console.error('错误堆栈:', error.stack);
|
||||
domainInfoDatabase = { domains: {}, categories: {} };
|
||||
return domainInfoDatabase;
|
||||
} finally {
|
||||
domainInfoLoading = false;
|
||||
console.log('域名信息数据库加载完成');
|
||||
}
|
||||
}
|
||||
|
||||
// 检查域名是否在跟踪器数据库中,并返回跟踪器信息
|
||||
async function isDomainInTrackerDatabase(domain) {
|
||||
if (!trackersDatabase || !trackersLoaded) {
|
||||
@@ -78,7 +130,7 @@ async function isDomainInTrackerDatabase(domain) {
|
||||
if (tracker && tracker.url) {
|
||||
try {
|
||||
const trackerUrl = new URL(tracker.url);
|
||||
if (trackerUrl.hostname === domain || trackerUrl.hostname.includes(domain)) {
|
||||
if (trackerUrl.hostname === domain) {
|
||||
return tracker;
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -91,6 +143,164 @@ async function isDomainInTrackerDatabase(domain) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 根据域名查找对应的网站信息
|
||||
async function getDomainInfo(domain) {
|
||||
console.log('开始查找域名信息,域名:', domain);
|
||||
|
||||
if (!domainInfoDatabase || !domainInfoLoaded) {
|
||||
console.log('域名信息数据库未加载,调用loadDomainInfoDatabase');
|
||||
await loadDomainInfoDatabase();
|
||||
}
|
||||
|
||||
if (!domainInfoDatabase || !domainInfoDatabase.domains) {
|
||||
console.error('域名信息数据库无效或为空');
|
||||
return null;
|
||||
}
|
||||
|
||||
// 规范化域名,移除可能的端口号
|
||||
const normalizedDomain = domain.replace(/:\d+$/, '').toLowerCase();
|
||||
console.log('规范化后的域名:', normalizedDomain);
|
||||
|
||||
// 遍历所有公司
|
||||
console.log('开始遍历公司,总公司数:', Object.keys(domainInfoDatabase.domains).length);
|
||||
for (const companyKey in domainInfoDatabase.domains) {
|
||||
if (domainInfoDatabase.domains.hasOwnProperty(companyKey)) {
|
||||
console.log('检查公司:', companyKey);
|
||||
const companyData = domainInfoDatabase.domains[companyKey];
|
||||
const companyName = companyData.company || companyKey;
|
||||
|
||||
// 遍历公司下的所有网站
|
||||
for (const websiteKey in companyData) {
|
||||
if (companyData.hasOwnProperty(websiteKey) && websiteKey !== 'company') {
|
||||
console.log(' 检查网站:', websiteKey);
|
||||
const website = companyData[websiteKey];
|
||||
|
||||
// 检查域名是否匹配网站的URL
|
||||
if (website.url) {
|
||||
// 处理字符串类型的URL
|
||||
if (typeof website.url === 'string') {
|
||||
console.log(' 检查字符串URL:', website.url);
|
||||
if (isDomainMatch(website.url, normalizedDomain)) {
|
||||
console.log(' 匹配成功,返回网站信息');
|
||||
return {
|
||||
name: website.name,
|
||||
icon: website.icon,
|
||||
categoryId: website.categoryId,
|
||||
categoryName: domainInfoDatabase.categories[website.categoryId] || '未知',
|
||||
company: companyName
|
||||
};
|
||||
}
|
||||
}
|
||||
// 处理对象类型的URL
|
||||
else if (typeof website.url === 'object') {
|
||||
console.log(' 检查对象类型URL,包含', Object.keys(website.url).length, '个URL');
|
||||
for (const urlKey in website.url) {
|
||||
if (website.url.hasOwnProperty(urlKey)) {
|
||||
const urlValue = website.url[urlKey];
|
||||
console.log(' 检查URL', urlKey, ':', urlValue);
|
||||
if (isDomainMatch(urlValue, normalizedDomain)) {
|
||||
console.log(' 匹配成功,返回网站信息');
|
||||
return {
|
||||
name: website.name,
|
||||
icon: website.icon,
|
||||
categoryId: website.categoryId,
|
||||
categoryName: domainInfoDatabase.categories[website.categoryId] || '未知',
|
||||
company: companyName
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log(' 网站没有URL属性');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('未找到匹配的域名信息');
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查域名是否匹配
|
||||
function isDomainMatch(urlValue, targetDomain) {
|
||||
console.log(' 开始匹配URL:', urlValue, '目标域名:', targetDomain);
|
||||
|
||||
try {
|
||||
// 尝试将URL值解析为完整URL
|
||||
console.log(' 尝试解析URL为完整URL');
|
||||
const url = new URL(urlValue);
|
||||
const hostname = url.hostname.toLowerCase();
|
||||
console.log(' 解析成功,主机名:', hostname);
|
||||
|
||||
// 只匹配完整域名,不进行主域名匹配
|
||||
// 这是为了避免同一个公司下的不同网站(如微信和腾讯视频)因为主域名相同而错误匹配
|
||||
if (hostname === targetDomain) {
|
||||
console.log(' 完整域名匹配成功');
|
||||
return true;
|
||||
} else {
|
||||
console.log(' 完整域名不匹配');
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(' 解析URL失败,将其视为纯域名处理,错误信息:', e.message);
|
||||
// 如果是纯域名而不是完整URL
|
||||
const urlDomain = urlValue.toLowerCase();
|
||||
console.log(' 处理为纯域名:', urlDomain);
|
||||
|
||||
// 只匹配完整域名,不进行主域名匹配
|
||||
if (urlDomain === targetDomain) {
|
||||
console.log(' 完整域名匹配成功');
|
||||
return true;
|
||||
} else {
|
||||
console.log(' 完整域名不匹配');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 提取主域名
|
||||
function extractPrimaryDomain(domain) {
|
||||
console.log(' 开始提取主域名,原始域名:', domain);
|
||||
|
||||
const parts = domain.split('.');
|
||||
console.log(' 域名分割为:', parts);
|
||||
|
||||
if (parts.length <= 2) {
|
||||
console.log(' 域名长度小于等于2,直接返回:', domain);
|
||||
return domain;
|
||||
}
|
||||
|
||||
// 处理常见的三级域名
|
||||
const commonSubdomains = ['www', 'mail', 'news', 'map', 'image', 'video', 'cdn', 'api', 'blog', 'shop', 'cloud', 'docs', 'help', 'support', 'dev', 'test', 'staging'];
|
||||
console.log(' 检查是否为常见三级域名');
|
||||
|
||||
if (commonSubdomains.includes(parts[0])) {
|
||||
const result = parts.slice(1).join('.');
|
||||
console.log(' 是常见三级域名,返回:', result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 处理特殊情况,如co.uk, co.jp等
|
||||
const countryTLDs = ['co.uk', 'co.jp', 'co.kr', 'co.in', 'co.ca', 'co.au', 'co.nz', 'co.th', 'co.sg', 'co.my', 'co.id', 'co.za', 'com.cn', 'org.cn', 'net.cn', 'gov.cn', 'edu.cn'];
|
||||
console.log(' 检查是否为特殊国家TLD');
|
||||
|
||||
for (const tld of countryTLDs) {
|
||||
if (domain.endsWith('.' + tld)) {
|
||||
const mainParts = domain.split('.');
|
||||
const result = mainParts.slice(-tld.split('.').length - 1).join('.');
|
||||
console.log(' 是特殊国家TLD,返回:', result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// 默认情况:返回最后两个部分
|
||||
const result = parts.slice(-2).join('.');
|
||||
console.log(' 默认情况,返回最后两个部分:', result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 初始化查询日志页面
|
||||
function initLogsPage() {
|
||||
console.log('初始化查询日志页面');
|
||||
@@ -1115,6 +1325,9 @@ async function showLogDetailModal(log) {
|
||||
const trackerInfo = await isDomainInTrackerDatabase(log.domain);
|
||||
const isTracker = trackerInfo !== null;
|
||||
|
||||
// 获取域名信息
|
||||
const domainInfo = await getDomainInfo(domain);
|
||||
|
||||
// 格式化DNS解析记录
|
||||
const dnsRecords = formatDNSRecords(log, result);
|
||||
|
||||
@@ -1196,6 +1409,31 @@ async function showLogDetailModal(log) {
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 域名信息
|
||||
const domainInfoDiv = document.createElement('div');
|
||||
domainInfoDiv.className = 'col-span-1 md:col-span-2 space-y-1';
|
||||
domainInfoDiv.innerHTML = `
|
||||
<div class="text-xs text-gray-500">域名信息</div>
|
||||
<div class="text-sm font-medium text-gray-900 p-3 bg-gray-50 rounded-md border border-gray-200">
|
||||
${domainInfo ? `
|
||||
<div class="flex items-center mb-2">
|
||||
${domainInfo.icon ? `<img src="${domainInfo.icon}" alt="${domainInfo.name}" class="w-6 h-6 mr-2 rounded-sm" onerror="this.style.display='none'" />` : ''}
|
||||
<span class="text-base font-semibold">${domainInfo.name || '未知'}</span>
|
||||
</div>
|
||||
<div class="ml-8 mt-1">
|
||||
<div class="flex items-center mb-1">
|
||||
<span class="text-gray-500 w-16">类别:</span>
|
||||
<span>${domainInfo.categoryName || '未知'}</span>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<span class="text-gray-500 w-16">所属公司:</span>
|
||||
<span>${domainInfo.company || '未知'}</span>
|
||||
</div>
|
||||
</div>
|
||||
` : '无'}
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 跟踪器信息
|
||||
const trackerDiv = document.createElement('div');
|
||||
trackerDiv.className = 'col-span-1 md:col-span-2 space-y-1';
|
||||
@@ -1236,6 +1474,7 @@ async function showLogDetailModal(log) {
|
||||
`;
|
||||
|
||||
basicInfoGrid.appendChild(dnsFeatures);
|
||||
basicInfoGrid.appendChild(domainInfoDiv);
|
||||
basicInfoGrid.appendChild(trackerDiv);
|
||||
basicInfoGrid.appendChild(recordsDiv);
|
||||
basicInfoGrid.appendChild(dnsServerDiv);
|
||||
@@ -1270,8 +1509,8 @@ async function showLogDetailModal(log) {
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 只有被屏蔽或者有自定义规则时才显示规则信息
|
||||
if (result === 'blocked' || (blockRule && blockRule !== '无' && blockRule !== '-')) {
|
||||
// 只有被屏蔽时才显示规则信息
|
||||
if (result === 'blocked') {
|
||||
responseDetailsHTML += `
|
||||
<div class="space-y-1">
|
||||
<div class="text-xs text-gray-500">规则</div>
|
||||
|
||||
Reference in New Issue
Block a user