统一发布分享弹窗为作品分享卡片 支持下载分享卡与小程序九宫切图保存 小程序复制链接改为可直达作品详情的 web-view 路径 修复本地 dev Rust 构建绕过损坏 sccache 补充分享链路与 dev 启动文档和测试
130 lines
3.6 KiB
JavaScript
130 lines
3.6 KiB
JavaScript
const ALLOWED_TARGET_PATHS = new Set(['/works/detail']);
|
|
|
|
function trimTrailingSlash(value) {
|
|
return String(value || '').trim().replace(/\/+$/u, '');
|
|
}
|
|
|
|
function appendQuery(url, query) {
|
|
const rawUrl = String(url || '');
|
|
const pairs = Object.keys(query)
|
|
.filter((key) => query[key])
|
|
.map(
|
|
(key) =>
|
|
`${encodeURIComponent(key)}=${encodeURIComponent(String(query[key]))}`,
|
|
);
|
|
|
|
if (pairs.length === 0) {
|
|
return rawUrl;
|
|
}
|
|
|
|
const hashIndex = rawUrl.indexOf('#');
|
|
const baseUrl = hashIndex >= 0 ? rawUrl.slice(0, hashIndex) : rawUrl;
|
|
const hash = hashIndex >= 0 ? rawUrl.slice(hashIndex) : '';
|
|
return `${baseUrl}${baseUrl.includes('?') ? '&' : '?'}${pairs.join('&')}${hash}`;
|
|
}
|
|
|
|
function appendHashParams(url, params) {
|
|
const nextKeys = new Set(Object.keys(params).filter((key) => params[key]));
|
|
const pairs = Object.keys(params)
|
|
.filter((key) => params[key])
|
|
.map(
|
|
(key) =>
|
|
`${encodeURIComponent(key)}=${encodeURIComponent(String(params[key]))}`,
|
|
);
|
|
if (pairs.length === 0) {
|
|
return url;
|
|
}
|
|
|
|
const hashIndex = url.indexOf('#');
|
|
const baseUrl = hashIndex >= 0 ? url.slice(0, hashIndex) : url;
|
|
const rawHash = hashIndex >= 0 ? url.slice(hashIndex + 1) : '';
|
|
const keptHashParts = rawHash.split('&').filter((part) => {
|
|
if (!part) {
|
|
return false;
|
|
}
|
|
const [rawKey = ''] = part.split('=');
|
|
try {
|
|
return !nextKeys.has(decodeURIComponent(rawKey));
|
|
} catch (_error) {
|
|
return !nextKeys.has(rawKey);
|
|
}
|
|
});
|
|
return `${baseUrl}#${keptHashParts.concat(pairs).join('&')}`;
|
|
}
|
|
|
|
function normalizeTargetPath(value) {
|
|
const trimmed = String(value || '').trim();
|
|
if (!trimmed.startsWith('/')) {
|
|
return '';
|
|
}
|
|
|
|
const normalized = trimmed.replace(/\/+$/u, '') || '/';
|
|
return ALLOWED_TARGET_PATHS.has(normalized) ? normalized : '';
|
|
}
|
|
|
|
function resolveLaunchTargetQuery(query) {
|
|
const targetPath = normalizeTargetPath(query && query.targetPath);
|
|
const work = String((query && query.work) || '').trim();
|
|
if (!targetPath || !work) {
|
|
return {};
|
|
}
|
|
|
|
return {
|
|
targetPath,
|
|
work,
|
|
};
|
|
}
|
|
|
|
function appendLaunchTargetToEntryUrl(entryUrl, query) {
|
|
const launchTarget = resolveLaunchTargetQuery(query);
|
|
if (!launchTarget.targetPath) {
|
|
return entryUrl;
|
|
}
|
|
|
|
const rawEntryUrl = String(entryUrl || '').trim();
|
|
const hashIndex = rawEntryUrl.indexOf('#');
|
|
const entryWithoutHash =
|
|
hashIndex >= 0 ? rawEntryUrl.slice(0, hashIndex) : rawEntryUrl;
|
|
const hash = hashIndex >= 0 ? rawEntryUrl.slice(hashIndex) : '';
|
|
const queryIndex = entryWithoutHash.indexOf('?');
|
|
const entryBase =
|
|
queryIndex >= 0 ? entryWithoutHash.slice(0, queryIndex) : entryWithoutHash;
|
|
const entrySearch =
|
|
queryIndex >= 0 ? entryWithoutHash.slice(queryIndex) : '';
|
|
const targetUrl = `${trimTrailingSlash(entryBase)}${launchTarget.targetPath}${entrySearch}${hash}`;
|
|
|
|
return appendQuery(targetUrl, {
|
|
work: launchTarget.work,
|
|
});
|
|
}
|
|
|
|
function resolveWebViewUrlFromRuntimeConfig(
|
|
authResult,
|
|
launchQuery = {},
|
|
runtimeConfig = {},
|
|
) {
|
|
const entryUrl = appendLaunchTargetToEntryUrl(
|
|
String(runtimeConfig.webViewEntryUrl || '').trim(),
|
|
launchQuery,
|
|
);
|
|
const sourcedUrl = appendQuery(entryUrl, runtimeConfig.sourceQuery || {});
|
|
if (!authResult || !authResult.token) {
|
|
return sourcedUrl;
|
|
}
|
|
|
|
return appendHashParams(sourcedUrl, {
|
|
auth_provider: 'wechat',
|
|
auth_token: authResult.token,
|
|
auth_binding_status: authResult.bindingStatus,
|
|
});
|
|
}
|
|
|
|
module.exports = {
|
|
appendHashParams,
|
|
appendLaunchTargetToEntryUrl,
|
|
appendQuery,
|
|
normalizeTargetPath,
|
|
resolveLaunchTargetQuery,
|
|
resolveWebViewUrlFromRuntimeConfig,
|
|
};
|