Files

66 lines
2.6 KiB
TypeScript

/** 与后端及 asset-sync 的公开资源配置保持一致;此处不包含上传凭据。 */
export const assetConfig = {
publicDomain: 'https://cdn.joeyone.cn',
keyPrefix: 'doodle-assets/',
};
/** 固定字体和页面预览使用相对路径;接口返回的资源 URL 直接使用。 */
export function publicAssetUrl(relativePath: string): string {
const parts = relativePath.split('/');
if (
relativePath.includes('\\') ||
[...relativePath].some(
(char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127,
) ||
parts.some((part) => !part || part === '.' || part === '..')
)
throw new Error('资源相对路径不正确');
const domain = assetConfig.publicDomain.replace(/\/+$/, '');
const prefix = assetConfig.keyPrefix.replace(/\/+$/, '');
return `${domain}/${[...(prefix ? prefix.split('/') : []), ...parts].map(encodeURIComponent).join('/')}`;
}
const LEGACY_PREVIEW_PREFIX =
'cloud://cloud1-9gifs7a2756e2c87.636c-cloud1-9gifs7a2756e2c87-1351593184/';
const LEGACY_PREVIEW_HTTPS =
/^https?:\/\/636c-cloud1-9gifs7a2756e2c87-1351593184\.tcb\.qcloud\.la\/([^?#]+)(?:[?#].*)?$/;
/** 旧预拉取、缓存和云函数中的预览图指向已迁移的公开目录;其他资源不作替换。 */
export function resolvePreviewUrl(value: unknown): string {
if (typeof value !== 'string') return '';
const raw = value.trim();
let path: string;
if (raw.startsWith(LEGACY_PREVIEW_PREFIX)) {
path = raw.slice(LEGACY_PREVIEW_PREFIX.length);
if (/[?#]/.test(path)) return '';
} else {
const match = raw.match(LEGACY_PREVIEW_HTTPS);
if (!match) return raw;
path = match[1];
}
try {
path = path.split('/').map(decodeURIComponent).join('/');
if (!path.startsWith('assets/previews/')) return raw;
return publicAssetUrl(path);
} catch {
// 畸形路径不继续请求旧云存储,也不生成越界的 CDN 地址。
return '';
}
}
/** 统一处理预拉取/缓存中的嵌套 previewImg;不修改原数据、头像和其他云文件引用。 */
export function normalizePreviewImages<T>(value: T): T {
if (Array.isArray(value)) {
return value.map((item) => normalizePreviewImages(item)) as T;
}
if (!value || typeof value !== 'object') return value;
const result = { ...value } as Record<string, unknown>;
for (const key of Object.keys(result)) {
result[key] =
key === 'previewImg'
? resolvePreviewUrl(result[key])
: normalizePreviewImages(result[key]);
}
return result as T;
}