image.ts 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. const VOLATILE_IMAGE_HOSTS = new Set([
  2. 'finder.video.qq.com',
  3. ]);
  4. const VOLATILE_IMAGE_PATH_PARTS = [
  5. '/stodownload',
  6. ];
  7. const IMAGE_FIELD_KEYS = new Set([
  8. 'avatar',
  9. 'avatarUrl',
  10. 'avatar_url',
  11. 'accountAvatar',
  12. 'authorAvatar',
  13. 'cover',
  14. 'coverUrl',
  15. 'cover_url',
  16. 'thumbnail',
  17. 'thumbnailUrl',
  18. 'thumbnail_url',
  19. ]);
  20. function isVolatileRemoteImageUrl(value: string): boolean {
  21. const trimmed = value.trim();
  22. if (!trimmed || trimmed.startsWith('data:') || trimmed.startsWith('blob:')) {
  23. return false;
  24. }
  25. try {
  26. const url = new URL(trimmed);
  27. const host = url.hostname.toLowerCase();
  28. const path = url.pathname.toLowerCase();
  29. if (VOLATILE_IMAGE_HOSTS.has(host) && VOLATILE_IMAGE_PATH_PARTS.some((part) => path.includes(part))) {
  30. return true;
  31. }
  32. return host.endsWith('.video.qq.com') && url.searchParams.has('encfilekey');
  33. } catch {
  34. return false;
  35. }
  36. }
  37. export function getSafeImageSrc(value: string | null | undefined): string | undefined {
  38. if (!value) return undefined;
  39. const trimmed = value.trim();
  40. if (!trimmed || isVolatileRemoteImageUrl(trimmed)) return undefined;
  41. return trimmed;
  42. }
  43. export function sanitizeApiImageFields<T>(payload: T): T {
  44. const seen = new WeakSet<object>();
  45. function visit(value: unknown): unknown {
  46. if (!value || typeof value !== 'object') return value;
  47. if (seen.has(value)) return value;
  48. seen.add(value);
  49. if (Array.isArray(value)) {
  50. value.forEach((item) => visit(item));
  51. return value;
  52. }
  53. const record = value as Record<string, unknown>;
  54. Object.keys(record).forEach((key) => {
  55. const fieldValue = record[key];
  56. if (typeof fieldValue === 'string' && IMAGE_FIELD_KEYS.has(key) && isVolatileRemoteImageUrl(fieldValue)) {
  57. record[key] = '';
  58. return;
  59. }
  60. visit(fieldValue);
  61. });
  62. return value;
  63. }
  64. return visit(payload) as T;
  65. }