fix: serve svg+xml as svg (#13277)
Based from https://github.com/payloadcms/payload/pull/13276 Fixes https://github.com/payloadcms/payload/issues/7624 If an uploaded image has `.svg` ext, and the mimeType is read as `application/xml` adjust the mimeType to `image/svg+xml`. --------- Co-authored-by: Philipp Schneider <47689073+philipp-tailor@users.noreply.github.com>
This commit is contained in:
@@ -93,9 +93,14 @@ export const getFileHandler: PayloadHandler = async (req) => {
|
|||||||
|
|
||||||
const data = streamFile(filePath)
|
const data = streamFile(filePath)
|
||||||
const fileTypeResult = (await fileTypeFromFile(filePath)) || getFileTypeFallback(filePath)
|
const fileTypeResult = (await fileTypeFromFile(filePath)) || getFileTypeFallback(filePath)
|
||||||
|
let mimeType = fileTypeResult.mime
|
||||||
|
|
||||||
|
if (filePath.endsWith('.svg') && fileTypeResult.mime === 'application/xml') {
|
||||||
|
mimeType = 'image/svg+xml'
|
||||||
|
}
|
||||||
|
|
||||||
let headers = new Headers()
|
let headers = new Headers()
|
||||||
headers.set('Content-Type', fileTypeResult.mime)
|
headers.set('Content-Type', mimeType)
|
||||||
headers.set('Content-Length', stats.size + '')
|
headers.set('Content-Length', stats.size + '')
|
||||||
headers = collection.config.upload?.modifyResponseHeaders
|
headers = collection.config.upload?.modifyResponseHeaders
|
||||||
? collection.config.upload.modifyResponseHeaders({ headers }) || headers
|
? collection.config.upload.modifyResponseHeaders({ headers }) || headers
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import {
|
|||||||
mediaWithoutCacheTagsSlug,
|
mediaWithoutCacheTagsSlug,
|
||||||
relationPreviewSlug,
|
relationPreviewSlug,
|
||||||
relationSlug,
|
relationSlug,
|
||||||
|
svgOnlySlug,
|
||||||
threeDimensionalSlug,
|
threeDimensionalSlug,
|
||||||
withMetadataSlug,
|
withMetadataSlug,
|
||||||
withOnlyJPEGMetadataSlug,
|
withOnlyJPEGMetadataSlug,
|
||||||
@@ -87,6 +88,7 @@ let collectErrorsFromPage: () => boolean
|
|||||||
let stopCollectingErrorsFromPage: () => boolean
|
let stopCollectingErrorsFromPage: () => boolean
|
||||||
let bulkUploadsURL: AdminUrlUtil
|
let bulkUploadsURL: AdminUrlUtil
|
||||||
let fileMimeTypeURL: AdminUrlUtil
|
let fileMimeTypeURL: AdminUrlUtil
|
||||||
|
let svgOnlyURL: AdminUrlUtil
|
||||||
|
|
||||||
describe('Uploads', () => {
|
describe('Uploads', () => {
|
||||||
let page: Page
|
let page: Page
|
||||||
@@ -126,6 +128,7 @@ describe('Uploads', () => {
|
|||||||
constructorOptionsURL = new AdminUrlUtil(serverURL, constructorOptionsSlug)
|
constructorOptionsURL = new AdminUrlUtil(serverURL, constructorOptionsSlug)
|
||||||
bulkUploadsURL = new AdminUrlUtil(serverURL, bulkUploadsSlug)
|
bulkUploadsURL = new AdminUrlUtil(serverURL, bulkUploadsSlug)
|
||||||
fileMimeTypeURL = new AdminUrlUtil(serverURL, fileMimeTypeSlug)
|
fileMimeTypeURL = new AdminUrlUtil(serverURL, fileMimeTypeSlug)
|
||||||
|
svgOnlyURL = new AdminUrlUtil(serverURL, svgOnlySlug)
|
||||||
|
|
||||||
const context = await browser.newContext()
|
const context = await browser.newContext()
|
||||||
page = await context.newPage()
|
page = await context.newPage()
|
||||||
@@ -270,6 +273,23 @@ describe('Uploads', () => {
|
|||||||
await expect(fileMetaSizeType).toHaveText(/model\/gltf-binary/)
|
await expect(fileMetaSizeType).toHaveText(/model\/gltf-binary/)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should show proper mimetype for svg+xml file', async () => {
|
||||||
|
await page.goto(svgOnlyURL.create)
|
||||||
|
|
||||||
|
await page.setInputFiles('input[type="file"]', path.resolve(dirname, './svgWithXml.svg'))
|
||||||
|
const filename = page.locator('.file-field__filename')
|
||||||
|
await expect(filename).toHaveValue('svgWithXml.svg')
|
||||||
|
|
||||||
|
await saveDocAndAssert(page)
|
||||||
|
|
||||||
|
const fileMetaSizeType = page.locator('.file-meta__size-type')
|
||||||
|
await expect(fileMetaSizeType).toHaveText(/image\/svg\+xml/)
|
||||||
|
|
||||||
|
// ensure the svg loads
|
||||||
|
const svgImage = page.locator('img[src*="svgWithXml"]')
|
||||||
|
await expect(svgImage).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
test('should create animated file upload', async () => {
|
test('should create animated file upload', async () => {
|
||||||
await page.goto(animatedTypeMediaURL.create)
|
await page.goto(animatedTypeMediaURL.create)
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,9 @@ export interface Config {
|
|||||||
'allow-list-media': AllowListMedia;
|
'allow-list-media': AllowListMedia;
|
||||||
'skip-safe-fetch-media': SkipSafeFetchMedia;
|
'skip-safe-fetch-media': SkipSafeFetchMedia;
|
||||||
'skip-allow-list-safe-fetch-media': SkipAllowListSafeFetchMedia;
|
'skip-allow-list-safe-fetch-media': SkipAllowListSafeFetchMedia;
|
||||||
|
'restrict-file-types': RestrictFileType;
|
||||||
|
'no-restrict-file-types': NoRestrictFileType;
|
||||||
|
'no-restrict-file-mime-types': NoRestrictFileMimeType;
|
||||||
'animated-type-media': AnimatedTypeMedia;
|
'animated-type-media': AnimatedTypeMedia;
|
||||||
enlarge: Enlarge;
|
enlarge: Enlarge;
|
||||||
'without-enlarge': WithoutEnlarge;
|
'without-enlarge': WithoutEnlarge;
|
||||||
@@ -113,6 +116,8 @@ export interface Config {
|
|||||||
'constructor-options': ConstructorOption;
|
'constructor-options': ConstructorOption;
|
||||||
'bulk-uploads': BulkUpload;
|
'bulk-uploads': BulkUpload;
|
||||||
'simple-relationship': SimpleRelationship;
|
'simple-relationship': SimpleRelationship;
|
||||||
|
'file-mime-type': FileMimeType;
|
||||||
|
'svg-only': SvgOnly;
|
||||||
users: User;
|
users: User;
|
||||||
'payload-locked-documents': PayloadLockedDocument;
|
'payload-locked-documents': PayloadLockedDocument;
|
||||||
'payload-preferences': PayloadPreference;
|
'payload-preferences': PayloadPreference;
|
||||||
@@ -137,6 +142,9 @@ export interface Config {
|
|||||||
'allow-list-media': AllowListMediaSelect<false> | AllowListMediaSelect<true>;
|
'allow-list-media': AllowListMediaSelect<false> | AllowListMediaSelect<true>;
|
||||||
'skip-safe-fetch-media': SkipSafeFetchMediaSelect<false> | SkipSafeFetchMediaSelect<true>;
|
'skip-safe-fetch-media': SkipSafeFetchMediaSelect<false> | SkipSafeFetchMediaSelect<true>;
|
||||||
'skip-allow-list-safe-fetch-media': SkipAllowListSafeFetchMediaSelect<false> | SkipAllowListSafeFetchMediaSelect<true>;
|
'skip-allow-list-safe-fetch-media': SkipAllowListSafeFetchMediaSelect<false> | SkipAllowListSafeFetchMediaSelect<true>;
|
||||||
|
'restrict-file-types': RestrictFileTypesSelect<false> | RestrictFileTypesSelect<true>;
|
||||||
|
'no-restrict-file-types': NoRestrictFileTypesSelect<false> | NoRestrictFileTypesSelect<true>;
|
||||||
|
'no-restrict-file-mime-types': NoRestrictFileMimeTypesSelect<false> | NoRestrictFileMimeTypesSelect<true>;
|
||||||
'animated-type-media': AnimatedTypeMediaSelect<false> | AnimatedTypeMediaSelect<true>;
|
'animated-type-media': AnimatedTypeMediaSelect<false> | AnimatedTypeMediaSelect<true>;
|
||||||
enlarge: EnlargeSelect<false> | EnlargeSelect<true>;
|
enlarge: EnlargeSelect<false> | EnlargeSelect<true>;
|
||||||
'without-enlarge': WithoutEnlargeSelect<false> | WithoutEnlargeSelect<true>;
|
'without-enlarge': WithoutEnlargeSelect<false> | WithoutEnlargeSelect<true>;
|
||||||
@@ -166,6 +174,8 @@ export interface Config {
|
|||||||
'constructor-options': ConstructorOptionsSelect<false> | ConstructorOptionsSelect<true>;
|
'constructor-options': ConstructorOptionsSelect<false> | ConstructorOptionsSelect<true>;
|
||||||
'bulk-uploads': BulkUploadsSelect<false> | BulkUploadsSelect<true>;
|
'bulk-uploads': BulkUploadsSelect<false> | BulkUploadsSelect<true>;
|
||||||
'simple-relationship': SimpleRelationshipSelect<false> | SimpleRelationshipSelect<true>;
|
'simple-relationship': SimpleRelationshipSelect<false> | SimpleRelationshipSelect<true>;
|
||||||
|
'file-mime-type': FileMimeTypeSelect<false> | FileMimeTypeSelect<true>;
|
||||||
|
'svg-only': SvgOnlySelect<false> | SvgOnlySelect<true>;
|
||||||
users: UsersSelect<false> | UsersSelect<true>;
|
users: UsersSelect<false> | UsersSelect<true>;
|
||||||
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
||||||
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
||||||
@@ -838,6 +848,60 @@ export interface SkipAllowListSafeFetchMedia {
|
|||||||
focalX?: number | null;
|
focalX?: number | null;
|
||||||
focalY?: number | null;
|
focalY?: number | null;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "restrict-file-types".
|
||||||
|
*/
|
||||||
|
export interface RestrictFileType {
|
||||||
|
id: string;
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
url?: string | null;
|
||||||
|
thumbnailURL?: string | null;
|
||||||
|
filename?: string | null;
|
||||||
|
mimeType?: string | null;
|
||||||
|
filesize?: number | null;
|
||||||
|
width?: number | null;
|
||||||
|
height?: number | null;
|
||||||
|
focalX?: number | null;
|
||||||
|
focalY?: number | null;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "no-restrict-file-types".
|
||||||
|
*/
|
||||||
|
export interface NoRestrictFileType {
|
||||||
|
id: string;
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
url?: string | null;
|
||||||
|
thumbnailURL?: string | null;
|
||||||
|
filename?: string | null;
|
||||||
|
mimeType?: string | null;
|
||||||
|
filesize?: number | null;
|
||||||
|
width?: number | null;
|
||||||
|
height?: number | null;
|
||||||
|
focalX?: number | null;
|
||||||
|
focalY?: number | null;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "no-restrict-file-mime-types".
|
||||||
|
*/
|
||||||
|
export interface NoRestrictFileMimeType {
|
||||||
|
id: string;
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
url?: string | null;
|
||||||
|
thumbnailURL?: string | null;
|
||||||
|
filename?: string | null;
|
||||||
|
mimeType?: string | null;
|
||||||
|
filesize?: number | null;
|
||||||
|
width?: number | null;
|
||||||
|
height?: number | null;
|
||||||
|
focalX?: number | null;
|
||||||
|
focalY?: number | null;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "animated-type-media".
|
* via the `definition` "animated-type-media".
|
||||||
@@ -1502,6 +1566,43 @@ export interface SimpleRelationship {
|
|||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "file-mime-type".
|
||||||
|
*/
|
||||||
|
export interface FileMimeType {
|
||||||
|
id: string;
|
||||||
|
title?: string | null;
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
url?: string | null;
|
||||||
|
thumbnailURL?: string | null;
|
||||||
|
filename?: string | null;
|
||||||
|
mimeType?: string | null;
|
||||||
|
filesize?: number | null;
|
||||||
|
width?: number | null;
|
||||||
|
height?: number | null;
|
||||||
|
focalX?: number | null;
|
||||||
|
focalY?: number | null;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "svg-only".
|
||||||
|
*/
|
||||||
|
export interface SvgOnly {
|
||||||
|
id: string;
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
url?: string | null;
|
||||||
|
thumbnailURL?: string | null;
|
||||||
|
filename?: string | null;
|
||||||
|
mimeType?: string | null;
|
||||||
|
filesize?: number | null;
|
||||||
|
width?: number | null;
|
||||||
|
height?: number | null;
|
||||||
|
focalX?: number | null;
|
||||||
|
focalY?: number | null;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "users".
|
* via the `definition` "users".
|
||||||
@@ -1601,6 +1702,18 @@ export interface PayloadLockedDocument {
|
|||||||
relationTo: 'skip-allow-list-safe-fetch-media';
|
relationTo: 'skip-allow-list-safe-fetch-media';
|
||||||
value: string | SkipAllowListSafeFetchMedia;
|
value: string | SkipAllowListSafeFetchMedia;
|
||||||
} | null)
|
} | null)
|
||||||
|
| ({
|
||||||
|
relationTo: 'restrict-file-types';
|
||||||
|
value: string | RestrictFileType;
|
||||||
|
} | null)
|
||||||
|
| ({
|
||||||
|
relationTo: 'no-restrict-file-types';
|
||||||
|
value: string | NoRestrictFileType;
|
||||||
|
} | null)
|
||||||
|
| ({
|
||||||
|
relationTo: 'no-restrict-file-mime-types';
|
||||||
|
value: string | NoRestrictFileMimeType;
|
||||||
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'animated-type-media';
|
relationTo: 'animated-type-media';
|
||||||
value: string | AnimatedTypeMedia;
|
value: string | AnimatedTypeMedia;
|
||||||
@@ -1717,6 +1830,14 @@ export interface PayloadLockedDocument {
|
|||||||
relationTo: 'simple-relationship';
|
relationTo: 'simple-relationship';
|
||||||
value: string | SimpleRelationship;
|
value: string | SimpleRelationship;
|
||||||
} | null)
|
} | null)
|
||||||
|
| ({
|
||||||
|
relationTo: 'file-mime-type';
|
||||||
|
value: string | FileMimeType;
|
||||||
|
} | null)
|
||||||
|
| ({
|
||||||
|
relationTo: 'svg-only';
|
||||||
|
value: string | SvgOnly;
|
||||||
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'users';
|
relationTo: 'users';
|
||||||
value: string | User;
|
value: string | User;
|
||||||
@@ -2429,6 +2550,57 @@ export interface SkipAllowListSafeFetchMediaSelect<T extends boolean = true> {
|
|||||||
focalX?: T;
|
focalX?: T;
|
||||||
focalY?: T;
|
focalY?: T;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "restrict-file-types_select".
|
||||||
|
*/
|
||||||
|
export interface RestrictFileTypesSelect<T extends boolean = true> {
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
url?: T;
|
||||||
|
thumbnailURL?: T;
|
||||||
|
filename?: T;
|
||||||
|
mimeType?: T;
|
||||||
|
filesize?: T;
|
||||||
|
width?: T;
|
||||||
|
height?: T;
|
||||||
|
focalX?: T;
|
||||||
|
focalY?: T;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "no-restrict-file-types_select".
|
||||||
|
*/
|
||||||
|
export interface NoRestrictFileTypesSelect<T extends boolean = true> {
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
url?: T;
|
||||||
|
thumbnailURL?: T;
|
||||||
|
filename?: T;
|
||||||
|
mimeType?: T;
|
||||||
|
filesize?: T;
|
||||||
|
width?: T;
|
||||||
|
height?: T;
|
||||||
|
focalX?: T;
|
||||||
|
focalY?: T;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "no-restrict-file-mime-types_select".
|
||||||
|
*/
|
||||||
|
export interface NoRestrictFileMimeTypesSelect<T extends boolean = true> {
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
url?: T;
|
||||||
|
thumbnailURL?: T;
|
||||||
|
filename?: T;
|
||||||
|
mimeType?: T;
|
||||||
|
filesize?: T;
|
||||||
|
width?: T;
|
||||||
|
height?: T;
|
||||||
|
focalX?: T;
|
||||||
|
focalY?: T;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "animated-type-media_select".
|
* via the `definition` "animated-type-media_select".
|
||||||
@@ -3138,6 +3310,41 @@ export interface SimpleRelationshipSelect<T extends boolean = true> {
|
|||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "file-mime-type_select".
|
||||||
|
*/
|
||||||
|
export interface FileMimeTypeSelect<T extends boolean = true> {
|
||||||
|
title?: T;
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
url?: T;
|
||||||
|
thumbnailURL?: T;
|
||||||
|
filename?: T;
|
||||||
|
mimeType?: T;
|
||||||
|
filesize?: T;
|
||||||
|
width?: T;
|
||||||
|
height?: T;
|
||||||
|
focalX?: T;
|
||||||
|
focalY?: T;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "svg-only_select".
|
||||||
|
*/
|
||||||
|
export interface SvgOnlySelect<T extends boolean = true> {
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
url?: T;
|
||||||
|
thumbnailURL?: T;
|
||||||
|
filename?: T;
|
||||||
|
mimeType?: T;
|
||||||
|
filesize?: T;
|
||||||
|
width?: T;
|
||||||
|
height?: T;
|
||||||
|
focalX?: T;
|
||||||
|
focalY?: T;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "users_select".
|
* via the `definition` "users_select".
|
||||||
|
|||||||
Reference in New Issue
Block a user