|
function isBrowserUA(request) { |
|
const userAgent = request.headers.get('User-Agent') || ''; |
|
const acceptHeader = request.headers.get('Accept') || ''; |
|
|
|
const prefersHtml = acceptHeader.includes('text/html'); |
|
|
|
const browserPatterns = [ |
|
'mozilla', |
|
'chrome', |
|
'safari', |
|
'firefox', |
|
'edge', |
|
'opera', |
|
'ucbrowser', |
|
'samsungbrowser', |
|
]; |
|
|
|
const nonBrowserPatterns = [ |
|
'curl', |
|
'wget', |
|
'httpie', |
|
'postman', |
|
'python-requests', |
|
'bot', |
|
'spider', |
|
'crawler', |
|
]; |
|
|
|
const isBrowserAgent = browserPatterns.some(pattern => userAgent.toLowerCase().includes(pattern)); |
|
|
|
const isNonBrowserAgent = nonBrowserPatterns.some(pattern => userAgent.toLowerCase().includes(pattern)); |
|
|
|
return prefersHtml || (isBrowserAgent && !isNonBrowserAgent); |
|
} |
|
|
|
async function process_json(request) { |
|
let data = { |
|
ip: request.headers.get('CF-Connecting-IP') || null, |
|
cf: { |
|
continent: request.cf?.continent || null, |
|
country: request.cf?.country || null, |
|
isEUCountry: request.cf?.isEUCountry || false, |
|
region: request.cf?.region || null, |
|
regionCode: request.cf?.regionCode || null, |
|
city: request.cf?.city || null, |
|
latitude: request.cf?.latitude || null, |
|
longitude: request.cf?.longitude || null, |
|
postalCode: request.cf?.postalCode || null, |
|
metroCode: request.cf?.metroCode || null, |
|
colo: request.cf?.colo || null, |
|
asn: request.cf?.asn || null, |
|
asOrganization: request.cf?.asOrganization || null, |
|
timezone: request.cf?.timezone || null, |
|
}, |
|
source: 'https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e', |
|
}; |
|
const dataJson = JSON.stringify(data, null, 4); |
|
return new Response(dataJson, { |
|
headers: { |
|
'Content-Type': 'application/json;charset=UTF-8', |
|
'x-script-info': 'https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e', |
|
}, |
|
}); |
|
} |
|
|
|
async function process_txt(request) { |
|
let data = { |
|
ip: request.headers.get('CF-Connecting-IP') || null, |
|
cf: { |
|
continent: request.cf?.continent || null, |
|
country: request.cf?.country || null, |
|
region: request.cf?.region || null, |
|
city: request.cf?.city || null, |
|
colo: request.cf?.colo || null, |
|
asn: request.cf?.asn || null, |
|
asOrganization: request.cf?.asOrganization || null, |
|
}, |
|
}; |
|
const text = `IP: ${data.ip || 'N/A'}\n` + |
|
`Continent: ${data.cf.continent || 'N/A'}\n` + |
|
`Country: ${data.cf.country || 'N/A'}\n` + |
|
`Region: ${data.cf.region || 'N/A'}\n` + |
|
`City: ${data.cf.city || 'N/A'}\n` + |
|
`Colo: ${data.cf.colo || 'N/A'}\n` + |
|
`ASN: ${data.cf.asn || 'N/A'}\n` + |
|
`ASOrganization: ${data.cf.asOrganization || 'N/A'}\n` + |
|
`Source: https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e\n`; |
|
return new Response(text, { |
|
headers: { |
|
'Content-Type': 'text/plain;charset=UTF-8', |
|
'x-script-info': 'https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e', |
|
}, |
|
}); |
|
} |
|
|
|
async function process_html(request) { |
|
let data = { |
|
ip: request.headers.get('CF-Connecting-IP') || null, |
|
cf: { |
|
continent: request.cf?.continent || null, |
|
country: request.cf?.country || null, |
|
region: request.cf?.region || null, |
|
city: request.cf?.city || null, |
|
colo: request.cf?.colo || null, |
|
asn: request.cf?.asn || null, |
|
asOrganization: request.cf?.asOrganization || null, |
|
}, |
|
source: 'https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e', |
|
}; |
|
|
|
const info = []; |
|
if (data.ip) info.push(`<strong>IP:</strong> ${data.ip}`); |
|
if (data.cf.continent) info.push(`<strong>大陆 | Continent:</strong> ${data.cf.continent}`); |
|
if (data.cf.country) info.push(`<strong>国家 | Country:</strong> ${data.cf.country}`); |
|
if (data.cf.region) info.push(`<strong>地区 | Region:</strong> ${data.cf.region}`); |
|
if (data.cf.city) info.push(`<strong>城市 | City:</strong> ${data.cf.city}`); |
|
if (data.cf.colo) info.push(`<strong>数据中心 | Colo:</strong> ${data.cf.colo}`); |
|
if (data.cf.asn) info.push(`<strong>ASN:</strong> ${data.cf.asn}`); |
|
if (data.cf.asOrganization) info.push(`<strong>AS 组织 | AS Organization:</strong> ${data.cf.asOrganization}`); |
|
const visitorInfo = info.length > 0 ? info.join('<br/><br/>') : '没有可用的访客信息 | No visitor information available.'; |
|
const html = ` |
|
<!DOCTYPE html> |
|
<html lang="zh-CN"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>IP 信息 | IP Information</title> |
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"> |
|
<style> |
|
:root { |
|
--bs-body-bg: #f8f9fa; |
|
--bs-body-color: #212529; |
|
} |
|
@media (prefers-color-scheme: dark) { |
|
:root { |
|
--bs-body-bg: #212529; |
|
--bs-body-color: #f8f9fa; |
|
} |
|
.card, .list-group-item { |
|
--bs-card-bg: #2c3034; |
|
--bs-card-color: #f8f9fa; |
|
} |
|
pre { |
|
--bs-code-bg: #343a40; |
|
} |
|
} |
|
body { |
|
background-color: var(--bs-body-bg); |
|
color: var(--bs-body-color); |
|
} |
|
.info-card .list-group-item { |
|
background-color: transparent; |
|
border-color: rgba(255,255,255,.125); |
|
} |
|
pre { |
|
background-color: var(--bs-code-bg, #e9ecef); |
|
border-radius: .375rem; |
|
} |
|
.source { |
|
font-size: .875rem; |
|
} |
|
</style> |
|
</head> |
|
<body class="py-4"> |
|
<div class="container"> |
|
<div class="row justify-content-center"> |
|
<div class="col-lg-8"> |
|
<h1 class="mb-4 text-center">您的 IP 信息 | Your IP Information</h1> |
|
|
|
<div class="card shadow-sm mb-4 info-card"> |
|
<div class="card-body"> |
|
<div class="list-group list-group-flush"> |
|
${info.map(item => `<div class="list-group-item px-0 py-3">${item}</div>`).join('') || '<div class="list-group-item px-0 py-3 text-muted">没有可用的访客信息 | No visitor information available.</div>'} |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="card shadow-sm"> |
|
<div class="card-header"> |
|
<h2 class="h5 mb-0">原始数据 | Raw Data</h2> |
|
</div> |
|
<div class="card-body p-0"> |
|
<pre class="m-0 p-3 overflow-auto"><code>${JSON.stringify(data, null, 2)}</code></pre> |
|
</div> |
|
</div> |
|
|
|
<p class="source text-center mt-4"> |
|
源代码 | Source: <a href="${data.source}" target="_blank" rel="noopener">${data.source}</a> |
|
</p> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
if (window.matchMedia('(prefers-color-scheme: dark)').matches) { |
|
document.documentElement.setAttribute('data-bs-theme', 'dark'); |
|
} else { |
|
document.documentElement.setAttribute('data-bs-theme', 'light'); |
|
} |
|
</script> |
|
</body> |
|
</html> |
|
`; |
|
return new Response(html, { |
|
headers: { |
|
'Content-Type': 'text/html;charset=UTF-8', |
|
'x-script-info': 'https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e', |
|
}, |
|
}); |
|
} |
|
|
|
async function process_root(request) { |
|
return isBrowserUA(request) ? process_html(request) : process_txt(request); |
|
} |
|
|
|
async function process_full(request, url) { |
|
let data = { |
|
method: request.method, |
|
url: request.url, |
|
origin: url.origin, |
|
ip: request.headers.get('CF-Connecting-IP') || null, |
|
cf: { |
|
continent: request.cf?.continent || null, |
|
country: request.cf?.country || null, |
|
isEUCountry: request.cf?.isEUCountry || false, |
|
region: request.cf?.region || null, |
|
regionCode: request.cf?.regionCode || null, |
|
city: request.cf?.city || null, |
|
latitude: request.cf?.latitude || null, |
|
longitude: request.cf?.longitude || null, |
|
postalCode: request.cf?.postalCode || null, |
|
metroCode: request.cf?.metroCode || null, |
|
colo: request.cf?.colo || null, |
|
asn: request.cf?.asn || null, |
|
asOrganization: request.cf?.asOrganization || null, |
|
timezone: request.cf?.timezone || null, |
|
}, |
|
headers: {}, |
|
security: {}, |
|
source: 'https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e', |
|
}; |
|
|
|
// Populate Headers |
|
request.headers.forEach((value, name) => { |
|
data.headers[name] = value; |
|
}); |
|
|
|
// Populate Security fields |
|
const securityKeys = [ |
|
'clientTcpRtt', |
|
'tlsCipher', |
|
'tlsVersion', |
|
'httpProtocol', |
|
'clientHandshake', |
|
'clientFinished', |
|
'serverHandshake', |
|
'serverFinished', |
|
'corporateProxy', |
|
'verifiedBot', |
|
'score', |
|
]; |
|
if (request.cf) { |
|
for (const key of securityKeys) { |
|
if (request.cf[key]) { |
|
if (typeof request.cf[key] === 'object') { |
|
for (const innerKey in request.cf[key]) { |
|
data.security[innerKey] = request.cf[key][innerKey]; |
|
} |
|
} else { |
|
data.security[key] = request.cf[key]; |
|
} |
|
} |
|
} |
|
} |
|
|
|
const dataJson = JSON.stringify(data, null, 4); |
|
return new Response(dataJson, { |
|
headers: { |
|
'Content-Type': 'application/json;charset=UTF-8', |
|
'x-script-info': 'https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e', |
|
}, |
|
}); |
|
} |
|
|
|
export default { |
|
async fetch(request) { |
|
const url = new URL(request.url); |
|
|
|
switch (url.pathname) { |
|
case '/ip': |
|
case '/.ip': |
|
return new Response(request.headers.get('CF-Connecting-IP') || 'N/A', { |
|
headers: { |
|
'Content-Type': 'text/plain;charset=UTF-8', |
|
'x-script-info': 'https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e', |
|
}, |
|
}); |
|
case '/json': |
|
case '/.json': |
|
return await process_json(request); |
|
case '/': |
|
return await process_root(request); |
|
case '/txt': |
|
case '/.txt': |
|
return await process_txt(request); |
|
case '/html': |
|
case '/.html': |
|
return await process_html(request); |
|
case '/full': |
|
case '/.full': |
|
return await process_full(request, url); |
|
default: |
|
return new Response( |
|
JSON.stringify( |
|
{ |
|
usage: { |
|
'/ip': '返回纯 IP | Return only IP', |
|
'/json': '返回 JSON 数据 (仅 IP 和位置信息) | Return JSON data (IP and location only)', |
|
'/full': '返回完整数据 (包括头部和安全信息) | Return FULL data (includes headers & security)', |
|
'/txt': '返回纯文本数据 (IP 和位置信息) | Return plain text data (IP and location)', |
|
'/html': '返回 HTML 页面 (IP 和位置信息) | Return HTML with IP and location data', |
|
}, |
|
source: 'https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e', |
|
}, |
|
null, |
|
4 |
|
), |
|
{ |
|
status: 404, |
|
headers: { |
|
'Content-Type': 'application/json;charset=UTF-8', |
|
'x-script-info': 'https://gist.github.com/wyf9/beeb6746fa7fda73f78380cbdda5461e', |
|
}, |
|
} |
|
); |
|
} |
|
}, |
|
}; |