diff --git a/fluxer_app/src/features/channel/components/embeds/attachments/TextualAttachmentPreviewFetch.test.ts b/fluxer_app/src/features/channel/components/embeds/attachments/TextualAttachmentPreviewFetch.test.ts new file mode 100644 index 000000000..8d40f3e28 --- /dev/null +++ b/fluxer_app/src/features/channel/components/embeds/attachments/TextualAttachmentPreviewFetch.test.ts @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'; + +const runtime = vi.hoisted(() => ({desktop: false})); + +vi.mock('@lingui/core/macro', () => ({msg: (descriptor: unknown) => descriptor})); + +vi.mock('@app/features/platform/DesktopLocalAppRuntime', async (importOriginal) => ({ + ...(await importOriginal()), + isDesktopLocalAppDocument: () => runtime.desktop, +})); +vi.mock('@app/features/app/state/RuntimeConfig', () => ({ + default: { + mediaEndpoint: 'https://fluxerusercontent.com', + staticCdnEndpoint: 'https://fluxerstatic.com', + uploadRelayEndpoint: null, + getSnapshot: () => ({apiEndpoint: 'https://api.fluxer.app'}), + }, +})); +vi.mock('@app/features/app/state/InstanceSnapshotStore', () => ({runtimeInstanceKey: () => 'instance-key'})); +vi.mock('@app/features/messaging/state/AttachmentUrlRefresher', () => ({ + default: {refresh: async (url: string) => url}, +})); + +const {fetchTextualPreviewText} = await import( + '@app/features/channel/components/embeds/attachments/TextualAttachmentPreviewFetch' +); + +const ATTACHMENT_URL = 'https://fluxerusercontent.com/attachments/1/2/message.txt?ex=1&is=2&hm=3'; + +describe('fetchTextualPreviewText', () => { + const fetchMock = vi.fn(async (_input: RequestInfo | URL, _init?: RequestInit) => new Response('hello')); + + beforeEach(() => { + vi.stubGlobal('fetch', fetchMock); + vi.stubGlobal('window', {location: {href: 'fluxer-app://app/channels/@me'}}); + }); + + afterEach(() => { + vi.unstubAllGlobals(); + fetchMock.mockClear(); + runtime.desktop = false; + }); + + it('routes media fetches through the desktop resource proxy', async () => { + runtime.desktop = true; + await expect(fetchTextualPreviewText(ATTACHMENT_URL, new AbortController().signal)).resolves.toBe('hello'); + const requested = new URL(String(fetchMock.mock.calls[0]?.[0])); + expect(requested.protocol).toBe('fluxer-app:'); + expect(requested.pathname).toBe('/proxy/instance-key'); + expect(requested.searchParams.get('url')).toBe(ATTACHMENT_URL); + }); + + it('fetches media directly on the web', async () => { + await expect(fetchTextualPreviewText(ATTACHMENT_URL, new AbortController().signal)).resolves.toBe('hello'); + expect(fetchMock.mock.calls[0]?.[0]).toBe(ATTACHMENT_URL); + }); +}); diff --git a/fluxer_app/src/features/channel/components/embeds/attachments/TextualAttachmentPreviewFetch.ts b/fluxer_app/src/features/channel/components/embeds/attachments/TextualAttachmentPreviewFetch.ts index 27f93621d..45af1d476 100644 --- a/fluxer_app/src/features/channel/components/embeds/attachments/TextualAttachmentPreviewFetch.ts +++ b/fluxer_app/src/features/channel/components/embeds/attachments/TextualAttachmentPreviewFetch.ts @@ -2,6 +2,7 @@ import AttachmentUrlRefresher from '@app/features/messaging/state/AttachmentUrlRefresher'; import {TEXT_PREVIEW_MAX_BYTES} from '@app/features/messaging/utils/AttachmentPreviewUtils'; +import {wrapDesktopLocalResourceURL} from '@app/features/messaging/utils/DesktopResourceUrl'; const SIGNATURE_REFUSAL_STATUSES = new Set([401, 403, 404, 410]); @@ -65,7 +66,7 @@ function requestFailed(response: Response): Error { export async function fetchTextualPreviewText(url: string, signal: AbortSignal): Promise { const target = await AttachmentUrlRefresher.refresh(url); - const response = await fetch(target, {signal}); + const response = await fetch(wrapDesktopLocalResourceURL(target), {signal}); if (response.ok) { return readPreviewText(response); } @@ -76,7 +77,7 @@ export async function fetchTextualPreviewText(url: string, signal: AbortSignal): if (refreshed === target) { throw requestFailed(response); } - const retried = await fetch(refreshed, {signal}); + const retried = await fetch(wrapDesktopLocalResourceURL(refreshed), {signal}); if (!retried.ok) { throw requestFailed(retried); } diff --git a/fluxer_app/src/features/expressions/utils/GifFileDownload.ts b/fluxer_app/src/features/expressions/utils/GifFileDownload.ts index 96d2108ae..0ba530c10 100644 --- a/fluxer_app/src/features/expressions/utils/GifFileDownload.ts +++ b/fluxer_app/src/features/expressions/utils/GifFileDownload.ts @@ -1,6 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-or-later import type {Gif} from '@app/features/expressions/commands/GifCommands'; +import {wrapDesktopLocalResourceURL} from '@app/features/messaging/utils/DesktopResourceUrl'; const GIF_IMAGE_FORMAT_KEYS = ['gif', 'tinygif', 'nanogif'] as const; const GIF_VIDEO_FORMAT_KEYS = ['mp4', 'tinymp4'] as const; @@ -60,7 +61,7 @@ async function downloadFirstAvailableTarget(targets: Array, b seenUrls.add(target.url); attempts += 1; try { - const response = await fetch(target.url); + const response = await fetch(wrapDesktopLocalResourceURL(target.url)); if (!response.ok) continue; const blob = await response.blob(); if (blob.size === 0) continue; diff --git a/fluxer_app/src/features/voice/engine/EntranceSoundPlaybackEngine.ts b/fluxer_app/src/features/voice/engine/EntranceSoundPlaybackEngine.ts index 9674cb819..6628d81fb 100644 --- a/fluxer_app/src/features/voice/engine/EntranceSoundPlaybackEngine.ts +++ b/fluxer_app/src/features/voice/engine/EntranceSoundPlaybackEngine.ts @@ -1,5 +1,6 @@ // SPDX-License-Identifier: AGPL-3.0-or-later +import {wrapDesktopLocalResourceURL} from '@app/features/messaging/utils/DesktopResourceUrl'; import {Logger} from '@app/features/platform/utils/AppLogger'; import StreamerMode from '@app/features/streamer_mode/state/StreamerMode'; import Sound from '@app/features/ui/state/Sound'; @@ -66,7 +67,7 @@ class EntranceSoundPlaybackEngine { const ctx = this.ensureContext(); if (!ctx) return null; try { - const response = await fetch(url, {cache: 'force-cache'}); + const response = await fetch(wrapDesktopLocalResourceURL(url), {cache: 'force-cache'}); if (!response.ok) { logger.warn('Entrance sound fetch failed', {url, status: response.status}); return null;