88c6ce8ccc
- 迁移为 frontend-web、frontend-electron、backend-web 与 docker 部署结构 - 网页端:订阅门禁二次弹窗、套餐/支付组件化、顶栏分组对齐 - 首页:最近文件与模板库布局优化,缩略图对齐,下载与删除操作 - 新增管理后台、支付与云端文件 API,更新 README 与项目规范 Co-authored-by: Cursor <cursoragent@cursor.com>
74 lines
2.7 KiB
JavaScript
74 lines
2.7 KiB
JavaScript
(function () {
|
|
'use strict';
|
|
|
|
var ALLOWED = /^(index\.web\.html|member\.web\.html|login\.web\.html|register\.web\.html|admin\/(?:index|login)\.html|index\.html|login\.html|design[12]\.web\.html)(\?.*)?$/i;
|
|
|
|
function soonEscapeHtml(s) {
|
|
if (s == null) return '';
|
|
return String(s)
|
|
.replace(/&/g, '&')
|
|
.replace(/"/g, '"')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>');
|
|
}
|
|
|
|
function soonSafeRedirect(urlParam, fallback) {
|
|
fallback = fallback || 'index.web.html';
|
|
if (!urlParam || typeof urlParam !== 'string') return fallback;
|
|
var raw = urlParam.trim();
|
|
if (!raw) return fallback;
|
|
try {
|
|
raw = decodeURIComponent(raw);
|
|
} catch (e) {
|
|
return fallback;
|
|
}
|
|
var lower = raw.toLowerCase();
|
|
if (lower.indexOf('//') === 0 || lower.indexOf('http:') === 0 || lower.indexOf('https:') === 0 ||
|
|
lower.indexOf('javascript:') === 0 || raw.indexOf('..') !== -1 || raw.indexOf('\\') !== -1) {
|
|
return fallback;
|
|
}
|
|
var pathOnly = raw.split(/[#?]/)[0];
|
|
if (ALLOWED.test(pathOnly)) return raw;
|
|
try {
|
|
var resolved = new URL(raw, window.location.href);
|
|
if (resolved.origin !== window.location.origin) return fallback;
|
|
var path = resolved.pathname.replace(/^.*\/pages\//, '').replace(/^\//, '');
|
|
if (!ALLOWED.test(path.split(/[#?]/)[0])) return fallback;
|
|
return path + resolved.search + resolved.hash;
|
|
} catch (e) {
|
|
return fallback;
|
|
}
|
|
}
|
|
|
|
function soonSafeImageUrl(url, fallback) {
|
|
if (!url || typeof url !== 'string') return fallback || '';
|
|
var raw = url.trim();
|
|
if (!raw) return fallback || '';
|
|
var lower = raw.toLowerCase();
|
|
if (lower.indexOf('javascript:') === 0 || lower.indexOf('data:text/html') === 0 ||
|
|
lower.indexOf('vbscript:') === 0 || lower.indexOf('//') === 0) {
|
|
return fallback || '';
|
|
}
|
|
if (lower.indexOf('data:image/') === 0) return raw;
|
|
if (lower.indexOf('blob:') === 0) return raw;
|
|
if (lower.indexOf('https:') === 0) return raw;
|
|
if (lower.indexOf('http:') === 0) {
|
|
try {
|
|
var apiBase = (typeof window !== 'undefined' && window.SOON_DEPLOY_CONFIG && window.SOON_DEPLOY_CONFIG.api_v1_base);
|
|
if (apiBase && new URL(raw).origin === new URL(apiBase).origin) return raw;
|
|
} catch (e) { /* fall through */ }
|
|
}
|
|
if (raw.indexOf('../') === 0 || raw.indexOf('./') === 0 || raw.indexOf('/') === 0) {
|
|
try {
|
|
var resolved = new URL(raw, window.location.href);
|
|
if (resolved.origin === window.location.origin) return raw;
|
|
} catch (e) { /* fall through */ }
|
|
}
|
|
return fallback || '';
|
|
}
|
|
|
|
window.soonEscapeHtml = soonEscapeHtml;
|
|
window.soonSafeRedirect = soonSafeRedirect;
|
|
window.soonSafeImageUrl = soonSafeImageUrl;
|
|
})();
|