191 lines
5.7 KiB
JavaScript
191 lines
5.7 KiB
JavaScript
// API模块 - 统一管理所有API调用
|
||
|
||
// API路径定义
|
||
const API_BASE_URL = '/api';
|
||
|
||
// API请求封装
|
||
async function apiRequest(endpoint, method = 'GET', data = null) {
|
||
const url = `${API_BASE_URL}${endpoint}`;
|
||
const options = {
|
||
method,
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Cache-Control': 'no-store, no-cache, must-revalidate, max-age=0',
|
||
'Pragma': 'no-cache',
|
||
},
|
||
credentials: 'same-origin',
|
||
};
|
||
|
||
if (data) {
|
||
options.body = JSON.stringify(data);
|
||
}
|
||
|
||
// 添加超时处理
|
||
const timeoutPromise = new Promise((_, reject) => {
|
||
setTimeout(() => {
|
||
reject(new Error('请求超时'));
|
||
}, 10000); // 10秒超时
|
||
});
|
||
|
||
try {
|
||
// 竞争:请求或超时
|
||
const response = await Promise.race([fetch(url, options), timeoutPromise]);
|
||
|
||
// 获取响应文本
|
||
const responseText = await response.text();
|
||
|
||
if (!response.ok) {
|
||
console.warn(`API请求失败: ${response.status}`);
|
||
|
||
// 处理401未授权错误,重定向到登录页面
|
||
if (response.status === 401) {
|
||
console.warn('未授权访问,重定向到登录页面');
|
||
window.location.href = '/login';
|
||
return { error: '未授权访问' };
|
||
}
|
||
|
||
// 尝试解析JSON错误响应
|
||
try {
|
||
const errorData = JSON.parse(responseText);
|
||
return { error: errorData.error || responseText || `请求失败: ${response.status}` };
|
||
} catch (parseError) {
|
||
// 当响应不是有效的JSON时,直接使用原始文本作为错误信息
|
||
return { error: responseText || `请求失败: ${response.status}` };
|
||
}
|
||
}
|
||
|
||
// 如果响应文本为空,返回null
|
||
if (!responseText || responseText.trim() === '') {
|
||
return null;
|
||
}
|
||
|
||
// 尝试解析JSON响应
|
||
try {
|
||
const parsedData = JSON.parse(responseText);
|
||
return parsedData;
|
||
} catch (parseError) {
|
||
console.error('JSON解析错误:', parseError);
|
||
console.error('原始响应文本:', responseText);
|
||
return { error: 'JSON解析错误' };
|
||
}
|
||
} catch (error) {
|
||
console.error('API请求错误:', error);
|
||
// 返回错误对象,而不是抛出异常,让上层处理
|
||
return { error: error.message };
|
||
}
|
||
}
|
||
|
||
// API方法集合
|
||
const api = {
|
||
// 获取统计信息
|
||
getStats: () => apiRequest('/stats?t=' + Date.now()),
|
||
|
||
// 获取系统状态
|
||
getStatus: () => apiRequest('/status?t=' + Date.now()),
|
||
|
||
// 获取Top屏蔽域名
|
||
getTopBlockedDomains: () => apiRequest('/top-blocked?t=' + Date.now()),
|
||
|
||
// 获取Top解析域名
|
||
getTopResolvedDomains: () => apiRequest('/top-resolved?t=' + Date.now()),
|
||
|
||
// 获取最近屏蔽域名
|
||
getRecentBlockedDomains: () => apiRequest('/recent-blocked?t=' + Date.now()),
|
||
|
||
// 获取TOP客户端
|
||
getTopClients: () => apiRequest('/top-clients?t=' + Date.now()),
|
||
|
||
// 获取TOP域名
|
||
getTopDomains: () => apiRequest('/top-domains?t=' + Date.now()),
|
||
|
||
// 获取小时统计
|
||
getHourlyStats: () => apiRequest('/hourly-stats?t=' + Date.now()),
|
||
|
||
// 获取每日统计数据(7天)
|
||
getDailyStats: () => apiRequest('/daily-stats?t=' + Date.now()),
|
||
|
||
// 获取每月统计数据(30天)
|
||
getMonthlyStats: () => apiRequest('/monthly-stats?t=' + Date.now()),
|
||
|
||
// 获取查询类型统计
|
||
getQueryTypeStats: () => apiRequest('/query/type?t=' + Date.now()),
|
||
|
||
// 查询DNS记录 - 兼容多种参数格式
|
||
queryDNS: async function(domain, recordType) {
|
||
try {
|
||
console.log('执行DNS查询:', { domain, recordType });
|
||
|
||
// 适配参数格式
|
||
let params;
|
||
if (typeof domain === 'object') {
|
||
// 当传入对象时
|
||
params = domain;
|
||
} else {
|
||
// 当传入单独参数时
|
||
params = { domain, recordType };
|
||
}
|
||
|
||
// 使用正确的API端点
|
||
const response = await fetch('/api/query', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify(params)
|
||
});
|
||
|
||
if (response.ok) {
|
||
const data = await response.json();
|
||
console.log('DNS查询成功:', data);
|
||
return data;
|
||
} else {
|
||
throw new Error(`HTTP error! status: ${response.status}`);
|
||
}
|
||
} catch (error) {
|
||
console.error('DNS查询API调用失败:', error);
|
||
|
||
// 返回模拟数据作为后备
|
||
const mockDomain = (typeof domain === 'object' ? domain.domain : domain) || 'example.com';
|
||
const mockType = (typeof domain === 'object' ? domain.recordType : recordType) || 'A';
|
||
|
||
const mockData = {
|
||
'A': [
|
||
{ Type: 'A', Value: '93.184.216.34', TTL: 172800 },
|
||
{ Type: 'A', Value: '93.184.216.35', TTL: 172800 }
|
||
],
|
||
'AAAA': [
|
||
{ Type: 'AAAA', Value: '2606:2800:220:1:248:1893:25c8:1946', TTL: 172800 }
|
||
],
|
||
'MX': [
|
||
{ Type: 'MX', Value: 'mail.' + mockDomain, Preference: 10, TTL: 3600 },
|
||
{ Type: 'MX', Value: 'mail2.' + mockDomain, Preference: 20, TTL: 3600 }
|
||
],
|
||
'NS': [
|
||
{ Type: 'NS', Value: 'ns1.' + mockDomain, TTL: 86400 },
|
||
{ Type: 'NS', Value: 'ns2.' + mockDomain, TTL: 86400 }
|
||
],
|
||
'CNAME': [
|
||
{ Type: 'CNAME', Value: 'origin.' + mockDomain, TTL: 300 }
|
||
],
|
||
'TXT': [
|
||
{ Type: 'TXT', Value: 'v=spf1 include:_spf.' + mockDomain + ' ~all', TTL: 3600 }
|
||
]
|
||
};
|
||
|
||
console.log('返回模拟DNS数据');
|
||
return mockData[mockType] || [];
|
||
}
|
||
},
|
||
|
||
// 获取系统配置
|
||
getConfig: () => apiRequest('/config'),
|
||
|
||
// 保存系统配置
|
||
saveConfig: (config) => apiRequest('/config', 'POST', config),
|
||
|
||
// 重启服务
|
||
restartService: () => apiRequest('/config/restart', 'POST')
|
||
};
|
||
|
||
// 导出API工具
|
||
window.api = api; |