264 lines
8.4 KiB
JavaScript
264 lines
8.4 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);
|
||
}
|
||
|
||
try {
|
||
const response = await fetch(url, options);
|
||
|
||
// 获取响应文本,用于调试和错误处理
|
||
const responseText = await response.text();
|
||
|
||
if (!response.ok) {
|
||
// 尝试解析错误响应
|
||
let errorData = {};
|
||
try {
|
||
// 首先检查响应文本是否为空或不是有效JSON
|
||
if (!responseText || responseText.trim() === '') {
|
||
console.warn('错误响应为空');
|
||
} else {
|
||
try {
|
||
errorData = JSON.parse(responseText);
|
||
} catch (parseError) {
|
||
console.error('无法解析错误响应为JSON:', parseError);
|
||
console.error('原始错误响应文本:', responseText);
|
||
}
|
||
}
|
||
// 直接返回错误信息,而不是抛出异常,让上层处理
|
||
console.warn(`API请求失败: ${response.status}`, errorData);
|
||
return { error: errorData.error || `请求失败: ${response.status}` };
|
||
} catch (e) {
|
||
console.error('处理错误响应时出错:', e);
|
||
return { error: `请求处理失败: ${e.message}` };
|
||
}
|
||
}
|
||
|
||
// 尝试解析成功响应
|
||
try {
|
||
// 首先检查响应文本是否为空
|
||
if (!responseText || responseText.trim() === '') {
|
||
console.warn('空响应文本');
|
||
return {};
|
||
}
|
||
|
||
// 尝试解析JSON
|
||
const parsedData = JSON.parse(responseText);
|
||
return parsedData;
|
||
} catch (parseError) {
|
||
// 详细记录错误信息和响应内容
|
||
console.error('JSON解析错误:', parseError);
|
||
console.error('原始响应文本:', responseText);
|
||
console.error('响应长度:', responseText.length);
|
||
console.error('响应前100字符:', responseText.substring(0, 100));
|
||
|
||
// 如果是位置66附近的错误,特别标记
|
||
if (parseError.message.includes('position 66')) {
|
||
console.error('位置66附近的字符:', responseText.substring(60, 75));
|
||
}
|
||
|
||
// 返回空数组作为默认值,避免页面功能完全中断
|
||
console.warn('使用默认空数组作为响应');
|
||
return [];
|
||
}
|
||
} catch (error) {
|
||
console.error('API请求错误:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 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()),
|
||
|
||
// 获取小时统计
|
||
getHourlyStats: () => apiRequest('/hourly-stats?t=' + Date.now()),
|
||
|
||
// 获取每日统计数据(7天)
|
||
getDailyStats: () => apiRequest('/daily-stats?t=' + Date.now()),
|
||
|
||
// 获取每月统计数据(30天)
|
||
getMonthlyStats: () => apiRequest('/monthly-stats?t=' + Date.now()),
|
||
|
||
// 获取屏蔽规则 - 已禁用
|
||
getShieldRules: () => {
|
||
console.log('屏蔽规则功能已禁用');
|
||
return Promise.resolve({}); // 返回空对象而非API调用
|
||
},
|
||
|
||
// 添加屏蔽规则 - 已禁用
|
||
addShieldRule: (rule) => {
|
||
console.log('屏蔽规则功能已禁用');
|
||
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
|
||
},
|
||
|
||
// 删除屏蔽规则 - 已禁用
|
||
deleteShieldRule: (rule) => {
|
||
console.log('屏蔽规则功能已禁用');
|
||
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
|
||
},
|
||
|
||
// 更新远程规则 - 已禁用
|
||
updateRemoteRules: () => {
|
||
console.log('屏蔽规则功能已禁用');
|
||
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
|
||
},
|
||
|
||
// 获取黑名单列表 - 已禁用
|
||
getBlacklists: () => {
|
||
console.log('屏蔽规则相关功能已禁用');
|
||
return Promise.resolve([]); // 返回空数组而非API调用
|
||
},
|
||
|
||
// 添加黑名单 - 已禁用
|
||
addBlacklist: (url) => {
|
||
console.log('屏蔽规则相关功能已禁用');
|
||
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
|
||
},
|
||
|
||
// 删除黑名单 - 已禁用
|
||
deleteBlacklist: (url) => {
|
||
console.log('屏蔽规则相关功能已禁用');
|
||
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
|
||
},
|
||
|
||
// 获取Hosts内容 - 已禁用
|
||
getHosts: () => {
|
||
console.log('屏蔽规则相关功能已禁用');
|
||
return Promise.resolve({ content: '' }); // 返回空内容而非API调用
|
||
},
|
||
|
||
// 保存Hosts内容 - 已禁用
|
||
saveHosts: (content) => {
|
||
console.log('屏蔽规则相关功能已禁用');
|
||
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
|
||
},
|
||
|
||
// 刷新Hosts - 已禁用
|
||
refreshHosts: () => {
|
||
console.log('屏蔽规则相关功能已禁用');
|
||
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
|
||
},
|
||
|
||
// 查询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 endpoints = ['/api/dns/query', '/dns/query', '/api/query', '/query'];
|
||
let lastError;
|
||
|
||
for (const endpoint of endpoints) {
|
||
try {
|
||
console.log(`尝试API端点: ${endpoint}`);
|
||
const response = await fetch(endpoint, {
|
||
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 {
|
||
lastError = new Error(`HTTP error! status: ${response.status} for endpoint: ${endpoint}`);
|
||
}
|
||
} catch (error) {
|
||
lastError = error;
|
||
console.log(`端点 ${endpoint} 调用失败,尝试下一个`);
|
||
}
|
||
}
|
||
|
||
// 如果所有端点都失败,抛出最后一个错误
|
||
throw lastError || new Error('所有API端点调用失败');
|
||
} 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; |