feat: 收口知识库分享链路

- 新增 shareKey 单参数 URL 分享页与失效页

- 新增知识库分享后端鉴权、审计与迁移脚本

- 在访问令牌中增加知识库分享授权入口
This commit is contained in:
2026-04-13 14:44:31 +08:00
parent 8cfe5400fe
commit 31a755a8bc
57 changed files with 5158 additions and 143 deletions

View File

@@ -0,0 +1,82 @@
import { api } from '#/api/request';
const EXPIRED_ERROR_CODES = new Set([4601, 4602]);
const SHARE_ERROR_REASON: Record<number, string> = {
4601: 'expired',
4602: 'invalid',
};
const APP_BASE_PATH = (import.meta.env.BASE_URL || '/').replace(/\/$/, '');
function getShareParams() {
const params = new URLSearchParams(window.location.search);
const shareKey = params.get('shareKey') || '';
return { shareKey };
}
function appendShareQuery(url: string, extraParams?: Record<string, any>) {
const base =
typeof window === 'undefined' ? 'http://localhost' : window.location.origin;
const target = new URL(url, base);
const { shareKey } = getShareParams();
if (shareKey) {
target.searchParams.set('shareKey', shareKey);
}
Object.entries(extraParams || {}).forEach(([key, value]) => {
if (value === undefined || value === null || value === '') {
return;
}
target.searchParams.set(key, String(value));
});
return `${target.pathname}${target.search}`;
}
function redirectIfExpired(response: any) {
const errorCode = Number(response?.errorCode);
if (!response || !EXPIRED_ERROR_CODES.has(errorCode)) {
return response;
}
const reason = SHARE_ERROR_REASON[errorCode] || 'expired';
window.location.assign(
`${APP_BASE_PATH}/share/knowledge/expired?reason=${reason}`,
);
return response;
}
export const knowledgeShareApi = {
get(url: string, config?: Record<string, any>) {
return api
.get(appendShareQuery(url, config?.params), {
...config,
params: undefined,
})
.then(redirectIfExpired);
},
post(url: string, data?: any, config?: Record<string, any>) {
return api
.post(appendShareQuery(url), data, config)
.then(redirectIfExpired);
},
postFile(url: string, data?: any, config?: Record<string, any>) {
return api
.postFile(appendShareQuery(url), data, config)
.then(redirectIfExpired);
},
upload(url: string, data?: any, config?: Record<string, any>) {
return api
.upload(appendShareQuery(url), data, config)
.then(redirectIfExpired);
},
download(url: string, config?: Record<string, any>) {
return api.download(appendShareQuery(url, config?.params), {
...config,
params: undefined,
});
},
};
export function buildKnowledgeShareUrl(
url: string,
extraParams?: Record<string, any>,
) {
return appendShareQuery(url, extraParams);
}