import { useState, type FC } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { toast } from "sonner"; import { Loader2 } from "lucide-react"; interface IPInfo { ip: string; city?: string; region?: string; country?: string; countryCode?: string; loc?: string; org?: string; timezone?: string; isp?: string; as?: string; proxy?: boolean; hosting?: boolean; query?: string; } const Tool: FC = () => { const [ip, setIp] = useState(""); const [loading, setLoading] = useState(false); const [ipInfo, setIpInfo] = useState(null); const [queryTime, setQueryTime] = useState(0); const isValidIP = (ip: string): boolean => { // IPv4 正则 const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/; // IPv6 正则 (简化版) const ipv6Regex = /^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$/; if (ipv4Regex.test(ip)) { const parts = ip.split('.'); return parts.every(part => parseInt(part) >= 0 && parseInt(part) <= 255); } return ipv6Regex.test(ip); }; const queryCurrentIP = async () => { setLoading(true); setIpInfo(null); setQueryTime(0); const startTime = performance.now(); try { // 使用 ipinfo.io 查询当前IP (免费,无需密钥) const response = await fetch("https://ipinfo.io/json"); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); const endTime = performance.now(); setQueryTime(endTime - startTime); setIpInfo(data); setIp(data.ip); toast.success("Successfully queried current IP"); } catch (error) { if (error instanceof Error) { toast.error(`Query failed: ${error.message}`); } else { toast.error("Query failed"); } } finally { setLoading(false); } }; const queryIP = async () => { if (!ip.trim()) { toast.error("Please enter an IP address"); return; } if (!isValidIP(ip.trim())) { toast.error("Invalid IP address format"); return; } setLoading(true); setIpInfo(null); setQueryTime(0); const startTime = performance.now(); try { // 使用 ip-api.com (免费,功能较全) const response = await fetch(`http://ip-api.com/json/${encodeURIComponent(ip.trim())}?fields=status,message,country,countryCode,region,city,lat,lon,timezone,isp,org,as,proxy,hosting,query`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); const endTime = performance.now(); setQueryTime(endTime - startTime); if (data.status === "fail") { toast.error(data.message || "Query failed"); return; } // 转换为统一格式 const ipData: IPInfo = { ip: data.query, city: data.city, region: data.region, country: data.country, countryCode: data.countryCode, loc: data.lat && data.lon ? `${data.lat},${data.lon}` : undefined, timezone: data.timezone, isp: data.isp, org: data.org, as: data.as, proxy: data.proxy, hosting: data.hosting, }; setIpInfo(ipData); toast.success("Query successful"); } catch (error) { if (error instanceof Error) { toast.error(`Query failed: ${error.message}`); } else { toast.error("Query failed"); } } finally { setLoading(false); } }; const handleKeyPress = (e: React.KeyboardEvent) => { if (e.key === "Enter" && !loading) { queryIP(); } }; const getRiskLevel = () => { if (!ipInfo) return null; if (ipInfo.proxy || ipInfo.hosting) { return { level: "High", color: "text-red-500", reasons: [ ipInfo.proxy && "Proxy/VPN detected", ipInfo.hosting && "Hosting/Datacenter IP", ].filter(Boolean), }; } return { level: "Low", color: "text-green-500", reasons: ["Regular residential IP"], }; }; const riskInfo = getRiskLevel(); return (
setIp(e.target.value)} onKeyPress={handleKeyPress} disabled={loading} /> Supports IPv4 and IPv6 addresses
{queryTime > 0 && (
Query time: {queryTime.toFixed(2)} ms
)} {ipInfo && (
IP Information:
{/* 基本信息 */}
Basic Information
IP Address:
{ipInfo.ip || ipInfo.query}
{ipInfo.country && ( <>
Country:
{ipInfo.country} ({ipInfo.countryCode})
)} {ipInfo.region && ( <>
Region:
{ipInfo.region}
)} {ipInfo.city && ( <>
City:
{ipInfo.city}
)} {ipInfo.loc && ( <>
Coordinates:
{ipInfo.loc}
)} {ipInfo.timezone && ( <>
Timezone:
{ipInfo.timezone}
)}
{/* 网络信息 */} {(ipInfo.isp || ipInfo.org || ipInfo.as) && (
Network Information
{ipInfo.isp && ( <>
ISP:
{ipInfo.isp}
)} {ipInfo.org && ( <>
Organization:
{ipInfo.org}
)} {ipInfo.as && ( <>
AS Number:
{ipInfo.as}
)}
)} {/* 风险评估 */} {riskInfo && (
Risk Assessment
Risk Level:
{riskInfo.level}
Details:
{riskInfo.reasons.map((reason, idx) => (
{reason}
))}
)}
)}
); }; export default Tool;