Last active
May 31, 2024 19:16
-
-
Save uubulb/7d57984ac4a9467f9d5f9dd8058aec40 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const backendUrl = '替换成后端地址'; | |
const apiToken = '替换成获取到的ssr token'; | |
const authToken = '替换成要设置的哪吒兼容api token(随意填写)'; | |
const ipv4Regex = /((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))/; | |
const ipv6Regex = /^(?:(?:[a-fA-F\d]{1,4}:){7}(?:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,2}|:)|(?:[a-fA-F\d]{1,4}:){4}(?:(?::[a-fA-F\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,3}|:)|(?:[a-fA-F\d]{1,4}:){3}(?:(?::[a-fA-F\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,4}|:)|(?:[a-fA-F\d]{1,4}:){2}(?:(?::[a-fA-F\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,5}|:)|(?:[a-fA-F\d]{1,4}:){1}(?:(?::[a-fA-F\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,6}|:)|(?::(?:(?::[a-fA-F\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,7}|:)))(?:%[0-9a-zA-Z]{1,})?$/gm; | |
export default { | |
async fetch(request) { | |
const validateAuthorization = (request) => { | |
const authHeader = request.headers.get('Authorization'); | |
const validToken = authToken; | |
if (!authHeader || authHeader !== `${validToken}`) { | |
return false; | |
} | |
return true; | |
}; | |
try { | |
const upgradeHeader = request.headers.get('Upgrade'); | |
const url = new URL(request.url); | |
switch (url.pathname) { | |
case '/': | |
return new Response(JSON.stringify(request.cf), { status: 200 }); | |
case '/api/v1/server/list': { | |
if (!validateAuthorization(request)) { | |
return new Response(JSON.stringify({ | |
code: 403, | |
message: '访问此接口需要认证' | |
}), { | |
status: 403, | |
headers: { 'Content-Type': 'application/json' } | |
}); | |
} | |
const params = new URLSearchParams(url.search); | |
const tag = params.get('tag'); | |
const convertedData = await convertList(); | |
let responseData; | |
if (tag) { | |
const filteredData = convertedData.result.find(server => server.tag === tag); | |
if (filteredData) { | |
responseData = { | |
code: 0, | |
message: "success", | |
result: [filteredData] | |
}; | |
} else { | |
responseData = { | |
code: 0, | |
message: "success", | |
result: [] | |
} | |
} | |
return new Response(JSON.stringify(responseData || { error: 'Not found' }), { | |
status: filteredData ? 200 : 404, | |
headers: { 'Content-Type': 'application/json' } | |
}); | |
} else { | |
return new Response(JSON.stringify(convertedData), { | |
status: 200, | |
headers: { 'Content-Type': 'application/json' } | |
}); | |
} | |
} | |
case '/api/v1/server/details': { | |
if (!validateAuthorization(request)) { | |
return new Response(JSON.stringify({ | |
code: 403, | |
message: '访问此接口需要认证' | |
}), { | |
status: 403, | |
headers: { 'Content-Type': 'application/json' } | |
}); | |
} | |
const params = new URLSearchParams(url.search); | |
const id = params.get('id'); | |
const tag = params.get('tag'); | |
const convertedData = await convertDetail(); | |
let responseData; | |
if (id) { | |
const filteredData = convertedData.result.find(server => server.id.toString() === id); | |
if (filteredData) { | |
responseData = { | |
code: 0, | |
message: "success", | |
result: [filteredData] | |
}; | |
} else { | |
responseData = { | |
code: 0, | |
message: "success", | |
result: [] | |
} | |
} | |
return new Response(JSON.stringify(responseData || { error: 'Not found' }), { | |
status: filteredData ? 200 : 404, | |
headers: { 'Content-Type': 'application/json' } | |
}); | |
} else if (tag) { | |
const filteredData = convertedData.result.find(server => server.tag === tag); | |
if (filteredData) { | |
responseData = { | |
code: 0, | |
message: "success", | |
result: [filteredData] | |
}; | |
} else { | |
responseData = { | |
code: 0, | |
message: "success", | |
result: [] | |
} | |
} | |
return new Response(JSON.stringify(responseData || { error: 'Not found' }), { | |
status: filteredData ? 200 : 404, | |
headers: { 'Content-Type': 'application/json' } | |
}); | |
} else { | |
return new Response(JSON.stringify(convertedData), { | |
status: 200, | |
headers: { 'Content-Type': 'application/json' } | |
}); | |
} | |
} | |
default: | |
return new Response('Not found', { status: 404 }); | |
} | |
} catch (err) { | |
/** @type {Error} */ let e = err; | |
return new Response(e.toString()); | |
} | |
}, | |
}; | |
async function fetchJSON() { | |
const apiUrl = `${backendUrl}/api/admin/stats.json` | |
const response = await fetch(apiUrl, { | |
method: 'GET', | |
headers: { | |
'Authorization': `Bearer ${apiToken}` | |
} | |
}); | |
if (!response.ok) { | |
throw new Error(`Error fetching data: ${response.status}`); | |
} | |
const data = await response.json(); | |
return data; | |
} | |
async function geoIP(ip) { | |
const api = `http://ip-api.com/json/${ip}` | |
const response = await fetch(api, { | |
method: 'GET' | |
}); | |
if (!response.ok) { | |
throw new Error(`Error fetching data: ${response.status}`); | |
} | |
const data = await response.json(); | |
return data; | |
} | |
async function convertList() { | |
const ssrApi = await fetchJSON(); | |
const convertedApi = { | |
code: 0, | |
message: "success", | |
result: [] | |
} | |
let idCounter = 1 | |
for (const server of ssrApi.servers) { | |
let serverResult = { | |
id: idCounter++, | |
name: `${server.alias}`, | |
tag: `${server.location}`, | |
last_active: server.latest_ts, | |
valid_ip: server.ip_info.query | |
} | |
if (ipv4Regex.test(server.ip_info.query)) { | |
serverResult.ipv4 = server.ip_info.query; | |
serverResult.ipv6 = ""; | |
} else if (ipv6Regex.test(server.ip_info.query)) { | |
serverResult.ipv4 = ""; | |
serverResult.ipv6 = server.ip_info.query; | |
} else { | |
serverResult.ipv4 = ""; | |
serverResult.ipv6 = ""; | |
} | |
convertedApi.result.push(serverResult); | |
} | |
return convertedApi; | |
} | |
async function convertDetail() { | |
const ssrApi = await fetchJSON(); | |
const convertedApi = { | |
code: 0, | |
message: "success", | |
result: [] | |
} | |
let idCounter = 1 | |
const osRegex = /os=([^;]+)/; | |
const versionRegex = /(\d+(\.\d+)?)/gm; | |
for (const server of ssrApi.servers) { | |
let os_name = server.labels.match(osRegex); | |
let os_version = server.sys_info.os_release.match(versionRegex) | |
let uptime = server.uptime.match(/\d+/) * 86400; | |
let serverResult = { | |
id: idCounter++, | |
name: `${server.alias}`, | |
tag: `${server.location}`, | |
last_active: server.latest_ts, | |
valid_ip: server.ip_info.query, | |
host: { | |
Platform: os_name[1], | |
PlatformVersion: os_version[0], | |
CPU: [`${server.sys_info.cpu_brand} ${server.sys_info.cpu_num} Core`], | |
MemTotal: server.memory_total * 1024, | |
DiskTotal: server.hdd_total * 1024 * 1024, | |
SwapTotal: server.swap_total, | |
Arch: server.sys_info.os_arch, | |
Virtualization: `${server.type}`, | |
BootTime: server.latest_ts - uptime, | |
Version: `${server.sys_info.version}-ssr` | |
}, | |
status: { | |
CPU: server.cpu, | |
MemUsed: server.memory_used * 1024, | |
SwapUsed: server.swap_used * 1024, | |
DiskUsed: server.hdd_used * 1024 * 1024, | |
NetInTransfer: server.network_in, | |
NetOutTransfer: server.network_out, | |
NetInSpeed: server.network_rx, | |
NetOutSpeed: server.network_tx, | |
Uptime: uptime, | |
Load1: server.load_1, | |
Load5: server.load_5, | |
Load15: server.load_15, | |
TcpConnCount: server.tcp_count, | |
UdpConnCount: server.udp_count, | |
ProcessCount: server.process_count | |
} | |
}; | |
try { | |
let geoIPResult = await geoIP(server.ip_info.query); | |
serverResult.host.CountryCode = geoIPResult.countryCode.toLowerCase(); | |
} catch (err) { | |
console.error(`Error fetching geoIP for server: ${server.name}`, err); | |
serverResult.host.CountryCode = ''; | |
} | |
if (ipv4Regex.test(server.ip_info.query)) { | |
serverResult.ipv4 = server.ip_info.query; | |
serverResult.ipv6 = ""; | |
} else if (ipv6Regex.test(server.ip_info.query)) { | |
serverResult.ipv4 = ""; | |
serverResult.ipv6 = server.ip_info.query; | |
} else { | |
serverResult.ipv4 = ""; | |
serverResult.ipv6 = server.ip_info.query; | |
} | |
convertedApi.result.push(serverResult); | |
} | |
return convertedApi; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment