From 0adaeccd0dd0cb80c30cf8bcb035004906c93cba Mon Sep 17 00:00:00 2001 From: Vlad0n20 Date: Mon, 24 Aug 2026 17:00:09 +0200 Subject: [PATCH] fix(preprints): version-pin pdf render url to prevent stale cache on recovered preprints --- .../preprint-file-section.component.spec.ts | 41 +++++++++++++++++++ .../preprint-file-section.component.ts | 7 +++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/app/features/preprints/components/preprint-details/preprint-file-section/preprint-file-section.component.spec.ts b/src/app/features/preprints/components/preprint-details/preprint-file-section/preprint-file-section.component.spec.ts index 5b90f6653..3cc3b1684 100644 --- a/src/app/features/preprints/components/preprint-details/preprint-file-section/preprint-file-section.component.spec.ts +++ b/src/app/features/preprints/components/preprint-details/preprint-file-section/preprint-file-section.component.spec.ts @@ -116,6 +116,47 @@ describe('PreprintFileSectionComponent', () => { expect(component.safeLink()).toBeNull(); }); + it('should pin the safe link to the latest file version to bust stale MFR/CDN caches', () => { + const downloadUrl = 'https://files.osf.io/v1/resources/abc/providers/osfstorage/def'; + setup({ + selectorOverrides: [ + { + selector: PreprintSelectors.getPreprintFile, + value: { + ...mockFile, + links: { ...mockFile.links, render: `https://mfr.osf.io/render?url=${encodeURIComponent(downloadUrl)}` }, + }, + }, + ], + }); + + const safeLink = component.safeLink(); + const nestedDownloadUrl = new URL(new URL(safeLink!).searchParams.get('url')!); + + expect(nestedDownloadUrl.searchParams.get('version')).toBe('1'); + }); + + it('should not add a version param when there are no file versions yet', () => { + const downloadUrl = 'https://files.osf.io/v1/resources/abc/providers/osfstorage/def'; + setup({ + selectorOverrides: [ + { + selector: PreprintSelectors.getPreprintFile, + value: { + ...mockFile, + links: { ...mockFile.links, render: `https://mfr.osf.io/render?url=${encodeURIComponent(downloadUrl)}` }, + }, + }, + { selector: PreprintSelectors.getPreprintFileVersions, value: [] }, + ], + }); + + const safeLink = component.safeLink(); + const nestedDownloadUrl = new URL(new URL(safeLink!).searchParams.get('url')!); + + expect(nestedDownloadUrl.searchParams.has('version')).toBe(false); + }); + it('should compute version menu items from file versions', () => { setup(); const menuItems = component.versionMenuItems(); diff --git a/src/app/features/preprints/components/preprint-details/preprint-file-section/preprint-file-section.component.ts b/src/app/features/preprints/components/preprint-details/preprint-file-section/preprint-file-section.component.ts index 8b81a88a3..b3f814fd5 100644 --- a/src/app/features/preprints/components/preprint-details/preprint-file-section/preprint-file-section.component.ts +++ b/src/app/features/preprints/components/preprint-details/preprint-file-section/preprint-file-section.component.ts @@ -14,6 +14,7 @@ import { ProviderReviewsWorkflow } from '@osf/features/preprints/enums'; import { PreprintSelectors } from '@osf/features/preprints/store/preprint'; import { LoadingSpinnerComponent } from '@osf/shared/components/loading-spinner/loading-spinner.component'; import { IS_LARGE, IS_MEDIUM } from '@osf/shared/helpers/breakpoints.tokens'; +import { getMfrUrlWithVersion } from '@osf/shared/helpers/mfr-url.helper'; import { SafeUrlPipe } from '@osf/shared/pipes/safe-url.pipe'; import { DataciteService } from '@osf/shared/services/datacite/datacite.service'; @@ -43,7 +44,11 @@ export class PreprintFileSectionComponent { fileVersions = select(PreprintSelectors.getPreprintFileVersions); areFileVersionsLoading = select(PreprintSelectors.arePreprintFileVersionsLoading); - safeLink = computed(() => this.file()?.links.render ?? null); + safeLink = computed(() => { + const latestVersionId = this.fileVersions()?.[0]?.id ?? ''; + + return getMfrUrlWithVersion(this.file()?.links.render, latestVersionId); + }); isIframeLoading = true; versionMenuItems = computed(() => {