feat: 完成分享、单会话与发布审批改造

- 增加工作流协作分享与知识库卡片分享入口,统一低版本浏览器复制反馈

- Web 新登录替换旧会话,并保持 API Key 会话隔离

- 发布审批增加必填说明并在审批详情展示

- 账号重置与导入改用可配置默认强密码
This commit is contained in:
2026-07-23 16:09:31 +08:00
parent caa1f07b66
commit 5a42826d44
71 changed files with 3191 additions and 132 deletions

View File

@@ -0,0 +1,88 @@
/**
* 工作流协作分享请求头。
*/
export const WORKFLOW_SHARE_HEADER = 'X-Workflow-Share-Key';
interface WorkflowShareResolutionOptions<T> {
currentWorkflowId?: null | T;
onFailure: (error: unknown) => Promise<void> | void;
resolve: () => Promise<null | T | undefined>;
shareKey?: unknown;
}
/**
* 从页面地址读取工作流分享密钥,兼容 history 与 hash 路由。
*/
export function readWorkflowShareKey(url?: string): null | string {
const currentUrl =
url || (typeof window === 'undefined' ? '' : window.location.href);
if (!currentUrl) {
return null;
}
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
);
} catch {
return null;
}
}
/**
* 在保留现有请求头的基础上附加工作流分享密钥。
*/
export function withWorkflowShareHeader(
headers: Record<string, string>,
url?: string,
): Record<string, string> {
const shareKey = readWorkflowShareKey(url);
if (!shareKey) {
return headers;
}
return {
...headers,
[WORKFLOW_SHARE_HEADER]: shareKey,
};
}
/**
* 解析分享地址对应的工作流,并在链接失效时统一收口异常。
*/
export async function resolveWorkflowShareWorkflowId<T>({
currentWorkflowId,
onFailure,
resolve,
shareKey,
}: WorkflowShareResolutionOptions<T>): Promise<null | T> {
if (currentWorkflowId !== null && currentWorkflowId !== undefined) {
return currentWorkflowId;
}
const normalizedShareKey = Array.isArray(shareKey)
? shareKey.find((value) => String(value || '').trim())
: shareKey;
if (!String(normalizedShareKey || '').trim()) {
return null;
}
try {
return (await resolve()) ?? null;
} catch (error) {
await onFailure(error);
return null;
}
}