Skip to content

Instantly share code, notes, and snippets.

@uubulb
Last active May 31, 2024 19:16
Show Gist options
  • Save uubulb/7d57984ac4a9467f9d5f9dd8058aec40 to your computer and use it in GitHub Desktop.
Save uubulb/7d57984ac4a9467f9d5f9dd8058aec40 to your computer and use it in GitHub Desktop.
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