feat: enhance input handling for network tools

- Added domain and URL normalization on blur for DNS, Ping, Speedtest, and TCPing components.
- Improved user experience by ensuring valid input formats and extracting ports where applicable.
This commit is contained in:
typist
2025-10-29 06:08:47 +08:00
parent be56d896ca
commit 972b6c7f22
4 changed files with 112 additions and 19 deletions

View File

@@ -41,6 +41,26 @@ const Tool: FC = () => {
const [results, setResults] = useState<DNSRecord[]>([]);
const [queryTime, setQueryTime] = useState<number>(0);
const handleDomainBlur = () => {
if (!domain.trim()) return;
let input = domain.trim();
let cleanDomain = input;
try {
// Try to parse as URL
const url = new URL(input.startsWith('http') ? input : `https://${input}`);
cleanDomain = url.hostname;
} catch {
// If parsing fails, fallback to manual cleanup
cleanDomain = input.replace(/^https?:\/\//, "").split("/")[0].split(":")[0];
}
if (cleanDomain !== input) {
setDomain(cleanDomain);
}
};
const queryDNS = async () => {
if (!domain.trim()) {
toast.error("Please enter a domain name");
@@ -58,7 +78,7 @@ const Tool: FC = () => {
const queries = DNS_RECORD_TYPES.map((recordType) =>
fetch(
`https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(
domain
domain.trim()
)}&type=${recordType.value}`,
{
headers: {
@@ -127,6 +147,7 @@ const Tool: FC = () => {
placeholder="e.g. example.com"
value={domain}
onChange={(e) => setDomain(e.target.value)}
onBlur={handleDomainBlur}
onKeyPress={handleKeyPress}
disabled={loading}
/>

View File

@@ -36,6 +36,27 @@ const Tool: FC = () => {
const seqRef = useRef<number>(0);
const resultsContainerRef = useRef<HTMLDivElement>(null);
const handleUrlBlur = () => {
if (!url.trim()) return;
let input = url.trim();
try {
// Try to parse as URL
const parsedUrl = new URL(input.startsWith('http') ? input : `https://${input}`);
const normalizedUrl = parsedUrl.toString();
if (normalizedUrl !== input) {
setUrl(normalizedUrl);
}
} catch {
// If parsing fails, add https:// prefix
if (!input.startsWith("http://") && !input.startsWith("https://")) {
setUrl(`https://${input}`);
}
}
};
const ping = async () => {
if (!url.trim()) {
toast.error("Please enter a URL");
@@ -43,12 +64,7 @@ const Tool: FC = () => {
}
const seq = ++seqRef.current;
let targetUrl = url.trim();
// If no protocol prefix, default to https://
if (!targetUrl.startsWith("http://") && !targetUrl.startsWith("https://")) {
targetUrl = `https://${targetUrl}`;
}
const targetUrl = url.trim();
const startTime = performance.now();
@@ -177,6 +193,7 @@ const Tool: FC = () => {
placeholder="e.g. example.com or https://example.com"
value={url}
onChange={(e) => setUrl(e.target.value)}
onBlur={handleUrlBlur}
disabled={running}
/>
</div>

View File

@@ -142,18 +142,34 @@ const Tool: FC = () => {
}
};
const handleUrlBlur = () => {
if (!url.trim()) return;
let input = url.trim();
try {
// Try to parse as URL
const parsedUrl = new URL(input.startsWith('http') ? input : `https://${input}`);
const normalizedUrl = parsedUrl.toString();
if (normalizedUrl !== input) {
setUrl(normalizedUrl);
}
} catch {
// If parsing fails, add https:// prefix
if (!input.startsWith("http://") && !input.startsWith("https://")) {
setUrl(`https://${input}`);
}
}
};
const startTest = async () => {
if (!url.trim()) {
toast.error("Please enter a URL");
return;
}
let targetUrl = url.trim();
// If no protocol prefix, default to https://
if (!targetUrl.startsWith("http://") && !targetUrl.startsWith("https://")) {
targetUrl = `https://${targetUrl}`;
}
const targetUrl = url.trim();
setTesting(true);
setResult(null);
@@ -218,6 +234,7 @@ const Tool: FC = () => {
placeholder="e.g. https://example.com"
value={url}
onChange={(e) => setUrl(e.target.value)}
onBlur={handleUrlBlur}
onKeyPress={handleKeyPress}
disabled={testing}
/>

View File

@@ -37,6 +37,46 @@ const Tool: FC = () => {
const seqRef = useRef<number>(0);
const resultsContainerRef = useRef<HTMLDivElement>(null);
const handleHostBlur = () => {
if (!host.trim()) return;
let input = host.trim();
let cleanHost = input;
let extractedPort: string | null = null;
try {
// Try to parse as URL
const url = new URL(input.startsWith('http') ? input : `https://${input}`);
cleanHost = url.hostname;
// Extract port if specified in URL
if (url.port) {
extractedPort = url.port;
}
} catch {
// If parsing fails, fallback to manual cleanup
const withoutProtocol = input.replace(/^https?:\/\//, "");
const withoutPath = withoutProtocol.split("/")[0];
// Check for port in the format hostname:port
const portMatch = withoutPath.match(/^(.+):(\d+)$/);
if (portMatch) {
cleanHost = portMatch[1];
extractedPort = portMatch[2];
} else {
cleanHost = withoutPath;
}
}
if (cleanHost !== input) {
setHost(cleanHost);
}
if (extractedPort) {
setPort(extractedPort);
}
};
const tcping = async () => {
if (!host.trim()) {
toast.error("Please enter a hostname or IP");
@@ -45,14 +85,11 @@ const Tool: FC = () => {
const seq = ++seqRef.current;
const portNum = parseInt(port) || 443;
let targetUrl = host.trim();
const targetHost = host.trim();
// 移除协议前缀
targetUrl = targetUrl.replace(/^https?:\/\//, "");
// 构建测试 URL
// Build test URL
const protocol = portNum === 443 ? "https" : "http";
const url = `${protocol}://${targetUrl}:${portNum}`;
const url = `${protocol}://${targetHost}:${portNum}`;
const startTime = performance.now();
@@ -182,6 +219,7 @@ const Tool: FC = () => {
placeholder="e.g. example.com or 192.168.1.1"
value={host}
onChange={(e) => setHost(e.target.value)}
onBlur={handleHostBlur}
disabled={running}
/>
</div>