重大重构
This commit is contained in:
@@ -7,7 +7,7 @@ let logsPerPage = 30; // 默认显示30条记录
|
||||
let currentFilter = '';
|
||||
let currentSearch = '';
|
||||
let logsChart = null;
|
||||
let currentSortField = '';
|
||||
let currentSortField = 'timestamp'; // 默认按时间排序,显示最新记录
|
||||
let currentSortDirection = 'desc'; // 默认降序
|
||||
|
||||
// IP地理位置缓存(检查是否已经存在,避免重复声明)
|
||||
@@ -165,15 +165,12 @@ 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));
|
||||
@@ -184,7 +181,6 @@ async function loadDomainInfoDatabase() {
|
||||
domainInfoLoading = true;
|
||||
|
||||
try {
|
||||
console.log('发起请求获取域名信息数据库');
|
||||
const response = await fetch('domain-info/domains/domain-info.json');
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -194,9 +190,7 @@ async function loadDomainInfoDatabase() {
|
||||
return domainInfoDatabase;
|
||||
}
|
||||
|
||||
console.log('域名信息数据库请求成功,开始解析JSON');
|
||||
domainInfoDatabase = await response.json();
|
||||
console.log('域名信息数据库解析成功,包含', Object.keys(domainInfoDatabase.domains || {}).length, '个公司');
|
||||
domainInfoLoaded = true;
|
||||
return domainInfoDatabase;
|
||||
} catch (error) {
|
||||
@@ -206,7 +200,6 @@ async function loadDomainInfoDatabase() {
|
||||
return domainInfoDatabase;
|
||||
} finally {
|
||||
domainInfoLoading = false;
|
||||
console.log('域名信息数据库加载完成');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,10 +240,8 @@ async function isDomainInTrackerDatabase(domain) {
|
||||
|
||||
// 根据域名查找对应的网站信息
|
||||
async function getDomainInfo(domain) {
|
||||
console.log('开始查找域名信息,域名:', domain);
|
||||
|
||||
if (!domainInfoDatabase || !domainInfoLoaded) {
|
||||
console.log('域名信息数据库未加载,调用loadDomainInfoDatabase');
|
||||
await loadDomainInfoDatabase();
|
||||
}
|
||||
|
||||
@@ -261,29 +252,23 @@ async function getDomainInfo(domain) {
|
||||
|
||||
// 规范化域名,移除可能的端口号
|
||||
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, website.categoryId)) {
|
||||
console.log(' 匹配成功,返回网站信息');
|
||||
return {
|
||||
name: website.name,
|
||||
icon: website.icon,
|
||||
@@ -295,13 +280,10 @@ async function getDomainInfo(domain) {
|
||||
}
|
||||
// 处理对象类型的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, website.categoryId)) {
|
||||
console.log(' 匹配成功,返回网站信息');
|
||||
return {
|
||||
name: website.name,
|
||||
icon: website.icon,
|
||||
@@ -315,18 +297,14 @@ async function getDomainInfo(domain) {
|
||||
}
|
||||
} else if (typeof website === 'object' && website !== null) {
|
||||
// 没有URL属性,可能是嵌套的类别
|
||||
console.log(' 发现嵌套类别,进一步检查');
|
||||
for (const nestedWebsiteKey in website) {
|
||||
if (website.hasOwnProperty(nestedWebsiteKey) && nestedWebsiteKey !== 'company') {
|
||||
console.log(' 检查嵌套网站/类别:', nestedWebsiteKey);
|
||||
const nestedWebsite = website[nestedWebsiteKey];
|
||||
|
||||
if (nestedWebsite.url) {
|
||||
// 处理字符串类型的URL
|
||||
if (typeof nestedWebsite.url === 'string') {
|
||||
console.log(' 检查字符串URL:', nestedWebsite.url);
|
||||
if (isDomainMatch(nestedWebsite.url, normalizedDomain, nestedWebsite.categoryId)) {
|
||||
console.log(' 匹配成功,返回网站信息');
|
||||
return {
|
||||
name: nestedWebsite.name,
|
||||
icon: nestedWebsite.icon,
|
||||
@@ -338,13 +316,10 @@ async function getDomainInfo(domain) {
|
||||
}
|
||||
// 处理对象类型的URL
|
||||
else if (typeof nestedWebsite.url === 'object') {
|
||||
console.log(' 检查对象类型URL,包含', Object.keys(nestedWebsite.url).length, '个URL');
|
||||
for (const urlKey in nestedWebsite.url) {
|
||||
if (nestedWebsite.url.hasOwnProperty(urlKey)) {
|
||||
const urlValue = nestedWebsite.url[urlKey];
|
||||
console.log(' 检查URL', urlKey, ':', urlValue);
|
||||
if (isDomainMatch(urlValue, normalizedDomain, nestedWebsite.categoryId)) {
|
||||
console.log(' 匹配成功,返回网站信息');
|
||||
return {
|
||||
name: nestedWebsite.name,
|
||||
icon: nestedWebsite.icon,
|
||||
@@ -358,18 +333,14 @@ async function getDomainInfo(domain) {
|
||||
}
|
||||
} else if (typeof nestedWebsite === 'object' && nestedWebsite !== null) {
|
||||
// 嵌套类别中的嵌套类别,递归检查
|
||||
console.log(' 发现二级嵌套类别,进一步检查');
|
||||
for (const secondNestedWebsiteKey in nestedWebsite) {
|
||||
if (nestedWebsite.hasOwnProperty(secondNestedWebsiteKey) && secondNestedWebsiteKey !== 'company') {
|
||||
console.log(' 检查二级嵌套网站:', secondNestedWebsiteKey);
|
||||
const secondNestedWebsite = nestedWebsite[secondNestedWebsiteKey];
|
||||
|
||||
if (secondNestedWebsite.url) {
|
||||
// 处理字符串类型的URL
|
||||
if (typeof secondNestedWebsite.url === 'string') {
|
||||
console.log(' 检查字符串URL:', secondNestedWebsite.url);
|
||||
if (isDomainMatch(secondNestedWebsite.url, normalizedDomain, secondNestedWebsite.categoryId)) {
|
||||
console.log(' 匹配成功,返回网站信息');
|
||||
return {
|
||||
name: secondNestedWebsite.name,
|
||||
icon: secondNestedWebsite.icon,
|
||||
@@ -381,13 +352,10 @@ async function getDomainInfo(domain) {
|
||||
}
|
||||
// 处理对象类型的URL
|
||||
else if (typeof secondNestedWebsite.url === 'object') {
|
||||
console.log(' 检查对象类型URL,包含', Object.keys(secondNestedWebsite.url).length, '个URL');
|
||||
for (const urlKey in secondNestedWebsite.url) {
|
||||
if (secondNestedWebsite.url.hasOwnProperty(urlKey)) {
|
||||
const urlValue = secondNestedWebsite.url[urlKey];
|
||||
console.log(' 检查URL', urlKey, ':', urlValue);
|
||||
if (isDomainMatch(urlValue, normalizedDomain, secondNestedWebsite.categoryId)) {
|
||||
console.log(' 匹配成功,返回网站信息');
|
||||
return {
|
||||
name: secondNestedWebsite.name,
|
||||
icon: secondNestedWebsite.icon,
|
||||
@@ -403,83 +371,67 @@ async function getDomainInfo(domain) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log(' 嵌套网站没有URL属性且不是对象类型');
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log(' 网站没有URL属性');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('未找到匹配的域名信息');
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查域名是否匹配
|
||||
function isDomainMatch(urlValue, targetDomain, categoryId) {
|
||||
console.log(' 开始匹配URL:', urlValue, '目标域名:', targetDomain, '类别ID:', categoryId);
|
||||
|
||||
// 规范化目标域名,去除末尾的点
|
||||
const normalizedTargetDomain = targetDomain.replace(/\.$/, '').toLowerCase();
|
||||
|
||||
try {
|
||||
// 尝试将URL值解析为完整URL
|
||||
console.log(' 尝试解析URL为完整URL');
|
||||
const url = new URL(urlValue);
|
||||
let hostname = url.hostname.toLowerCase();
|
||||
// 规范化主机名,去除末尾的点
|
||||
hostname = hostname.replace(/\.$/, '');
|
||||
console.log(' 解析成功,主机名:', hostname, '规范化目标域名:', normalizedTargetDomain);
|
||||
|
||||
// 根据类别ID选择匹配方式
|
||||
if (categoryId === 2) {
|
||||
// CDN类别,使用域名后缀匹配
|
||||
if (normalizedTargetDomain.endsWith('.' + hostname) || normalizedTargetDomain === hostname) {
|
||||
console.log(' CDN域名后缀匹配成功');
|
||||
return true;
|
||||
} else {
|
||||
console.log(' CDN域名后缀不匹配');
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// 其他类别,使用完整域名匹配
|
||||
if (hostname === normalizedTargetDomain) {
|
||||
console.log(' 完整域名匹配成功');
|
||||
return true;
|
||||
} else {
|
||||
console.log(' 完整域名不匹配');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(' 解析URL失败,将其视为纯域名处理,错误信息:', e.message);
|
||||
// 如果是纯域名而不是完整URL
|
||||
let urlDomain = urlValue.toLowerCase();
|
||||
// 规范化纯域名,去除末尾的点
|
||||
urlDomain = urlDomain.replace(/\.$/, '');
|
||||
console.log(' 处理为纯域名:', urlDomain, '规范化目标域名:', normalizedTargetDomain);
|
||||
|
||||
// 根据类别ID选择匹配方式
|
||||
if (categoryId === 2) {
|
||||
// CDN类别,使用域名后缀匹配
|
||||
if (normalizedTargetDomain.endsWith('.' + urlDomain) || normalizedTargetDomain === urlDomain) {
|
||||
console.log(' CDN域名后缀匹配成功');
|
||||
return true;
|
||||
} else {
|
||||
console.log(' CDN域名后缀不匹配');
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// 其他类别,使用完整域名匹配
|
||||
if (urlDomain === normalizedTargetDomain) {
|
||||
console.log(' 完整域名匹配成功');
|
||||
return true;
|
||||
} else {
|
||||
console.log(' 完整域名不匹配');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -488,42 +440,34 @@ function isDomainMatch(urlValue, targetDomain, categoryId) {
|
||||
|
||||
// 提取主域名
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -793,7 +737,6 @@ function initResizableColumns() {
|
||||
|
||||
// 初始化查询日志页面
|
||||
function initLogsPage() {
|
||||
console.log('初始化查询日志页面');
|
||||
|
||||
// 加载日志统计数据
|
||||
loadLogsStats();
|
||||
@@ -992,6 +935,9 @@ function bindLogsEvents() {
|
||||
loadLogs();
|
||||
});
|
||||
});
|
||||
|
||||
// 初始化排序图标
|
||||
updateSortIcons();
|
||||
}
|
||||
|
||||
// 更新排序图标
|
||||
@@ -1345,7 +1291,6 @@ async function updateLogsTable(logs) {
|
||||
if (e.target.closest('button')) {
|
||||
return;
|
||||
}
|
||||
console.log('Row clicked, log object:', log);
|
||||
showLogDetailModal(log);
|
||||
});
|
||||
|
||||
@@ -1479,14 +1424,12 @@ function connectLogsWebSocket() {
|
||||
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsUrl = `${wsProtocol}//${window.location.host}/ws/stats`;
|
||||
|
||||
console.log('正在连接WebSocket:', wsUrl);
|
||||
|
||||
// 创建WebSocket连接
|
||||
logsWsConnection = new WebSocket(wsUrl);
|
||||
|
||||
// 连接打开事件
|
||||
logsWsConnection.onopen = function() {
|
||||
console.log('WebSocket连接已建立');
|
||||
};
|
||||
|
||||
// 接收消息事件
|
||||
@@ -1495,7 +1438,6 @@ function connectLogsWebSocket() {
|
||||
const data = JSON.parse(event.data);
|
||||
|
||||
if (data.type === 'initial_data' || data.type === 'stats_update') {
|
||||
console.log('收到实时数据更新');
|
||||
// 只更新统计数据,不更新日志详情
|
||||
updateLogsStatsFromWebSocket(data.data);
|
||||
}
|
||||
@@ -1530,7 +1472,6 @@ function setupLogsReconnect() {
|
||||
}
|
||||
|
||||
const reconnectDelay = 5000; // 5秒后重连
|
||||
console.log(`将在${reconnectDelay}ms后尝试重新连接WebSocket`);
|
||||
|
||||
logsWsReconnectTimer = setTimeout(() => {
|
||||
connectLogsWebSocket();
|
||||
@@ -1567,17 +1508,13 @@ function updateLogsStatsFromWebSocket(stats) {
|
||||
// 拦截域名
|
||||
async function blockDomain(domain) {
|
||||
try {
|
||||
console.log(`开始拦截域名: ${domain}`);
|
||||
|
||||
// 创建拦截规则,使用AdBlock Plus格式
|
||||
const blockRule = `||${domain}^`;
|
||||
console.log(`创建的拦截规则: ${blockRule}`);
|
||||
|
||||
// 调用API添加拦截规则
|
||||
console.log(`调用API添加拦截规则,路径: /shield, 方法: POST`);
|
||||
const response = await apiRequest('/shield', 'POST', { rule: blockRule });
|
||||
|
||||
console.log(`API响应:`, response);
|
||||
|
||||
// 处理不同的响应格式
|
||||
if (response && (response.success || response.status === 'success')) {
|
||||
@@ -1665,17 +1602,13 @@ async function refreshRulesList() {
|
||||
// 放行域名
|
||||
async function unblockDomain(domain) {
|
||||
try {
|
||||
console.log(`开始放行域名: ${domain}`);
|
||||
|
||||
// 创建放行规则,使用AdBlock Plus格式
|
||||
const allowRule = `@@||${domain}^`;
|
||||
console.log(`创建的放行规则: ${allowRule}`);
|
||||
|
||||
// 调用API添加放行规则
|
||||
console.log(`调用API添加放行规则,路径: /shield, 方法: POST`);
|
||||
const response = await apiRequest('/shield', 'POST', { rule: allowRule });
|
||||
|
||||
console.log(`API响应:`, response);
|
||||
|
||||
// 处理不同的响应格式
|
||||
if (response && (response.success || response.status === 'success')) {
|
||||
@@ -1808,7 +1741,6 @@ function formatDNSString(str) {
|
||||
|
||||
// 显示日志详情弹窗
|
||||
async function showLogDetailModal(log) {
|
||||
console.log('showLogDetailModal called with log:', JSON.stringify(log, null, 2)); // 输出完整的log对象
|
||||
|
||||
if (!log) {
|
||||
console.error('No log data provided!');
|
||||
|
||||
Reference in New Issue
Block a user