mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Apply consistent Sharp library configuration (#23064)
* Apply consistent Sharp library configuration (#20995) * rename useSharp to getSharpInstance * Update changeset * Update export style --------- Co-authored-by: Pascal Jufer <pascal-jufer@bluewin.ch> Co-authored-by: Rijk van Zanten <rijkvanzanten@me.com>
This commit is contained in:
5
.changeset/silent-emus-rush.md
Normal file
5
.changeset/silent-emus-rush.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@directus/api': patch
|
||||
---
|
||||
|
||||
Ensured `ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION` is also respected for extraction of metadata during image upload
|
||||
@@ -13,7 +13,6 @@ import { contentType } from 'mime-types';
|
||||
import type { Readable } from 'node:stream';
|
||||
import hash from 'object-hash';
|
||||
import path from 'path';
|
||||
import type { FailOnOptions } from 'sharp';
|
||||
import sharp from 'sharp';
|
||||
import { SUPPORTED_IMAGE_TRANSFORM_FORMATS } from '../constants.js';
|
||||
import getDatabase from '../database/index.js';
|
||||
@@ -25,6 +24,7 @@ import { isValidUuid } from '../utils/is-valid-uuid.js';
|
||||
import * as TransformationUtils from '../utils/transformations.js';
|
||||
import { AuthorizationService } from './authorization.js';
|
||||
import { FilesService } from './files.js';
|
||||
import { getSharpInstance } from './files/lib/get-sharp-instance.js';
|
||||
|
||||
const env = useEnv();
|
||||
const logger = useLogger();
|
||||
@@ -162,11 +162,7 @@ export class AssetsService {
|
||||
|
||||
const readStream = await storage.location(file.storage).read(file.filename_disk, range);
|
||||
|
||||
const transformer = sharp({
|
||||
limitInputPixels: Math.pow(env['ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION'] as number, 2),
|
||||
sequentialRead: true,
|
||||
failOn: env['ASSETS_INVALID_IMAGE_SENSITIVITY_LEVEL'] as FailOnOptions,
|
||||
});
|
||||
const transformer = getSharpInstance();
|
||||
|
||||
transformer.timeout({
|
||||
seconds: clamp(Math.round(getMilliseconds(env['ASSETS_TRANSFORM_TIMEOUT'], 0) / 1000), 1, 3600),
|
||||
|
||||
37
api/src/services/files/lib/get-sharp-instance.test.ts
Normal file
37
api/src/services/files/lib/get-sharp-instance.test.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { useEnv } from '@directus/env';
|
||||
import { getSharpInstance } from './get-sharp-instance';
|
||||
|
||||
import { beforeAll, expect, test, vi } from 'vitest';
|
||||
|
||||
vi.mock('@directus/env');
|
||||
|
||||
vi.mock('sharp', () => {
|
||||
const sharp = {
|
||||
// using object with default property to mock default import
|
||||
default: vi.fn(),
|
||||
};
|
||||
|
||||
return sharp;
|
||||
});
|
||||
|
||||
const ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION = 94906265;
|
||||
const ASSETS_INVALID_IMAGE_SENSITIVITY_LEVEL = 'error';
|
||||
|
||||
beforeAll(() => {
|
||||
vi.mocked(useEnv).mockReturnValue({
|
||||
ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION,
|
||||
ASSETS_INVALID_IMAGE_SENSITIVITY_LEVEL,
|
||||
});
|
||||
});
|
||||
|
||||
test('getSharpInstance should apply the correct options', async () => {
|
||||
const sharp = await import('sharp');
|
||||
|
||||
getSharpInstance();
|
||||
|
||||
expect(sharp.default).toHaveBeenCalledWith({
|
||||
limitInputPixels: Math.pow(ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION, 2),
|
||||
sequentialRead: true,
|
||||
failOn: ASSETS_INVALID_IMAGE_SENSITIVITY_LEVEL,
|
||||
});
|
||||
});
|
||||
12
api/src/services/files/lib/get-sharp-instance.ts
Normal file
12
api/src/services/files/lib/get-sharp-instance.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { useEnv } from '@directus/env';
|
||||
import sharp, { type FailOnOptions, type Sharp } from 'sharp';
|
||||
|
||||
export function getSharpInstance(): Sharp {
|
||||
const env = useEnv();
|
||||
|
||||
return sharp({
|
||||
limitInputPixels: Math.trunc(Math.pow(env['ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION'] as number, 2)),
|
||||
sequentialRead: true,
|
||||
failOn: env['ASSETS_INVALID_IMAGE_SENSITIVITY_LEVEL'] as FailOnOptions,
|
||||
});
|
||||
}
|
||||
@@ -4,10 +4,10 @@ import { type IccProfile, parse as parseIcc } from 'icc';
|
||||
import { pick } from 'lodash-es';
|
||||
import type { Readable } from 'node:stream';
|
||||
import { pipeline } from 'node:stream/promises';
|
||||
import sharp from 'sharp';
|
||||
import { useEnv } from '@directus/env';
|
||||
import { useLogger } from '../../../logger/index.js';
|
||||
import { parseIptc, parseXmp } from './parse-image-metadata.js';
|
||||
import { getSharpInstance } from '../lib/get-sharp-instance.js';
|
||||
|
||||
const env = useEnv();
|
||||
const logger = useLogger();
|
||||
@@ -18,10 +18,12 @@ export async function getMetadata(
|
||||
stream: Readable,
|
||||
allowList: string | string[] = env['FILE_METADATA_ALLOW_LIST'] as string[],
|
||||
): Promise<Metadata> {
|
||||
const transformer = getSharpInstance();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
pipeline(
|
||||
stream,
|
||||
sharp().metadata(async (err, sharpMetadata) => {
|
||||
transformer.metadata(async (err, sharpMetadata) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
|
||||
@@ -142,4 +142,5 @@
|
||||
- brandondrew
|
||||
- alantiller
|
||||
- SP12893678
|
||||
- AndriyAntonenko
|
||||
- jacobwise
|
||||
|
||||
Reference in New Issue
Block a user