/** 与后端及 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(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; for (const key of Object.keys(result)) { result[key] = key === 'previewImg' ? resolvePreviewUrl(result[key]) : normalizePreviewImages(result[key]); } return result as T; }