2026-09-14 17:51:40 +08:00
|
|
|
|
// URL 匹配 + HTML 转义,浏览器与 Node 通用
|
2026-09-14 13:45:17 +08:00
|
|
|
|
(function (root, factory) {
|
|
|
|
|
|
if (typeof module === "object" && module.exports) {
|
|
|
|
|
|
module.exports = factory();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
const api = factory();
|
|
|
|
|
|
root.isDomainMatch = api.isDomainMatch;
|
|
|
|
|
|
root.escapeHtml = api.escapeHtml;
|
|
|
|
|
|
}
|
|
|
|
|
|
})(typeof globalThis !== "undefined" ? globalThis : this, function () {
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-09-14 17:51:40 +08:00
|
|
|
|
* 判断 URL 是否匹配规则。
|
|
|
|
|
|
* 纯 hostname:example.com / *.example.com
|
|
|
|
|
|
* hostname+path:example.com/login/*,路径部分支持 * 通配
|
2026-09-14 13:45:17 +08:00
|
|
|
|
*/
|
|
|
|
|
|
function isDomainMatch(url, pattern) {
|
|
|
|
|
|
if (!url || !pattern) return false;
|
|
|
|
|
|
try {
|
|
|
|
|
|
const u = new URL(url);
|
|
|
|
|
|
const hostname = u.hostname;
|
|
|
|
|
|
const rawPattern = String(pattern).trim();
|
|
|
|
|
|
|
|
|
|
|
|
if (rawPattern.includes("/")) {
|
|
|
|
|
|
const slashIdx = rawPattern.indexOf("/");
|
|
|
|
|
|
const hostPart = rawPattern.slice(0, slashIdx);
|
|
|
|
|
|
const pathPart = rawPattern.slice(slashIdx + 1) || "";
|
|
|
|
|
|
if (!matchHost(hostname, hostPart)) return false;
|
|
|
|
|
|
return matchPath(u.pathname, pathPart);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return matchHost(hostname, rawPattern);
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function matchHost(hostname, hostPart) {
|
|
|
|
|
|
if (hostPart.startsWith("*.")) {
|
|
|
|
|
|
const suffix = hostPart.slice(2);
|
|
|
|
|
|
return hostname === suffix || hostname.endsWith("." + suffix);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (hostPart.includes("*")) {
|
|
|
|
|
|
const regexStr = hostPart.replace(/\./g, "\\.").replace(/\*/g, ".*");
|
|
|
|
|
|
return new RegExp("^" + regexStr + "$").test(hostname);
|
|
|
|
|
|
}
|
|
|
|
|
|
return hostname === hostPart;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function matchPath(pathname, pathPart) {
|
|
|
|
|
|
const pagePath = pathname.startsWith("/") ? pathname.slice(1) : pathname;
|
|
|
|
|
|
if (!pathPart) return true;
|
|
|
|
|
|
if (pathPart === "*") return true;
|
|
|
|
|
|
if (pathPart.endsWith("/*")) {
|
|
|
|
|
|
const prefix = pathPart.slice(0, -2);
|
|
|
|
|
|
return pagePath === prefix || pagePath.startsWith(prefix + "/");
|
|
|
|
|
|
}
|
|
|
|
|
|
if (pathPart.includes("*")) {
|
|
|
|
|
|
const regexStr = pathPart.replace(/\//g, "\\/").replace(/\*/g, ".*");
|
|
|
|
|
|
return new RegExp("^" + regexStr + "$").test(pagePath);
|
|
|
|
|
|
}
|
|
|
|
|
|
return pagePath === pathPart;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-14 17:51:40 +08:00
|
|
|
|
/** HTML 转义,防止配置中的特殊字符破坏页面 */
|
2026-09-14 13:45:17 +08:00
|
|
|
|
function escapeHtml(str) {
|
|
|
|
|
|
if (str == null) return "";
|
|
|
|
|
|
return String(str)
|
|
|
|
|
|
.replace(/&/g, "&")
|
|
|
|
|
|
.replace(/</g, "<")
|
|
|
|
|
|
.replace(/>/g, ">")
|
|
|
|
|
|
.replace(/"/g, """)
|
|
|
|
|
|
.replace(/'/g, "'");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return { isDomainMatch, escapeHtml };
|
|
|
|
|
|
});
|