fix: 修复分享链接路由与登录回跳

- 统一生成包含部署基路径和 Hash 路由的知识库、工作流分享地址

- 未登录访问分享页时保留目标地址,并在登录成功后自动回跳

- 限定分享密钥作用域并补充失效、加载失败及回归测试
This commit is contained in:
2026-07-24 16:00:57 +08:00
parent 41545fcef0
commit 526e16163b
15 changed files with 465 additions and 71 deletions

View File

@@ -1,3 +1,5 @@
import { readScopedRouteQueryParam } from './share-route-context';
/**
* 工作流协作分享请求头。
*/
@@ -10,37 +12,52 @@ interface WorkflowShareResolutionOptions<T> {
shareKey?: unknown;
}
interface WorkflowShareHeaderOptions {
pageUrl?: string;
requestMethod?: string;
requestUrl?: string;
}
const WORKFLOW_DESIGN_ROUTE = '/ai/workflow/design';
const WORKFLOW_SHARE_REQUESTS = [
['GET', '/api/v1/workflow/detail'],
['GET', '/api/v1/workflow/getRunningParameters'],
['GET', '/api/v1/workflow/publishApprovalRequirement'],
['GET', '/api/v1/workflowShare/resolve'],
['POST', '/api/v1/workflow/check'],
['POST', '/api/v1/workflow/getChainStatus'],
['POST', '/api/v1/workflow/resume'],
['POST', '/api/v1/workflow/runAsync'],
['POST', '/api/v1/workflow/singleRun'],
['POST', '/api/v1/workflow/submitPublishApproval'],
['POST', '/api/v1/workflow/update'],
] as const;
/**
* 从页面地址读取工作流分享密钥,兼容 history 与 hash 路由。
*/
export function readWorkflowShareKey(url?: string): null | string {
const currentUrl =
url || (typeof window === 'undefined' ? '' : window.location.href);
if (!currentUrl) {
return null;
return readScopedRouteQueryParam(WORKFLOW_DESIGN_ROUTE, 'shareKey', url);
}
/**
* 判断当前请求是否属于工作流分享授权白名单。
*/
export function isWorkflowShareRequest(
requestUrl?: string,
requestMethod?: string,
) {
if (!requestUrl || !requestMethod) {
return false;
}
try {
const parsed = new URL(
currentUrl,
typeof window === 'undefined'
? 'http://localhost'
: window.location.origin,
);
const historyKey = parsed.searchParams.get('shareKey')?.trim();
if (historyKey) {
return historyKey;
}
const queryIndex = parsed.hash.indexOf('?');
if (queryIndex === -1) {
return null;
}
return (
new URLSearchParams(parsed.hash.slice(queryIndex + 1))
.get('shareKey')
?.trim() || null
);
const parsed = new URL(requestUrl, 'http://easyflow.local');
const method = requestMethod.trim().toUpperCase();
return WORKFLOW_SHARE_REQUESTS.some(([allowedMethod, allowedPath]) => {
return method === allowedMethod && parsed.pathname.endsWith(allowedPath);
});
} catch {
return null;
return false;
}
}
@@ -49,9 +66,12 @@ export function readWorkflowShareKey(url?: string): null | string {
*/
export function withWorkflowShareHeader(
headers: Record<string, string>,
url?: string,
options: WorkflowShareHeaderOptions = {},
): Record<string, string> {
const shareKey = readWorkflowShareKey(url);
if (!isWorkflowShareRequest(options.requestUrl, options.requestMethod)) {
return headers;
}
const shareKey = readWorkflowShareKey(options.pageUrl);
if (!shareKey) {
return headers;
}