fix: better detect for IP addresses, add test
This commit is contained in:
parent
2265344952
commit
8d39e2833c
@ -13,8 +13,9 @@
|
|||||||
},
|
},
|
||||||
"search-help-text": "Search {engine}"
|
"search-help-text": "Search {engine}"
|
||||||
},
|
},
|
||||||
"404": {
|
"notfound": {
|
||||||
"title": "Page Not Found"
|
"title": "page not found",
|
||||||
|
"desc": "Please check if there is a typo in the URL. <br/>If SparkHome brought you to this page,<br/> please <a style=\"text-decoration:underline;\" href=\"mailto:contact@alikia2x.com\">contact us.</a>"
|
||||||
},
|
},
|
||||||
"about": {
|
"about": {
|
||||||
"title": "SparkHome"
|
"title": "SparkHome"
|
||||||
|
@ -1,22 +1,26 @@
|
|||||||
import punycode from "punycode/";
|
import punycode from "punycode";
|
||||||
import { tldList } from "./tldList";
|
import { tldList } from "./tldList";
|
||||||
|
|
||||||
export default function validLink(link: string) {
|
export default function validLink(link: string) {
|
||||||
let finalURL = '';
|
let finalURL;
|
||||||
try {
|
try {
|
||||||
const url = new URL(link);
|
const url = new URL(link);
|
||||||
finalURL = url.origin;
|
finalURL = url;
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// if the URL is invalid, try to add the protocol
|
// if the URL is invalid, try to add the protocol
|
||||||
try {
|
try {
|
||||||
const urlWithHTTP = new URL("http://" + link);
|
const urlWithHTTP = new URL("http://" + link);
|
||||||
finalURL = urlWithHTTP.origin;
|
finalURL = urlWithHTTP;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (validTLD(finalURL)) {
|
if (
|
||||||
|
validTLD(finalURL.host) ||
|
||||||
|
isValidIPv6(finalURL.host.slice(1, finalURL.host.length - 1)) ||
|
||||||
|
isValidIPv4(finalURL.host)
|
||||||
|
) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@ -31,3 +35,54 @@ export function validTLD(domain: string): boolean {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isValidIPv6(ip: string): boolean {
|
||||||
|
const length = ip.length;
|
||||||
|
let groups = 1;
|
||||||
|
let groupDigits = 0;
|
||||||
|
let doubleColonCount = 0;
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const char = ip[i];
|
||||||
|
if ("0" <= char && char <= "9") {
|
||||||
|
groupDigits++;
|
||||||
|
} else if ("a" <= char && char <= "f") {
|
||||||
|
groupDigits++;
|
||||||
|
} else if ("A" <= char && char <= "F") {
|
||||||
|
groupDigits++;
|
||||||
|
} else if (char === ":" && i + 1 < length && ip[i + 1] !== ":") {
|
||||||
|
groups++;
|
||||||
|
groupDigits = 0;
|
||||||
|
} else if (char === ":" && i + 1 < length && ip[i + 1] === ":") {
|
||||||
|
doubleColonCount++;
|
||||||
|
i++;
|
||||||
|
groupDigits = 0;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (groups > 8) {
|
||||||
|
return false;
|
||||||
|
} else if (groupDigits > 4) {
|
||||||
|
return false;
|
||||||
|
} else if (doubleColonCount > 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (doubleColonCount === 0 && groups !== 8) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isValidIPv4(ip: string): boolean {
|
||||||
|
const parts = ip.split(".");
|
||||||
|
if (parts.length !== 4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (const part of parts) {
|
||||||
|
const num = Number(part);
|
||||||
|
if (isNaN(num) || num < 0 || num > 255 || !part.match(/^\d+$/)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "sparkhome",
|
"name": "sparkhome",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "5.2.1",
|
"version": "5.2.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bun server.ts",
|
"dev": "bun server.ts",
|
||||||
@ -21,6 +21,7 @@
|
|||||||
"i18next": "^23.11.5",
|
"i18next": "^23.11.5",
|
||||||
"i18next-browser-languagedetector": "^8.0.0",
|
"i18next-browser-languagedetector": "^8.0.0",
|
||||||
"i18next-icu": "^2.3.0",
|
"i18next-icu": "^2.3.0",
|
||||||
|
"jest": "^29.7.0",
|
||||||
"jotai": "^2.8.3",
|
"jotai": "^2.8.3",
|
||||||
"node-nlp": "^4.27.0",
|
"node-nlp": "^4.27.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
|
17
pages/[...].tsx
Normal file
17
pages/[...].tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
export default function NotFound() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<div className="relative w-screen h-screen flex justify-center items-center">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<h1 className="text-7xl font-thin">404</h1>
|
||||||
|
<div className="relative h-20 mx-4 w-[0.15rem] bg-black dark:bg-white"></div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<div className="uppercase text-3xl font-light">{t("notfound.title")}</div>
|
||||||
|
<div className="text-sm" dangerouslySetInnerHTML={{__html:t("notfound.desc")}}></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
70
test/validLink.test.ts
Normal file
70
test/validLink.test.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import { describe, expect, test } from "@jest/globals";
|
||||||
|
import validLink, { validTLD } from "../lib/url/validLink";
|
||||||
|
|
||||||
|
describe("Check if a string is an accessible domain/URL/IP", () => {
|
||||||
|
test("Plain, full URL", () => {
|
||||||
|
// Plain form
|
||||||
|
expect(validLink("http://example.com")).toBe(true);
|
||||||
|
// With https and path
|
||||||
|
expect(validLink("https://jestjs.io/docs/getting-started/")).toBe(true);
|
||||||
|
// With anchor
|
||||||
|
expect(validLink("https://difftastic.wilfred.me.uk/zh-CN/git.html#git-difftool")).toBe(true);
|
||||||
|
// With params
|
||||||
|
expect(validLink("https://www.bilibili.com/list/ml2252204359?oid=990610203&bvid=BV1sx4y1g7Hh")).toBe(true);
|
||||||
|
});
|
||||||
|
test("Punycode URL", () => {
|
||||||
|
expect(validLink("https://原神大学.com/")).toBe(true);
|
||||||
|
expect(validLink("中国原神大学.com")).toBe(true);
|
||||||
|
});
|
||||||
|
test("Invalid TLD with protocol", () => {
|
||||||
|
expect(validLink("https://www.example.notexist")).toBe(true);
|
||||||
|
});
|
||||||
|
test("Invalid TLD with no protocol", () => {
|
||||||
|
expect(validLink("www.example.notexist")).toBe(false);
|
||||||
|
});
|
||||||
|
test("IPv4 without protocol", () => {
|
||||||
|
expect(validLink("127.0.0.1")).toBe(true);
|
||||||
|
});
|
||||||
|
test("IPv6 without protocol", () => {
|
||||||
|
expect(validLink("[::]")).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reference: https://www.iana.org/domains/root/db
|
||||||
|
describe("Check if the given TLD exist and assigned.", () => {
|
||||||
|
test("Valid normal TLD", () => {
|
||||||
|
expect(validTLD("com")).toBe(true);
|
||||||
|
expect(validTLD("top")).toBe(true);
|
||||||
|
expect(validTLD("net")).toBe(true);
|
||||||
|
expect(validTLD("org")).toBe(true);
|
||||||
|
});
|
||||||
|
test("Valid new TLDs", () => {
|
||||||
|
// they really exist!
|
||||||
|
expect(validTLD("foo")).toBe(true);
|
||||||
|
expect(validTLD("bar")).toBe(true);
|
||||||
|
});
|
||||||
|
test("Exist but not assigned TLD", () => {
|
||||||
|
expect(validTLD("active")).toBe(false);
|
||||||
|
expect(validTLD("off")).toBe(false);
|
||||||
|
});
|
||||||
|
test("with dot", () => {
|
||||||
|
expect(validTLD(".com")).toBe(true);
|
||||||
|
expect(validTLD(".us")).toBe(true);
|
||||||
|
expect(validTLD(".cn")).toBe(true);
|
||||||
|
expect(validTLD(".io")).toBe(true);
|
||||||
|
});
|
||||||
|
test("Punycode TLDs", () => {
|
||||||
|
expect(validTLD(".中国")).toBe(true);
|
||||||
|
expect(validTLD(".РФ")).toBe(true);
|
||||||
|
expect(validTLD(".कॉम")).toBe(true);
|
||||||
|
expect(validTLD("ایران")).toBe(true);
|
||||||
|
expect(validTLD("இலங்கை")).toBe(true);
|
||||||
|
expect(validTLD("გე")).toBe(true);
|
||||||
|
expect(validTLD("ポイント")).toBe(true);
|
||||||
|
});
|
||||||
|
test("Punycode TLDs but not assigned", () => {
|
||||||
|
expect(validTLD("テスト")).toBe(false);
|
||||||
|
expect(validTLD("परीक्षा")).toBe(false);
|
||||||
|
expect(validTLD("测试")).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user