直接上代码:
{% unless request.design_mode %}
<style id="country-access-gate-style">
/*
* 国家检测完成前隐藏整个页面,
* 避免受限制用户短暂看到网站内容。
*/
html {
visibility: hidden !important;
}
</style>
<script>
(function () {
'use strict';
/*
* 禁止访问的国家代码。
*
* 德国:DE
* 法国:FR
* 奥地利:AT
* 美国:US
* 英国:GB
*
* 多个国家示例:
* ['DE', 'FR', 'AT']
*/
var BLOCKED_COUNTRIES = ['DE'];
/*
* 命中国家后跳转的页面。
*
* Shopify 内部页面:
* /pages/access-restricted
*
* 也可以填写外部完整地址:
* https://example.com/access-restricted
*/
var REDIRECT_URL = '/pages/access-restricted';
/*
* IP 国家查询接口。
*/
var LOOKUP_URL = 'https://ipapi.co/country_code/';
/*
* 查询超时时间:5 秒。
*/
var REQUEST_TIMEOUT_MS = 5000;
/*
* 国家结果缓存时间:30 分钟。
* 避免每打开一个页面都重复查询。
*/
var CACHE_TTL_MS = 30 * 60 * 1000;
/*
* IP 查询失败时是否也强制跳转。
*
* false:
* 查询失败时允许访问,避免接口故障导致所有访客被拦截。
*
* true:
* 查询失败时也跳转到限制页面。
* 更严格,但 IP 接口故障时可能拦截所有访客。
*/
var BLOCK_ON_LOOKUP_ERROR = false;
var CACHE_KEY = 'storefront_country_access_v1';
var gateStyle = document.getElementById(
'country-access-gate-style'
);
var blockedCountries = BLOCKED_COUNTRIES.map(function (code) {
return String(code).trim().toUpperCase();
});
/*
* 显示网站内容。
*/
function revealPage() {
if (gateStyle) {
gateStyle.remove();
gateStyle = null;
}
document.documentElement.style.visibility = 'visible';
}
/*
* 标准化路径,移除末尾斜杠。
*/
function normalizePath(pathname) {
var path = pathname || '/';
if (
path.length > 1 &&
path.charAt(path.length - 1) === '/'
) {
path = path.slice(0, -1);
}
return path;
}
/*
* 判断当前页面是否已经是限制页面,
* 防止无限跳转。
*
* endsWith 可以兼容 Shopify Markets 的语言或市场路径,
* 例如:
* /de/pages/access-restricted
* /en-de/pages/access-restricted
*/
function isRedirectPage() {
var redirectUrl = new URL(
REDIRECT_URL,
window.location.origin
);
/*
* 外部跳转地址不需要检查当前 Shopify 路径。
*/
if (redirectUrl.origin !== window.location.origin) {
return false;
}
var currentPath = normalizePath(
window.location.pathname
);
var redirectPath = normalizePath(
redirectUrl.pathname
);
if (redirectPath === '/') {
return currentPath === '/';
}
return (
currentPath === redirectPath ||
currentPath.endsWith(redirectPath)
);
}
/*
* 判断国家是否在禁止列表中。
*/
function isBlockedCountry(countryCode) {
var normalizedCode = String(countryCode || '')
.trim()
.toUpperCase();
return blockedCountries.indexOf(normalizedCode) !== -1;
}
/*
* 跳转到限制页面。
*
* replace 不会在浏览器历史中保留当前页面,
* 用户点击返回按钮时不会立即返回受限制页面。
*/
function redirectVisitor(countryCode, reason) {
var redirectUrl = new URL(
REDIRECT_URL,
window.location.origin
);
/*
* 添加参数,方便限制页面显示不同提示。
* 不需要这些参数时可以删除。
*/
if (countryCode) {
redirectUrl.searchParams.set(
'country',
countryCode
);
}
if (reason) {
redirectUrl.searchParams.set(
'reason',
reason
);
}
/*
* 最后的死循环保护。
*/
if (redirectUrl.href === window.location.href) {
revealPage();
return;
}
window.location.replace(redirectUrl.href);
}
/*
* 读取当前浏览器会话中的国家缓存。
*/
function readCache() {
try {
var rawValue = window.sessionStorage.getItem(
CACHE_KEY
);
if (!rawValue) {
return null;
}
var cached = JSON.parse(rawValue);
if (
!cached ||
typeof cached.country !== 'string' ||
typeof cached.savedAt !== 'number' ||
Date.now() - cached.savedAt > CACHE_TTL_MS
) {
window.sessionStorage.removeItem(CACHE_KEY);
return null;
}
return cached.country.trim().toUpperCase();
} catch (error) {
return null;
}
}
/*
* 保存国家结果。
*/
function writeCache(countryCode) {
try {
window.sessionStorage.setItem(
CACHE_KEY,
JSON.stringify({
country: countryCode,
savedAt: Date.now()
})
);
} catch (error) {
/*
* 某些隐私浏览模式可能禁用 sessionStorage。
* 缓存失败不影响后续判断。
*/
}
}
/*
* 根据国家结果决定显示或跳转。
*/
function applyCountryRule(countryCode) {
var normalizedCode = String(countryCode || '')
.trim()
.toUpperCase();
/*
* 国家代码必须是两个英文字母。
*/
if (!/^[A-Z]{2}$/.test(normalizedCode)) {
throw new Error(
'Invalid country code returned by lookup service.'
);
}
writeCache(normalizedCode);
if (isBlockedCountry(normalizedCode)) {
redirectVisitor(
normalizedCode,
'country_blocked'
);
return;
}
revealPage();
}
/*
* 限制提示页面本身必须允许显示,
* 否则会造成跳转循环。
*/
if (isRedirectPage()) {
revealPage();
return;
}
/*
* 优先使用缓存。
*/
var cachedCountry = readCache();
if (cachedCountry) {
if (isBlockedCountry(cachedCountry)) {
redirectVisitor(
cachedCountry,
'country_blocked'
);
} else {
revealPage();
}
return;
}
/*
* 用 AbortController 中止超时请求。
*/
var controller =
typeof AbortController !== 'undefined'
? new AbortController()
: null;
var fetchOptions = {
method: 'GET',
cache: 'no-store',
credentials: 'omit',
headers: {
Accept: 'text/plain'
}
};
if (controller) {
fetchOptions.signal = controller.signal;
}
/*
* 超时 Promise。
* 即便旧浏览器不支持 AbortController,
* 页面也不会永久停留在隐藏状态。
*/
var timeoutId;
var timeoutPromise = new Promise(function (_, reject) {
timeoutId = window.setTimeout(function () {
if (controller) {
controller.abort();
}
reject(
new Error('Country lookup timed out.')
);
}, REQUEST_TIMEOUT_MS);
});
/*
* 国家查询请求。
*/
var lookupPromise = fetch(
LOOKUP_URL,
fetchOptions
).then(function (response) {
if (!response.ok) {
throw new Error(
'Country lookup failed with HTTP ' +
response.status +
'.'
);
}
return response.text();
});
/*
* 查询和超时竞争,先完成的结果生效。
*/
Promise.race([
lookupPromise,
timeoutPromise
])
.then(function (countryCode) {
applyCountryRule(countryCode);
})
.catch(function (error) {
/*
* 严格模式:
* IP 查询失败时也跳转。
*/
if (BLOCK_ON_LOOKUP_ERROR) {
redirectVisitor(
'',
'country_lookup_failed'
);
return;
}
/*
* 默认安全模式:
* 查询失败时允许正常访问。
*/
revealPage();
if (
window.console &&
typeof window.console.warn === 'function'
) {
window.console.warn(
'Country access check failed:',
error
);
}
})
.finally(function () {
window.clearTimeout(timeoutId);
});
})();
</script>
<noscript>
<style>
/*
* JavaScript 被禁用时避免整个网站永久空白。
*/
html {
visibility: visible !important;
}
</style>
</noscript>
{% endunless %}
完事了,不懂就留言问吧


没有回复内容