更新
This commit is contained in:
+1719
-1781
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -28,7 +28,8 @@
|
||||
"24": "API服务",
|
||||
"25": "其他",
|
||||
"26": "游戏网站",
|
||||
"27": "行为分析、跟踪遥测、数据统计等"
|
||||
"27": "行为分析、跟踪遥测、数据统计等",
|
||||
"28": "银狐木马"
|
||||
},
|
||||
"domains": {
|
||||
"网易": {
|
||||
@@ -924,6 +925,116 @@
|
||||
"url": "https://insider.microsoft.com/",
|
||||
"icon": "https://insider.microsoft.com/favicon.ico"
|
||||
},
|
||||
"microsoft teams": {
|
||||
"Microsoft Teams个人免费版入口": {
|
||||
"name": "Microsoft Teams个人免费版网页端/聊天视频会议协作平台",
|
||||
"categoryId": 1,
|
||||
"url": "teams.live.com",
|
||||
"icon": "https://teams.live.com/favicon.ico"
|
||||
},
|
||||
"Microsoft Teams企业版入口": {
|
||||
"name": "Microsoft Teams企业版网页端/聊天视频会议协作平台",
|
||||
"categoryId": 1,
|
||||
"url": "teams.microsoft.com",
|
||||
"icon": "https://teams.microsoft.com/favicon.ico"
|
||||
},
|
||||
"Microsoft Teams会议通话代理API": {
|
||||
"name": "Microsoft Teams会议会话与通话管理API",
|
||||
"categoryId": 11,
|
||||
"url": "api.flightproxy.teams.microsoft.com",
|
||||
"icon": "https://teams.microsoft.com/favicon.ico"
|
||||
},
|
||||
"Microsoft Teams主API服务": {
|
||||
"name": "Microsoft Teams主API接口",
|
||||
"categoryId": 11,
|
||||
"url": "api.teams.microsoft.com",
|
||||
"icon": "https://teams.microsoft.com/favicon.ico"
|
||||
},
|
||||
"Microsoft Teams配置服务": {
|
||||
"name": "Microsoft Teams客户端配置获取服务",
|
||||
"categoryId": 11,
|
||||
"url": "config.teams.microsoft.com",
|
||||
"icon": "https://teams.microsoft.com/favicon.ico"
|
||||
},
|
||||
"Microsoft Teams在线状态API": {
|
||||
"name": "Microsoft Teams用户在线状态查询API",
|
||||
"categoryId": 11,
|
||||
"url": "presence.teams.microsoft.com",
|
||||
"icon": "https://teams.microsoft.com/favicon.ico"
|
||||
},
|
||||
"Microsoft Teams信令路由服务": {
|
||||
"name": "Microsoft Teams实时通信信令路由",
|
||||
"categoryId": 11,
|
||||
"url": "trouter.teams.microsoft.com",
|
||||
"icon": "https://teams.microsoft.com/favicon.ico"
|
||||
},
|
||||
"Microsoft Teams认证服务": {
|
||||
"name": "Microsoft Teams账号认证服务",
|
||||
"categoryId": 22,
|
||||
"url": "authsvc.teams.microsoft.com",
|
||||
"icon": "https://teams.microsoft.com/favicon.ico"
|
||||
},
|
||||
"Microsoft Teams静态资源CDN": {
|
||||
"name": "Microsoft Teams前端静态资源CDN",
|
||||
"categoryId": 2,
|
||||
"url": "ecdn.teams.microsoft.com",
|
||||
"icon": "https://teams.microsoft.com/favicon.ico"
|
||||
},
|
||||
"Microsoft Teams文件服务": {
|
||||
"name": "Microsoft Teams文件存储与传输服务",
|
||||
"categoryId": 11,
|
||||
"url": "files.teams.microsoft.com",
|
||||
"icon": "https://teams.microsoft.com/favicon.ico"
|
||||
},
|
||||
"Microsoft Teams通知服务": {
|
||||
"name": "Microsoft Teams消息与通知推送服务",
|
||||
"categoryId": 11,
|
||||
"url": "notifications.teams.microsoft.com",
|
||||
"icon": "https://teams.microsoft.com/favicon.ico"
|
||||
},
|
||||
"Microsoft Teams遥测数据上报": {
|
||||
"name": "Microsoft Teams性能与异常数据收集",
|
||||
"categoryId": 5,
|
||||
"url": "telemetry.teams.microsoft.com",
|
||||
"icon": "https://teams.microsoft.com/favicon.ico"
|
||||
},
|
||||
"Microsoft Teams企业信令路由节点(USWE-14)": {
|
||||
"name": "Microsoft Teams企业租户公网信令路由服务(美国西部第14节点)",
|
||||
"categoryId": 11,
|
||||
"url": "pub-ent-uswe-14-t.trouter.teams.microsoft.com",
|
||||
"icon": "https://teams.microsoft.com/favicon.ico"
|
||||
},
|
||||
"Microsoft Teams企业信令路由节点(USWE-17)": {
|
||||
"name": "Microsoft Teams企业租户公网信令路由服务(美国西部第17节点)",
|
||||
"categoryId": 11,
|
||||
"url": "pub-ent-uswe-17-t.trouter.teams.microsoft.com",
|
||||
"icon": "https://teams.microsoft.com/favicon.ico"
|
||||
},
|
||||
"Microsoft Teams信令路由全局入口": {
|
||||
"name": "Microsoft Teams客户端信令路由初始化与最优节点选择服务",
|
||||
"categoryId": 11,
|
||||
"url": "go.trouter.teams.microsoft.com",
|
||||
"icon": "https://teams.microsoft.com/favicon.ico"
|
||||
},
|
||||
"Microsoft Teams生产环境异步网关": {
|
||||
"name": "Microsoft Teams异步请求处理服务(URL预览/链接展开)",
|
||||
"categoryId": 11,
|
||||
"url": "as-prod.asyncgw.teams.microsoft.com",
|
||||
"icon": "https://teams.microsoft.com/favicon.ico"
|
||||
},
|
||||
"Microsoft Teams端点配置服务(ECS)": {
|
||||
"name": "Microsoft Teams客户端配置与更新管理服务",
|
||||
"categoryId": 11,
|
||||
"url": "config.teams.microsoft.com",
|
||||
"icon": "https://teams.microsoft.com/favicon.ico"
|
||||
},
|
||||
"Microsoft Teams全球Azure区域媒体中继服务": {
|
||||
"name": "Microsoft Teams媒体流中继服务(音频/视频/屏幕共享中转)",
|
||||
"categoryId": 11,
|
||||
"url": "worldaz.relay.teams.microsoft.com",
|
||||
"icon": "https://teams.microsoft.com/favicon.ico"
|
||||
}
|
||||
},
|
||||
"Azure": {
|
||||
"微软Azure官网": {
|
||||
"name": "Azure",
|
||||
@@ -4076,6 +4187,259 @@
|
||||
"icon": "#"
|
||||
},
|
||||
"company": "未知/个人"
|
||||
},
|
||||
"sentryio": {
|
||||
"Sentry项目专属数据摄入节点": {
|
||||
"name": "Sentry项目专属错误日志与性能指标摄入服务",
|
||||
"categoryId": 5,
|
||||
"url": "o1098464.ingest.sentry.io",
|
||||
"icon": "https://sentry.io/static/favicon-46f8676a36982f8eb852ac6860387755.ico"
|
||||
},
|
||||
"Sentry平台主API接口": {
|
||||
"name": "Sentry平台主API/项目管理与事件查询服务",
|
||||
"categoryId": 11,
|
||||
"url": "api.sentry.io",
|
||||
"icon": "https://sentry.io/static/favicon-46f8676a36982f8eb852ac6860387755.ico"
|
||||
},
|
||||
"Sentry全局统计数据节点": {
|
||||
"name": "Sentry全局统计数据/项目错误趋势与性能指标汇总服务",
|
||||
"categoryId": 5,
|
||||
"url": "stats.sentry.io",
|
||||
"icon": "https://sentry.io/static/favicon-46f8676a36982f8eb852ac6860387755.ico"
|
||||
},
|
||||
"Sentry统一身份认证节点": {
|
||||
"name": "Sentry统一身份认证/账号登录与OAuth授权服务",
|
||||
"categoryId": 22,
|
||||
"url": "auth.sentry.io",
|
||||
"icon": "https://sentry.io/static/favicon-46f8676a36982f8eb852ac6860387755.ico"
|
||||
},
|
||||
"Sentry静态资源CDN节点": {
|
||||
"name": "Sentry静态资源CDN/SDK与控制台资源分发服务",
|
||||
"categoryId": 2,
|
||||
"url": "cdn.sentry.io",
|
||||
"icon": "https://sentry.io/static/favicon-46f8676a36982f8eb852ac6860387755.ico"
|
||||
},
|
||||
"Sentry官方文档中心": {
|
||||
"name": "Sentry官方文档中心/SDK集成与故障排查指南",
|
||||
"categoryId": 15,
|
||||
"url": "docs.sentry.io",
|
||||
"icon": "https://sentry.io/static/favicon-46f8676a36982f8eb852ac6860387755.ico"
|
||||
},
|
||||
"Sentry平台服务状态页": {
|
||||
"name": "Sentry平台服务状态/节点可用性与故障公告",
|
||||
"categoryId": 21,
|
||||
"url": "status.sentry.io",
|
||||
"icon": "https://sentry.io/static/favicon-46f8676a36982f8eb852ac6860387755.ico"
|
||||
}
|
||||
},
|
||||
"爱奇艺": {
|
||||
"fluxbak.iqiyi.com": {
|
||||
"name": "爱奇艺Flux数据备份服务",
|
||||
"categoryId": 27,
|
||||
"url": "fluxbak.iqiyi.com",
|
||||
"icon": "https://www.iqiyi.com/favicon.ico"
|
||||
},
|
||||
"爱奇艺图片CDN": {
|
||||
"name": "爱奇艺图片CDN加速服务",
|
||||
"categoryId": 2,
|
||||
"url": {
|
||||
"1": "pic0.iqiyipic.com",
|
||||
"2": "pic1.iqiyipic.com",
|
||||
"3": "pic2.iqiyipic.com",
|
||||
"4": "pic3.iqiyipic.com",
|
||||
"5": "pic4.iqiyipic.com",
|
||||
"6": "pic5.iqiyipic.com",
|
||||
"7": "pic6.iqiyipic.com",
|
||||
"8": "pic7.iqiyipic.com",
|
||||
"9": "pic8.iqiyipic.com",
|
||||
"10": "pic9.iqiyipic.com",
|
||||
"11": "pic.cdn.iqiyi.com"
|
||||
},
|
||||
"icon": "https://www.iqiyi.com/favicon.ico"
|
||||
},
|
||||
"data.video.iqiyi.com": {
|
||||
"name": "爱奇艺视频数据接口服务",
|
||||
"categoryId": 2,
|
||||
"url": "data.video.iqiyi.com",
|
||||
"icon": "https://www.iqiyi.com/favicon.ico"
|
||||
},
|
||||
"company": "北京爱奇艺科技有限公司"
|
||||
},
|
||||
"银狐木马": {
|
||||
"wangzheguilaioss.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "wangzheguilaioss.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"muchengoss.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "muchengoss.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"mustdll.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "mustdll.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"titi2-4.oss-cn-hangzhou.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "titi2-4.oss-cn-hangzhou.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"11bucketyun.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "11bucketyun.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"jubaopengosssi.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "jubaopengosssi.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"variety.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "variety.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"alivt.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "alivt.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"shimo-oss1.oss-cn-hangzhou.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "shimo-oss1.oss-cn-hangzhou.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"kefubahaohonsheng.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "kefubahaohonsheng.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"alibwj.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "alibwj.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"adll.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "adll.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"jpbdoss.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "jpbdoss.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"baiduwenshen.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "baiduwenshen.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"jubaopengosser.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "jubaopengosser.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"ossbaiwenj.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "ossbaiwenj.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"aliyunlianjieoss.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "aliyunlianjieoss.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"winios2024.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "winios2024.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"whitefile.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "whitefile.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"bucketyun11.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "bucketyun11.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"bucketossbj.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "bucketossbj.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"51yunpio.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "51yunpio.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"aexe.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "aexe.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"supervt.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "supervt.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"osstesto.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "osstesto.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"alidll.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "alidll.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"condongjkhdsgdsd.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "condongjkhdsgdsd.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"shunfengoss.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "shunfengoss.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
},
|
||||
"423down.oss-cn-hongkong.aliyuncs.com": {
|
||||
"name": "阿里云OSS对象存储",
|
||||
"categoryId": 28,
|
||||
"url": "423down.oss-cn-hongkong.aliyuncs.com",
|
||||
"icon": "../images/malware.svg"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -20,7 +20,6 @@
|
||||
"16": "",
|
||||
"101": "移动分析"
|
||||
},
|
||||
|
||||
"trackers": {
|
||||
"163": {
|
||||
"name": "163",
|
||||
@@ -28,7 +27,7 @@
|
||||
"url": "http://www.163.com/",
|
||||
"companyId": "163"
|
||||
},
|
||||
"miui.com":{
|
||||
"miui.com": {
|
||||
"name": "MIUI",
|
||||
"categoryId": 101,
|
||||
"url": "http://tracking.miui.com",
|
||||
@@ -14312,7 +14311,7 @@
|
||||
"companyId": "tencent",
|
||||
"source": "AdGuard"
|
||||
},
|
||||
"weixin":{
|
||||
"weixin": {
|
||||
"name": "微信广告",
|
||||
"categoryId": 12,
|
||||
"url": "https://wxsnsdythumb.wxs.qq.com",
|
||||
@@ -25344,4 +25343,4 @@
|
||||
"zwaar.org": "zwaar",
|
||||
"extend.tv": "zypmedia"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg t="1770892336492" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1932" width="200" height="200"><path d="M512 0a512 512 0 1 1-0.064 1024.064A512 512 0 0 1 512 0z m0 684.8a64 64 0 1 0 0 128 64 64 0 0 0 0-128zM539.456 256H484.48l-4.416 0.448a21.952 21.952 0 0 0-17.472 21.504V588.8l0.448 4.416c2.048 9.984 10.88 17.536 21.44 17.536h54.912l4.416-0.448A21.952 21.952 0 0 0 561.28 588.8V277.952l-0.448-4.48A21.952 21.952 0 0 0 539.52 256z" fill="#ED7B00" p-id="1933"></path></svg>
|
||||
|
After Width: | Height: | Size: 525 B |
+1211
-469
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,293 @@
|
||||
// about.js - 关于页面功能实现
|
||||
|
||||
// 初始化关于页面
|
||||
async function initAboutPage() {
|
||||
try {
|
||||
console.log('初始化关于页面');
|
||||
|
||||
// 立即显示内容,确保页面不会空白
|
||||
const contentElement = document.getElementById('about-content');
|
||||
if (contentElement) {
|
||||
contentElement.classList.remove('hidden');
|
||||
}
|
||||
|
||||
// 检查是否有有效的缓存数据
|
||||
const cachedData = window.pageDataCache && window.pageDataCache.getCache('about');
|
||||
if (cachedData) {
|
||||
console.log('使用缓存的关于页面数据');
|
||||
updateAboutPageUI(cachedData);
|
||||
return;
|
||||
}
|
||||
|
||||
// 并行获取服务器信息和版本信息
|
||||
const [serverInfo, versionInfo] = await Promise.all([
|
||||
fetchServerInfo(),
|
||||
fetchVersionInfo()
|
||||
]);
|
||||
|
||||
// 组合数据
|
||||
const aboutData = {
|
||||
serverInfo,
|
||||
versionInfo
|
||||
};
|
||||
|
||||
// 更新UI
|
||||
updateAboutPageUI(aboutData);
|
||||
|
||||
// 存储数据到缓存
|
||||
if (window.pageDataCache) {
|
||||
window.pageDataCache.setCache('about', aboutData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('初始化关于页面失败:', error);
|
||||
showAboutError('加载数据失败,请稍后重试');
|
||||
|
||||
// 即使出错也显示默认内容
|
||||
const defaultData = {
|
||||
serverInfo: {
|
||||
version: '1.0.0',
|
||||
uptime: 0,
|
||||
system: 'Unknown',
|
||||
platform: 'Unknown',
|
||||
arch: 'Unknown',
|
||||
goVersion: 'Unknown',
|
||||
cpu: 'Unknown',
|
||||
memory: 'Unknown',
|
||||
disk: 'Unknown'
|
||||
},
|
||||
versionInfo: {
|
||||
mainFile: {
|
||||
version: '1.0.0',
|
||||
lastUpdate: new Date().toISOString()
|
||||
},
|
||||
configFile: {
|
||||
version: '1.0.0',
|
||||
lastUpdate: new Date().toISOString()
|
||||
},
|
||||
ruleFiles: {
|
||||
version: '1.0.0',
|
||||
lastUpdate: new Date().toISOString()
|
||||
}
|
||||
}
|
||||
};
|
||||
updateAboutPageUI(defaultData);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取服务器信息
|
||||
async function fetchServerInfo() {
|
||||
try {
|
||||
const response = await fetch('/api/server-info');
|
||||
if (!response.ok) {
|
||||
throw new Error(`获取服务器信息失败: ${response.status}`);
|
||||
}
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('获取服务器信息失败:', error);
|
||||
// 返回默认值
|
||||
return {
|
||||
version: '1.0.0',
|
||||
uptime: 0,
|
||||
system: 'Unknown',
|
||||
platform: 'Unknown',
|
||||
arch: 'Unknown',
|
||||
goVersion: 'Unknown',
|
||||
cpu: 'Unknown',
|
||||
memory: 'Unknown',
|
||||
disk: 'Unknown'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 获取版本信息
|
||||
async function fetchVersionInfo() {
|
||||
try {
|
||||
const response = await fetch('/api/version-info');
|
||||
if (!response.ok) {
|
||||
throw new Error(`获取版本信息失败: ${response.status}`);
|
||||
}
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('获取版本信息失败:', error);
|
||||
// 返回默认值
|
||||
return {
|
||||
mainFile: {
|
||||
version: '1.0.0',
|
||||
lastUpdate: new Date().toISOString()
|
||||
},
|
||||
configFile: {
|
||||
version: '1.0.0',
|
||||
lastUpdate: new Date().toISOString()
|
||||
},
|
||||
ruleFiles: {
|
||||
version: '1.0.0',
|
||||
lastUpdate: new Date().toISOString()
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 更新关于页面UI
|
||||
function updateAboutPageUI(data) {
|
||||
if (!data) return;
|
||||
|
||||
const { serverInfo, versionInfo } = data;
|
||||
|
||||
// 更新服务器基本信息
|
||||
updateServerInfoUI(serverInfo);
|
||||
|
||||
// 更新版本信息
|
||||
updateVersionInfoUI(versionInfo);
|
||||
|
||||
// 隐藏加载状态(已移除加载元素)
|
||||
|
||||
// 显示内容
|
||||
const contentElement = document.getElementById('about-content');
|
||||
if (contentElement) {
|
||||
contentElement.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// 更新服务器基本信息UI
|
||||
function updateServerInfoUI(info) {
|
||||
if (!info) return;
|
||||
|
||||
const elements = {
|
||||
'server-version': info.version || '1.0.0',
|
||||
'server-uptime': formatUptime(info.uptime || 0),
|
||||
'server-system': info.system || 'Unknown',
|
||||
'server-platform': info.platform || 'Unknown',
|
||||
'server-arch': info.arch || 'Unknown',
|
||||
'server-go-version': info.goVersion || 'Unknown',
|
||||
'server-cpu': info.cpu || 'Unknown',
|
||||
'server-memory': info.memory || 'Unknown',
|
||||
'server-disk': info.disk || 'Unknown'
|
||||
};
|
||||
|
||||
Object.entries(elements).forEach(([id, value]) => {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.textContent = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 更新版本信息UI
|
||||
function updateVersionInfoUI(info) {
|
||||
if (!info) return;
|
||||
|
||||
const elements = {
|
||||
'main-file-version': info.mainFile?.version || '1.0.0',
|
||||
'main-file-update': formatDate(info.mainFile?.lastUpdate || new Date()),
|
||||
'config-file-version': info.configFile?.version || '1.0.0',
|
||||
'config-file-update': formatDate(info.configFile?.lastUpdate || new Date()),
|
||||
'rule-files-version': info.ruleFiles?.version || '1.0.0',
|
||||
'rule-files-update': formatDate(info.ruleFiles?.lastUpdate || new Date())
|
||||
};
|
||||
|
||||
Object.entries(elements).forEach(([id, value]) => {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.textContent = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 显示错误信息
|
||||
function showAboutError(message) {
|
||||
const errorElement = document.getElementById('about-error');
|
||||
if (errorElement) {
|
||||
errorElement.textContent = message;
|
||||
errorElement.classList.remove('hidden');
|
||||
}
|
||||
|
||||
// 隐藏加载状态(已移除加载元素)
|
||||
}
|
||||
|
||||
// 格式化运行时间
|
||||
function formatUptime(seconds) {
|
||||
if (!seconds || seconds < 0) return '0秒';
|
||||
|
||||
const days = Math.floor(seconds / (24 * 60 * 60));
|
||||
const hours = Math.floor((seconds % (24 * 60 * 60)) / (60 * 60));
|
||||
const minutes = Math.floor((seconds % (60 * 60)) / 60);
|
||||
const secs = Math.floor(seconds % 60);
|
||||
|
||||
let result = '';
|
||||
if (days > 0) result += `${days}天 `;
|
||||
if (hours > 0) result += `${hours}小时 `;
|
||||
if (minutes > 0) result += `${minutes}分钟 `;
|
||||
if (secs > 0 || result === '') result += `${secs}秒`;
|
||||
|
||||
return result.trim();
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
function formatDate(date) {
|
||||
if (!date) return 'Unknown';
|
||||
|
||||
const dateObj = typeof date === 'string' ? new Date(date) : date;
|
||||
if (isNaN(dateObj.getTime())) return 'Unknown';
|
||||
|
||||
return dateObj.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
});
|
||||
}
|
||||
|
||||
// 刷新关于页面数据
|
||||
function refreshAboutPage() {
|
||||
// 清除缓存
|
||||
if (window.pageDataCache) {
|
||||
window.pageDataCache.clearCache('about');
|
||||
window.pageDataCache.initializedPages['about'] = false;
|
||||
}
|
||||
|
||||
// 显示加载状态(已移除加载元素)
|
||||
|
||||
// 隐藏错误信息
|
||||
const errorElement = document.getElementById('about-error');
|
||||
if (errorElement) {
|
||||
errorElement.classList.add('hidden');
|
||||
}
|
||||
|
||||
// 重新初始化页面
|
||||
initAboutPage();
|
||||
}
|
||||
|
||||
// 设置关于页面事件监听器
|
||||
function setupAboutEventListeners() {
|
||||
// 刷新按钮事件
|
||||
const refreshBtn = document.getElementById('refresh-about-btn');
|
||||
if (refreshBtn) {
|
||||
refreshBtn.addEventListener('click', refreshAboutPage);
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 检查当前是否是about页面
|
||||
if (window.location.hash === '#about') {
|
||||
initAboutPage();
|
||||
}
|
||||
setupAboutEventListeners();
|
||||
});
|
||||
} else {
|
||||
// 检查当前是否是about页面
|
||||
if (window.location.hash === '#about') {
|
||||
initAboutPage();
|
||||
}
|
||||
setupAboutEventListeners();
|
||||
}
|
||||
|
||||
// 当切换到about页面时重新加载数据
|
||||
window.addEventListener('hashchange', function() {
|
||||
if (window.location.hash === '#about') {
|
||||
initAboutPage();
|
||||
}
|
||||
});
|
||||
|
||||
+4
-1
@@ -184,7 +184,10 @@ const api = {
|
||||
saveConfig: (config) => apiRequest('/config', 'POST', config),
|
||||
|
||||
// 重启服务
|
||||
restartService: () => apiRequest('/config/restart', 'POST')
|
||||
restartService: () => apiRequest('/config/restart', 'POST'),
|
||||
|
||||
// 域名信息查询
|
||||
domainInfo: (domain) => apiRequest(`/domain-info?domains=${encodeURIComponent(domain)}`, 'GET')
|
||||
};
|
||||
|
||||
// 导出API工具
|
||||
|
||||
+59
-37
@@ -22,6 +22,10 @@ window.dashboardHistoryData = window.dashboardHistoryData || {
|
||||
let lastProcessedTime = 0;
|
||||
const PROCESS_THROTTLE_INTERVAL = 1000; // 1秒节流间隔
|
||||
|
||||
// 跟踪器信息缓存
|
||||
const trackerInfoCache = {};
|
||||
const TRACKER_CACHE_EXPIRY = 24 * 60 * 60 * 1000; // 24小时缓存过期时间
|
||||
|
||||
// 引入颜色配置文件
|
||||
const COLOR_CONFIG = window.COLOR_CONFIG || {};
|
||||
|
||||
@@ -944,7 +948,7 @@ async function updateTopBlockedTable(domains) {
|
||||
<div class="tracker-tooltip absolute z-50 bg-white shadow-lg rounded-md border p-3 min-w-64 text-sm">
|
||||
<div class="font-semibold mb-2">已知跟踪器</div>
|
||||
<div class="mb-1"><strong>名称:</strong> ${trackerInfo.name || '未知'}</div>
|
||||
<div class="mb-1"><strong>类别:</strong> ${trackerInfo.categoryId && trackersDatabase && trackersDatabase.categories ? trackersDatabase.categories[trackerInfo.categoryId] : '未知'}</div>
|
||||
<div class="mb-1"><strong>类别:</strong> ${trackerInfo.category || '未知'}</div>
|
||||
${trackerInfo.url ? `<div class="mb-1"><strong>URL:</strong> <a href="${trackerInfo.url}" target="_blank" class="text-blue-500 hover:underline">${trackerInfo.url}</a></div>` : ''}
|
||||
${trackerInfo.source ? `<div class="mb-1"><strong>源:</strong> ${trackerInfo.source}</div>` : ''}
|
||||
</div>
|
||||
@@ -1082,7 +1086,7 @@ async function loadTrackersDatabase() {
|
||||
trackersLoading = true;
|
||||
|
||||
try {
|
||||
const response = await fetch('domain-info/tracker/trackers.json');
|
||||
const response = await fetch('/api/domain-info?trackers');
|
||||
if (!response.ok) {
|
||||
console.error('加载跟踪器数据库失败:', response.statusText);
|
||||
trackersDatabase = { trackers: {} };
|
||||
@@ -1103,37 +1107,61 @@ async function loadTrackersDatabase() {
|
||||
|
||||
// 检查域名是否在跟踪器数据库中
|
||||
async function isDomainInTrackerDatabase(domain) {
|
||||
if (!trackersDatabase || !trackersLoaded) {
|
||||
await loadTrackersDatabase();
|
||||
// 检查缓存
|
||||
const now = Date.now();
|
||||
if (trackerInfoCache[domain] && (now - trackerInfoCache[domain].timestamp) < TRACKER_CACHE_EXPIRY) {
|
||||
return trackerInfoCache[domain].data;
|
||||
}
|
||||
|
||||
if (!trackersDatabase || !trackersDatabase.trackers) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查域名是否直接作为跟踪器键存在
|
||||
if (trackersDatabase.trackers.hasOwnProperty(domain)) {
|
||||
return trackersDatabase.trackers[domain];
|
||||
}
|
||||
|
||||
// 检查域名是否在跟踪器URL中
|
||||
for (const trackerKey in trackersDatabase.trackers) {
|
||||
if (trackersDatabase.trackers.hasOwnProperty(trackerKey)) {
|
||||
const tracker = trackersDatabase.trackers[trackerKey];
|
||||
if (tracker && tracker.url) {
|
||||
try {
|
||||
const trackerUrl = new URL(tracker.url);
|
||||
if (trackerUrl.hostname === domain) {
|
||||
return tracker;
|
||||
}
|
||||
} catch (e) {
|
||||
// 忽略无效URL
|
||||
}
|
||||
try {
|
||||
// 使用新的API端点获取跟踪器信息
|
||||
const response = await fetch(`/api/domain-info?trackers=${encodeURIComponent(domain)}`);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('获取跟踪器信息失败:', response.statusText);
|
||||
// 将失败结果也存入缓存,避免频繁请求失败的域名
|
||||
trackerInfoCache[domain] = {
|
||||
data: null,
|
||||
timestamp: now
|
||||
};
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
let result = null;
|
||||
if (Array.isArray(data) && data.length > 0) {
|
||||
// 如果有多个跟踪器,返回一个包含所有跟踪器的对象
|
||||
if (data.length === 1) {
|
||||
result = data[0];
|
||||
} else {
|
||||
// 构建一个包含所有跟踪器信息的对象
|
||||
result = {
|
||||
name: data.map(t => t.name).join(' | '),
|
||||
category: data.map(t => t.category).filter((v, i, a) => a.indexOf(v) === i).join(' | '),
|
||||
url: data.map(t => t.url).join(' | '),
|
||||
company: data.map(t => t.company).filter((v, i, a) => a.indexOf(v) === i).join(' | '),
|
||||
multiple: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 将结果存入缓存
|
||||
trackerInfoCache[domain] = {
|
||||
data: result,
|
||||
timestamp: now
|
||||
};
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('获取跟踪器信息失败:', error);
|
||||
// 将失败结果也存入缓存,避免频繁请求失败的域名
|
||||
trackerInfoCache[domain] = {
|
||||
data: null,
|
||||
timestamp: now
|
||||
};
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取IP地理位置信息
|
||||
@@ -1360,7 +1388,7 @@ async function updateTopDomainsTable(domains) {
|
||||
<div class="tracker-tooltip absolute z-50 bg-white shadow-lg rounded-md border p-3 min-w-64 text-sm">
|
||||
<div class="font-semibold mb-2">已知跟踪器</div>
|
||||
<div class="mb-1"><strong>名称:</strong> ${trackerInfo.name || '未知'}</div>
|
||||
<div class="mb-1"><strong>类别:</strong> ${trackerInfo.categoryId && trackersDatabase && trackersDatabase.categories ? trackersDatabase.categories[trackerInfo.categoryId] : '未知'}</div>
|
||||
<div class="mb-1"><strong>类别:</strong> ${trackerInfo.category || '未知'}</div>
|
||||
${trackerInfo.url ? `<div class="mb-1"><strong>URL:</strong> <a href="${trackerInfo.url}" target="_blank" class="text-blue-500 hover:underline">${trackerInfo.url}</a></div>` : ''}
|
||||
${trackerInfo.source ? `<div class="mb-1"><strong>源:</strong> ${trackerInfo.source}</div>` : ''}
|
||||
</div>
|
||||
@@ -3180,10 +3208,7 @@ async function loadDashboardData() {
|
||||
// 更新图表
|
||||
updateCharts(stats, queryTypeStats);
|
||||
|
||||
// 初始化或更新查询类型统计饼图
|
||||
if (queryTypeStats) {
|
||||
drawQueryTypeChart(queryTypeStats);
|
||||
}
|
||||
|
||||
|
||||
// 更新查询类型统计信息
|
||||
if (document.getElementById('top-query-type')) {
|
||||
@@ -3307,10 +3332,7 @@ function processDashboardData(cachedData) {
|
||||
// 更新图表
|
||||
updateCharts(stats, queryTypeStats);
|
||||
|
||||
// 初始化或更新查询类型统计饼图
|
||||
if (queryTypeStats) {
|
||||
drawQueryTypeChart(queryTypeStats);
|
||||
}
|
||||
|
||||
|
||||
// 更新查询类型统计信息
|
||||
if (document.getElementById('top-query-type')) {
|
||||
|
||||
+38
-173
@@ -121,10 +121,9 @@ if (typeof trackersDatabase === 'undefined') {
|
||||
var trackersLoading = false;
|
||||
}
|
||||
|
||||
// 域名信息数据库缓存
|
||||
let domainInfoDatabase = null;
|
||||
let domainInfoLoaded = false;
|
||||
let domainInfoLoading = false;
|
||||
// 域名信息缓存(用于 API 调用)
|
||||
let domainInfoCache = {};
|
||||
let domainInfoCacheLoading = {};
|
||||
|
||||
// WebSocket连接和重连计时器
|
||||
let logsWsConnection = null;
|
||||
@@ -163,43 +162,51 @@ async function loadTrackersDatabase() {
|
||||
}
|
||||
}
|
||||
|
||||
// 加载域名信息数据库
|
||||
async function loadDomainInfoDatabase() {
|
||||
|
||||
if (domainInfoLoaded) {
|
||||
return domainInfoDatabase;
|
||||
// 从 API 获取域名信息
|
||||
async function getDomainInfoFromAPI(domain) {
|
||||
// 检查缓存
|
||||
if (domainInfoCache[domain]) {
|
||||
return domainInfoCache[domain];
|
||||
}
|
||||
|
||||
if (domainInfoLoading) {
|
||||
// 等待正在进行的加载完成
|
||||
while (domainInfoLoading) {
|
||||
// 检查是否正在加载中
|
||||
if (domainInfoCacheLoading[domain]) {
|
||||
while (domainInfoCacheLoading[domain]) {
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
}
|
||||
return domainInfoDatabase;
|
||||
return domainInfoCache[domain] || null;
|
||||
}
|
||||
|
||||
domainInfoLoading = true;
|
||||
domainInfoCacheLoading[domain] = true;
|
||||
|
||||
try {
|
||||
const response = await fetch('domain-info/domains/domain-info.json');
|
||||
|
||||
const response = await fetch(`/api/domain-info?domains=${encodeURIComponent(domain)}`);
|
||||
if (!response.ok) {
|
||||
console.error('加载域名信息数据库失败,HTTP状态:', response.status, response.statusText);
|
||||
console.error('请求URL:', response.url);
|
||||
domainInfoDatabase = { domains: {}, categories: {} };
|
||||
return domainInfoDatabase;
|
||||
console.error('获取域名信息失败:', response.statusText);
|
||||
return null;
|
||||
}
|
||||
|
||||
domainInfoDatabase = await response.json();
|
||||
domainInfoLoaded = true;
|
||||
return domainInfoDatabase;
|
||||
const data = await response.json();
|
||||
|
||||
// API 返回的是数组,取第一个匹配的结果
|
||||
if (Array.isArray(data) && data.length > 0) {
|
||||
const info = data[0];
|
||||
// 缓存结果
|
||||
domainInfoCache[domain] = {
|
||||
name: info.name || '未知',
|
||||
icon: info.icon || null,
|
||||
category: info.category || '未知',
|
||||
company: info.company || '未知'
|
||||
};
|
||||
return domainInfoCache[domain];
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error('加载域名信息数据库失败,错误信息:', error.message);
|
||||
console.error('错误堆栈:', error.stack);
|
||||
domainInfoDatabase = { domains: {}, categories: {} };
|
||||
return domainInfoDatabase;
|
||||
console.error('获取域名信息失败:', error);
|
||||
return null;
|
||||
} finally {
|
||||
domainInfoLoading = false;
|
||||
domainInfoCacheLoading[domain] = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,152 +245,10 @@ async function isDomainInTrackerDatabase(domain) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 根据域名查找对应的网站信息
|
||||
// 根据域名查找对应的网站信息(使用 API)
|
||||
async function getDomainInfo(domain) {
|
||||
|
||||
if (!domainInfoDatabase || !domainInfoLoaded) {
|
||||
await loadDomainInfoDatabase();
|
||||
}
|
||||
|
||||
if (!domainInfoDatabase || !domainInfoDatabase.domains) {
|
||||
console.error('域名信息数据库无效或为空');
|
||||
return null;
|
||||
}
|
||||
|
||||
// 规范化域名,移除可能的端口号
|
||||
const normalizedDomain = domain.replace(/:\d+$/, '').toLowerCase();
|
||||
|
||||
// 遍历所有公司
|
||||
for (const companyKey in domainInfoDatabase.domains) {
|
||||
if (domainInfoDatabase.domains.hasOwnProperty(companyKey)) {
|
||||
const companyData = domainInfoDatabase.domains[companyKey];
|
||||
const companyName = companyData.company || companyKey;
|
||||
|
||||
// 遍历公司下的所有网站和类别
|
||||
for (const websiteKey in companyData) {
|
||||
if (companyData.hasOwnProperty(websiteKey) && websiteKey !== 'company') {
|
||||
const website = companyData[websiteKey];
|
||||
|
||||
// 如果有URL属性,直接检查域名
|
||||
if (website.url) {
|
||||
// 处理字符串类型的URL
|
||||
if (typeof website.url === 'string') {
|
||||
if (isDomainMatch(website.url, normalizedDomain, website.categoryId)) {
|
||||
return {
|
||||
name: website.name,
|
||||
icon: website.icon,
|
||||
categoryId: website.categoryId,
|
||||
categoryName: domainInfoDatabase.categories[website.categoryId] || '未知',
|
||||
company: website.company || companyName
|
||||
};
|
||||
}
|
||||
}
|
||||
// 处理对象类型的URL
|
||||
else if (typeof website.url === 'object') {
|
||||
for (const urlKey in website.url) {
|
||||
if (website.url.hasOwnProperty(urlKey)) {
|
||||
const urlValue = website.url[urlKey];
|
||||
if (isDomainMatch(urlValue, normalizedDomain, website.categoryId)) {
|
||||
return {
|
||||
name: website.name,
|
||||
icon: website.icon,
|
||||
categoryId: website.categoryId,
|
||||
categoryName: domainInfoDatabase.categories[website.categoryId] || '未知',
|
||||
company: website.company || companyName
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (typeof website === 'object' && website !== null) {
|
||||
// 没有URL属性,可能是嵌套的类别
|
||||
for (const nestedWebsiteKey in website) {
|
||||
if (website.hasOwnProperty(nestedWebsiteKey) && nestedWebsiteKey !== 'company') {
|
||||
const nestedWebsite = website[nestedWebsiteKey];
|
||||
|
||||
if (nestedWebsite.url) {
|
||||
// 处理字符串类型的URL
|
||||
if (typeof nestedWebsite.url === 'string') {
|
||||
if (isDomainMatch(nestedWebsite.url, normalizedDomain, nestedWebsite.categoryId)) {
|
||||
return {
|
||||
name: nestedWebsite.name,
|
||||
icon: nestedWebsite.icon,
|
||||
categoryId: nestedWebsite.categoryId,
|
||||
categoryName: domainInfoDatabase.categories[nestedWebsite.categoryId] || '未知',
|
||||
company: nestedWebsite.company || companyName
|
||||
};
|
||||
}
|
||||
}
|
||||
// 处理对象类型的URL
|
||||
else if (typeof nestedWebsite.url === 'object') {
|
||||
for (const urlKey in nestedWebsite.url) {
|
||||
if (nestedWebsite.url.hasOwnProperty(urlKey)) {
|
||||
const urlValue = nestedWebsite.url[urlKey];
|
||||
if (isDomainMatch(urlValue, normalizedDomain, nestedWebsite.categoryId)) {
|
||||
return {
|
||||
name: nestedWebsite.name,
|
||||
icon: nestedWebsite.icon,
|
||||
categoryId: nestedWebsite.categoryId,
|
||||
categoryName: domainInfoDatabase.categories[nestedWebsite.categoryId] || '未知',
|
||||
company: nestedWebsite.company || companyName
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (typeof nestedWebsite === 'object' && nestedWebsite !== null) {
|
||||
// 嵌套类别中的嵌套类别,递归检查
|
||||
for (const secondNestedWebsiteKey in nestedWebsite) {
|
||||
if (nestedWebsite.hasOwnProperty(secondNestedWebsiteKey) && secondNestedWebsiteKey !== 'company') {
|
||||
const secondNestedWebsite = nestedWebsite[secondNestedWebsiteKey];
|
||||
|
||||
if (secondNestedWebsite.url) {
|
||||
// 处理字符串类型的URL
|
||||
if (typeof secondNestedWebsite.url === 'string') {
|
||||
if (isDomainMatch(secondNestedWebsite.url, normalizedDomain, secondNestedWebsite.categoryId)) {
|
||||
return {
|
||||
name: secondNestedWebsite.name,
|
||||
icon: secondNestedWebsite.icon,
|
||||
categoryId: secondNestedWebsite.categoryId,
|
||||
categoryName: domainInfoDatabase.categories[secondNestedWebsite.categoryId] || '未知',
|
||||
company: secondNestedWebsite.company || companyName
|
||||
};
|
||||
}
|
||||
}
|
||||
// 处理对象类型的URL
|
||||
else if (typeof secondNestedWebsite.url === 'object') {
|
||||
for (const urlKey in secondNestedWebsite.url) {
|
||||
if (secondNestedWebsite.url.hasOwnProperty(urlKey)) {
|
||||
const urlValue = secondNestedWebsite.url[urlKey];
|
||||
if (isDomainMatch(urlValue, normalizedDomain, secondNestedWebsite.categoryId)) {
|
||||
return {
|
||||
name: secondNestedWebsite.name,
|
||||
icon: secondNestedWebsite.icon,
|
||||
categoryId: secondNestedWebsite.categoryId,
|
||||
categoryName: domainInfoDatabase.categories[secondNestedWebsite.categoryId] || '未知',
|
||||
company: secondNestedWebsite.company || companyName
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return await getDomainInfoFromAPI(domain);
|
||||
}
|
||||
|
||||
// 检查域名是否匹配
|
||||
function isDomainMatch(urlValue, targetDomain, categoryId) {
|
||||
|
||||
@@ -1931,7 +1796,7 @@ async function showLogDetailModal(log) {
|
||||
<div class="mt-1">
|
||||
<div class="flex items-center mb-1 flex-wrap">
|
||||
<span class="text-gray-500 dark:text-gray-400 mr-2">类别:</span>
|
||||
<span class="flex-grow">${domainInfo.categoryName || '未知'}</span>
|
||||
<span class="flex-grow">${domainInfo.category || '未知'}</span>
|
||||
</div>
|
||||
<div class="flex items-center flex-wrap">
|
||||
<span class="text-gray-500 dark:text-gray-400 mr-2">所属单位/公司:</span>
|
||||
|
||||
+60
-20
@@ -30,6 +30,16 @@ const pageDataCache = {
|
||||
timestamp: 0,
|
||||
data: null,
|
||||
expiry: 10 * 60 * 1000 // 10分钟过期
|
||||
},
|
||||
about: {
|
||||
timestamp: 0,
|
||||
data: null,
|
||||
expiry: 10 * 60 * 1000 // 10分钟过期
|
||||
},
|
||||
threats: {
|
||||
timestamp: 0,
|
||||
data: null,
|
||||
expiry: 5 * 60 * 1000 // 5分钟过期
|
||||
}
|
||||
},
|
||||
|
||||
@@ -90,8 +100,11 @@ function initPageByHash() {
|
||||
document.getElementById('hosts-content'),
|
||||
document.getElementById('gfwlist-content'),
|
||||
document.getElementById('query-content'),
|
||||
document.getElementById('domain-content'),
|
||||
document.getElementById('logs-content'),
|
||||
document.getElementById('config-content')
|
||||
document.getElementById('config-content'),
|
||||
document.getElementById('about-content'),
|
||||
document.getElementById('threats-content')
|
||||
];
|
||||
|
||||
contentSections.forEach(section => {
|
||||
@@ -109,17 +122,20 @@ function initPageByHash() {
|
||||
// 更新页面标题
|
||||
const pageTitle = document.getElementById('page-title');
|
||||
if (pageTitle) {
|
||||
const titles = {
|
||||
'dashboard': '仪表盘',
|
||||
'shield': '屏蔽管理',
|
||||
'hosts': 'Hosts管理',
|
||||
'gfwlist': 'GFWList管理',
|
||||
'query': 'DNS屏蔽查询',
|
||||
'logs': '查询日志',
|
||||
'config': '系统设置'
|
||||
};
|
||||
pageTitle.textContent = titles[hash] || '仪表盘';
|
||||
}
|
||||
const titles = {
|
||||
'dashboard': '仪表盘',
|
||||
'shield': '屏蔽管理',
|
||||
'hosts': 'Hosts管理',
|
||||
'gfwlist': 'GFWList管理',
|
||||
'query': 'DNS屏蔽查询',
|
||||
'domain': '域名查询',
|
||||
'logs': '查询日志',
|
||||
'config': '系统设置',
|
||||
'about': '关于',
|
||||
'threats': '威胁告警'
|
||||
};
|
||||
pageTitle.textContent = titles[hash] || '仪表盘';
|
||||
}
|
||||
|
||||
// 页面特定初始化 - 使用setTimeout延迟调用,确保所有脚本文件都已加载完成
|
||||
if (hash === 'shield') {
|
||||
@@ -172,23 +188,43 @@ function initPageByHash() {
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
} else if (hash === 'about') {
|
||||
setTimeout(() => {
|
||||
if (typeof initAboutPage === 'function') {
|
||||
// 检查页面是否已经初始化
|
||||
if (!pageDataCache.isPageInitialized('about') || !pageDataCache.isCacheValid('about')) {
|
||||
initAboutPage();
|
||||
pageDataCache.markPageInitialized('about');
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
} else if (hash === 'threats') {
|
||||
setTimeout(() => {
|
||||
if (typeof initThreatsPage === 'function') {
|
||||
// 检查页面是否已经初始化
|
||||
if (!pageDataCache.isPageInitialized('threats') || !pageDataCache.isCacheValid('threats')) {
|
||||
initThreatsPage();
|
||||
pageDataCache.markPageInitialized('threats');
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化运行时间
|
||||
function formatUptime(milliseconds) {
|
||||
// 简化版的格式化,实际使用时需要根据API返回的数据格式调整
|
||||
const seconds = Math.floor(milliseconds / 1000);
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const days = Math.floor(hours / 24);
|
||||
const totalSeconds = Math.floor(milliseconds / 1000);
|
||||
const days = Math.floor(totalSeconds / (24 * 60 * 60));
|
||||
const hours = Math.floor((totalSeconds % (24 * 60 * 60)) / (60 * 60));
|
||||
const minutes = Math.floor((totalSeconds % (60 * 60)) / 60);
|
||||
const seconds = totalSeconds % 60;
|
||||
|
||||
if (days > 0) {
|
||||
return `${days}天${hours % 24}小时`;
|
||||
return `${days}天${hours}小时${minutes}分钟`;
|
||||
} else if (hours > 0) {
|
||||
return `${hours}小时${minutes % 60}分钟`;
|
||||
return `${hours}小时${minutes}分钟${seconds}秒`;
|
||||
} else if (minutes > 0) {
|
||||
return `${minutes}分钟${seconds % 60}秒`;
|
||||
return `${minutes}分钟${seconds}秒`;
|
||||
} else {
|
||||
return `${seconds}秒`;
|
||||
}
|
||||
@@ -528,6 +564,10 @@ function handleVisibilityChange() {
|
||||
initShieldPage();
|
||||
} else if (hash === 'hosts' && typeof initHostsPage === 'function') {
|
||||
initHostsPage();
|
||||
} else if (hash === 'about' && typeof initAboutPage === 'function') {
|
||||
initAboutPage();
|
||||
} else if (hash === 'threats' && typeof initThreatsPage === 'function') {
|
||||
initThreatsPage();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1280,6 +1280,96 @@ async function handleAddBlacklist(event) {
|
||||
}
|
||||
}
|
||||
|
||||
// 处理一键更新所有黑名单
|
||||
async function handleUpdateAllBlacklists() {
|
||||
try {
|
||||
// 显示通知
|
||||
showNotification('开始更新所有黑名单...', 'info');
|
||||
|
||||
// 获取当前所有黑名单
|
||||
const response = await fetch('/api/shield/blacklists');
|
||||
if (!response.ok) {
|
||||
throw new Error(`获取黑名单失败: ${response.status}`);
|
||||
}
|
||||
|
||||
const blacklists = await response.json();
|
||||
|
||||
// 确保blacklists是数组
|
||||
const blacklistArray = Array.isArray(blacklists) ? blacklists : [];
|
||||
|
||||
if (blacklistArray.length === 0) {
|
||||
showNotification('暂无黑名单可更新', 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新所有黑名单的最后更新时间
|
||||
const updatedBlacklists = blacklistArray.map(blacklist => ({
|
||||
Name: blacklist.name,
|
||||
URL: blacklist.url,
|
||||
Enabled: blacklist.enabled,
|
||||
LastUpdateTime: new Date().toISOString()
|
||||
}));
|
||||
|
||||
// 显示所有黑名单的加载状态
|
||||
blacklistArray.forEach(blacklist => {
|
||||
updateStatus(blacklist.url, 'loading');
|
||||
});
|
||||
|
||||
// 发送更新请求
|
||||
const updateResponse = await fetch('/api/shield/blacklists', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(updatedBlacklists)
|
||||
});
|
||||
|
||||
// 解析服务器响应
|
||||
let responseData;
|
||||
try {
|
||||
responseData = await updateResponse.json();
|
||||
} catch (jsonError) {
|
||||
responseData = {};
|
||||
}
|
||||
|
||||
// 根据服务器响应判断是否成功
|
||||
if (updateResponse.ok && (responseData.status === 'success' || !responseData.status)) {
|
||||
// 显示所有黑名单的成功状态
|
||||
blacklistArray.forEach(blacklist => {
|
||||
updateStatus(blacklist.url, 'success');
|
||||
});
|
||||
|
||||
// 显示通知
|
||||
showNotification('所有黑名单更新成功', 'success');
|
||||
|
||||
// 延迟重新加载黑名单和统计信息,让用户能看到成功状态
|
||||
setTimeout(() => {
|
||||
// 重新加载黑名单
|
||||
loadRemoteBlacklists();
|
||||
// 重新加载统计信息
|
||||
loadShieldStats();
|
||||
}, 3000);
|
||||
} else {
|
||||
// 显示所有黑名单的错误状态
|
||||
blacklistArray.forEach(blacklist => {
|
||||
updateStatus(blacklist.url, 'error', responseData.error || responseData.message || `更新失败: ${updateResponse.status}`);
|
||||
});
|
||||
showNotification(`更新失败: ${responseData.error || responseData.message || updateResponse.status}`, 'error');
|
||||
|
||||
// 延迟重新加载黑名单和统计信息
|
||||
setTimeout(() => {
|
||||
// 重新加载黑名单
|
||||
loadRemoteBlacklists();
|
||||
// 重新加载统计信息
|
||||
loadShieldStats();
|
||||
}, 3000);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('更新所有黑名单失败:', error);
|
||||
showNotification('更新所有黑名单失败: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 当前显示的规则类型:'local' 或 'remote'
|
||||
@@ -1299,6 +1389,12 @@ function setupShieldEventListeners() {
|
||||
saveBlacklistBtn.addEventListener('click', handleAddBlacklist);
|
||||
}
|
||||
|
||||
// 一键更新所有黑名单事件
|
||||
const updateAllBlacklistsBtn = document.getElementById('update-all-blacklists-btn');
|
||||
if (updateAllBlacklistsBtn) {
|
||||
updateAllBlacklistsBtn.addEventListener('click', handleUpdateAllBlacklists);
|
||||
}
|
||||
|
||||
// 添加切换查看自定义规则和远程规则的事件监听
|
||||
const viewLocalRulesBtn = document.getElementById('view-local-rules-btn');
|
||||
if (viewLocalRulesBtn) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user