@lucidcms/core
Version:
The core of the Lucid CMS. It's responsible for spinning up the API and serving the CMS.
1 lines • 11.6 kB
Source Map (JSON)
{"version":3,"file":"process-image.mjs","names":["mediaServices.checks.checkHasMediaStrategy","processedImageServices.optimizeImage","processedImageServices.getSingleCount","processedImageServices.checks.checkCanStore"],"sources":["../../../src/services/processed-images/process-image.ts"],"sourcesContent":["import { PassThrough } from \"node:stream\";\nimport type { ImageProcessorOptions } from \"../../libs/image-processor/types.js\";\nimport {\n\tsplitBodyForProcessing,\n\ttoNodeReadable,\n} from \"../../libs/media/index.js\";\nimport type { MediaAdapterStreamBody } from \"../../libs/media/types.js\";\nimport {\n\tMediaRepository,\n\tProcessedImagesRepository,\n} from \"../../libs/repositories/index.js\";\nimport { createBufferETag, matchesETag } from \"../../utils/http/etag.js\";\nimport {\n\tgetMediaKeyTenantKey,\n\tresolveMediaKeyTenant,\n} from \"../../utils/media/index.js\";\nimport type { ServiceFn } from \"../../utils/services/types.js\";\nimport { mediaServices, processedImageServices } from \"../index.js\";\nimport adjustStorageUsage from \"../media/adjust-storage-usage.js\";\n\nconst processImage: ServiceFn<\n\t[\n\t\t{\n\t\t\tkey: string;\n\t\t\tprocessKey: string;\n\t\t\tifNoneMatch?: string;\n\t\t\toptions: ImageProcessorOptions;\n\t\t},\n\t],\n\t{\n\t\tkey: string;\n\t\tcontentLength: number | undefined;\n\t\tcontentType: string | undefined;\n\t\tbody: MediaAdapterStreamBody;\n\t\tetag?: string | null;\n\t\tnotModified?: boolean;\n\t}\n> = async (context, data) => {\n\tconst mediaStrategyRes =\n\t\tawait mediaServices.checks.checkHasMediaStrategy(context);\n\tif (mediaStrategyRes.error) return mediaStrategyRes;\n\n\tconst sourceTenant = resolveMediaKeyTenant(context.config, data.key);\n\tconst processedTenant = resolveMediaKeyTenant(\n\t\tcontext.config,\n\t\tdata.processKey,\n\t);\n\tconst sourceTenantKey = getMediaKeyTenantKey(data.key);\n\n\t// get og image\n\tconst mediaRes = await mediaStrategyRes.data.stream(context, {\n\t\tkey: data.key,\n\t\ttenant: sourceTenant,\n\t});\n\tif (mediaRes.error) return mediaRes;\n\n\t// If the response is not an image\n\tif (!mediaRes.data?.contentType?.startsWith(\"image/\")) {\n\t\treturn {\n\t\t\terror: undefined,\n\t\t\tdata: {\n\t\t\t\tkey: data.key,\n\t\t\t\tcontentLength: mediaRes.data.contentLength,\n\t\t\t\tcontentType: mediaRes.data.contentType,\n\t\t\t\tbody: mediaRes.data.body,\n\t\t\t\tetag: mediaRes.data.etag,\n\t\t\t},\n\t\t};\n\t}\n\n\tconst { processingBody, fallbackBody } = splitBodyForProcessing(\n\t\tmediaRes.data.body,\n\t);\n\tconst Media = new MediaRepository(context.db.client, context.config.db);\n\n\tconst focalRes = await Media.selectSingle({\n\t\tselect: [\"focal_x\", \"focal_y\"],\n\t\twhere: [{ key: \"key\", operator: \"=\", value: data.key }],\n\t});\n\tif (focalRes.error) return focalRes;\n\n\tconst focalPoint =\n\t\tfocalRes.data?.focal_x != null && focalRes.data.focal_y != null\n\t\t\t? {\n\t\t\t\t\tx: focalRes.data.focal_x / 10000,\n\t\t\t\t\ty: focalRes.data.focal_y / 10000,\n\t\t\t\t}\n\t\t\t: undefined;\n\n\t// Optimize image\n\tconst [imageRes, processedCountRes] = await Promise.all([\n\t\tprocessedImageServices.optimizeImage(context, {\n\t\t\tstream: toNodeReadable(processingBody),\n\t\t\toptions: { ...data.options, focalPoint },\n\t\t}),\n\t\tprocessedImageServices.getSingleCount(context, {\n\t\t\tkey: data.key,\n\t\t}),\n\t]);\n\n\tif (\n\t\timageRes.error ||\n\t\tprocessedCountRes.error ||\n\t\t!imageRes.data ||\n\t\t!imageRes.data.processed\n\t) {\n\t\treturn {\n\t\t\terror: undefined,\n\t\t\tdata: {\n\t\t\t\tkey: data.key,\n\t\t\t\tcontentLength: mediaRes.data.contentLength,\n\t\t\t\tcontentType: mediaRes.data.contentType,\n\t\t\t\tbody: fallbackBody,\n\t\t\t\tetag: mediaRes.data.etag,\n\t\t\t},\n\t\t};\n\t}\n\n\tlet processedEtag = createBufferETag(imageRes.data.buffer);\n\n\tif (data.ifNoneMatch && matchesETag(data.ifNoneMatch, processedEtag)) {\n\t\treturn {\n\t\t\terror: undefined,\n\t\t\tdata: {\n\t\t\t\tkey: data.processKey,\n\t\t\t\tcontentLength: undefined,\n\t\t\t\tcontentType: imageRes.data.mimeType,\n\t\t\t\tbody: new Uint8Array(),\n\t\t\t\tetag: processedEtag,\n\t\t\t\tnotModified: true,\n\t\t\t},\n\t\t};\n\t}\n\n\tconst stream = new PassThrough();\n\tstream.end(imageRes.data.buffer);\n\n\t// If the image should not be stored, return the stream\n\tif (!imageRes.data.shouldStore) {\n\t\treturn {\n\t\t\terror: undefined,\n\t\t\tdata: {\n\t\t\t\tkey: data.processKey,\n\t\t\t\tcontentLength: imageRes.data.size,\n\t\t\t\tcontentType: imageRes.data.mimeType,\n\t\t\t\tbody: stream,\n\t\t\t\tetag: processedEtag,\n\t\t\t},\n\t\t};\n\t}\n\n\t// Check if the processed image limit has been reached for this key, if so return processed image without saving\n\tif (\n\t\tprocessedCountRes.data >= context.config.media.limits.processedImagesPerFile\n\t) {\n\t\treturn {\n\t\t\terror: undefined,\n\t\t\tdata: {\n\t\t\t\tkey: data.processKey,\n\t\t\t\tcontentLength: imageRes.data.size,\n\t\t\t\tcontentType: imageRes.data.mimeType,\n\t\t\t\tbody: stream,\n\t\t\t\tetag: processedEtag,\n\t\t\t},\n\t\t};\n\t}\n\n\t// Check if we can store it\n\tconst canStoreRes = await processedImageServices.checks.checkCanStore(\n\t\tcontext,\n\t\t{\n\t\t\tsize: imageRes.data.size,\n\t\t\ttenantKey: sourceTenantKey,\n\t\t},\n\t);\n\tif (canStoreRes.error) {\n\t\treturn {\n\t\t\terror: undefined,\n\t\t\tdata: {\n\t\t\t\tkey: data.processKey,\n\t\t\t\tcontentLength: imageRes.data.size,\n\t\t\t\tcontentType: imageRes.data.mimeType,\n\t\t\t\tbody: stream,\n\t\t\t\tetag: processedEtag,\n\t\t\t},\n\t\t};\n\t}\n\n\tconst ProcessedImages = new ProcessedImagesRepository(\n\t\tcontext.db.client,\n\t\tcontext.config.db,\n\t);\n\n\tif (context.config.media.images.storeProcessed === true) {\n\t\tconst storageLimit = context.config.media.limits.storageBytes;\n\t\tconst adjustStorageRes = await adjustStorageUsage(context, {\n\t\t\ttenantKey: sourceTenantKey,\n\t\t\tdelta: imageRes.data.size,\n\t\t\tmax: storageLimit === false ? undefined : storageLimit,\n\t\t\tmin: 0,\n\t\t});\n\t\tif (adjustStorageRes.error || !adjustStorageRes.data.applied) {\n\t\t\treturn {\n\t\t\t\terror: undefined,\n\t\t\t\tdata: {\n\t\t\t\t\tkey: data.processKey,\n\t\t\t\t\tcontentLength: imageRes.data.size,\n\t\t\t\t\tcontentType: imageRes.data.mimeType,\n\t\t\t\t\tbody: stream,\n\t\t\t\t\tetag: processedEtag,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst [createProcessedImageRes, uploadRes] = await Promise.all([\n\t\t\tProcessedImages.createSingle({\n\t\t\t\tdata: {\n\t\t\t\t\tkey: data.processKey,\n\t\t\t\t\tmedia_key: data.key,\n\t\t\t\t\tfile_size: imageRes.data.size,\n\t\t\t\t},\n\t\t\t}),\n\t\t\tmediaStrategyRes.data.upload(context, {\n\t\t\t\tkey: data.processKey,\n\t\t\t\tbody: imageRes.data.buffer,\n\t\t\t\tmimeType: imageRes.data.mimeType,\n\t\t\t\textension: imageRes.data.extension,\n\t\t\t\tsize: imageRes.data.size,\n\t\t\t\ttype: \"image\",\n\t\t\t\ttenant: processedTenant,\n\t\t\t}),\n\t\t]);\n\n\t\tif (\n\t\t\tcreateProcessedImageRes.error !== undefined ||\n\t\t\tuploadRes.error !== undefined\n\t\t) {\n\t\t\tawait adjustStorageUsage(context, {\n\t\t\t\ttenantKey: sourceTenantKey,\n\t\t\t\tdelta: imageRes.data.size * -1,\n\t\t\t\tmin: 0,\n\t\t\t});\n\n\t\t\tawait Promise.allSettled([\n\t\t\t\tcreateProcessedImageRes.error === undefined\n\t\t\t\t\t? ProcessedImages.deleteSingle({\n\t\t\t\t\t\t\twhere: [{ key: \"key\", operator: \"=\", value: data.processKey }],\n\t\t\t\t\t\t\treturning: [\"key\"],\n\t\t\t\t\t\t})\n\t\t\t\t\t: Promise.resolve(),\n\t\t\t\tuploadRes.error === undefined\n\t\t\t\t\t? mediaStrategyRes.data.delete(context, {\n\t\t\t\t\t\t\tkey: data.processKey,\n\t\t\t\t\t\t\ttenant: processedTenant,\n\t\t\t\t\t\t})\n\t\t\t\t\t: Promise.resolve(),\n\t\t\t]);\n\n\t\t\treturn {\n\t\t\t\terror: undefined,\n\t\t\t\tdata: {\n\t\t\t\t\tkey: data.processKey,\n\t\t\t\t\tcontentLength: imageRes.data.size,\n\t\t\t\t\tcontentType: imageRes.data.mimeType,\n\t\t\t\t\tbody: stream,\n\t\t\t\t\tetag: processedEtag,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (uploadRes.error === undefined && uploadRes.data?.etag) {\n\t\t\tprocessedEtag = uploadRes.data.etag;\n\t\t}\n\t}\n\n\treturn {\n\t\terror: undefined,\n\t\tdata: {\n\t\t\tkey: data.processKey,\n\t\t\tcontentLength: imageRes.data.size,\n\t\t\tcontentType: imageRes.data.mimeType,\n\t\t\tbody: stream,\n\t\t\tetag: processedEtag,\n\t\t},\n\t};\n};\n\nexport default processImage;\n"],"mappings":"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"}