@websolutespa/bom-mixer-image 3.0.1 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -30
- package/dist/index.js.map +1 -1
- package/package.json +12 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @websolutespa/bom-mixer-image
|
|
2
2
|
|
|
3
|
+
## 3.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 3f7c21b: - Payload 3.91.0 and Next.js 16.4.2 (React 19.2.9): peer dependencies are now ranges, `payload` / `@payloadcms/*` `^3.91.0`, `next` `^16.3.3`, `react` / `react-dom` `^19.2.1`
|
|
8
|
+
- payload-plugin-bowl: the mixer context is built only for reads by an authenticated user (payload 3.90+ resolves the jwt user with the request query, populating tenants before `req.user` is set); email options typed with payload `SendEmailOptions`, no direct nodemailer dependency
|
|
9
|
+
- payload-plugin-storage: `AZURE_STORAGE_CONTAINER_ACCESS` (`private` default since payload 3.90, `blob`, `container`) for containers created by the plugin
|
|
10
|
+
- mixer web: `images.qualities` includes `IMAGE_QUALITY` (next 16 defaults to `[75]`)
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- 12b6e2f: - test: replace the jest presets with shared vitest configurations, `@websolutespa/test/vitest` (`defineNodeConfig`, `defineDomConfig`, `definePayloadConfig`); drop jest dependencies
|
|
15
|
+
- declare every imported package and script binary in the package that uses it (no more reliance on hoisting), drop unused dependencies
|
|
16
|
+
- bom-mixer-checkout: `useCart.add` no longer mutates the previous cart state
|
|
17
|
+
|
|
3
18
|
## 3.0.1
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/image/image.handler.ts"],"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/image/image.handler.ts"],"mappings":";;;;KAsBY,oBAAA;EACV,OAAA;EACA,KAAA;EACA,OAAA;AAAA;AAAA,KAGU,aAAA,GAAgB,YAAA;EAC1B,MAAA;EACA,MAAA;EACA,GAAA;AAAA;AAAA,KAGG,wBAAA;EACH,MAAA,EAAQ,OAAA;IAAU,KAAA;EAAA;IAAwB,KAAA;EAAA;AAAA;AAAA,iBAG5B,YAAA,CAAA;EACd,OAAA;EACA,KAAA;EACA;AAAA,GACC,oBAAA;AAAA,iBAsFa,eAAA,CAAA;EACd,OAAA;EACA,KAAA;EACA;AAAA,GACC,oBAAA,IACa,OAAA,EAAS,WAAA,EAAa,OAAA,EAAS,wBAAA,KAA2B,OAAA,CAAQ,QAAA"}
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,6 @@ import { NextResponse } from "next/server";
|
|
|
3
3
|
import * as path from "path";
|
|
4
4
|
import sharp from "sharp";
|
|
5
5
|
import { ImageStrategy, decryptImageResize } from "@websolutespa/bom-core";
|
|
6
|
-
|
|
7
6
|
//#region src/api/api.handler.ts
|
|
8
7
|
function apiHandler(handler) {
|
|
9
8
|
return async (request, response) => {
|
|
@@ -18,15 +17,13 @@ function apiHandler(handler) {
|
|
|
18
17
|
}
|
|
19
18
|
function errorHandler(error, response) {
|
|
20
19
|
if (typeof error === "string") {
|
|
21
|
-
const
|
|
22
|
-
const statusCode = is404 ? 404 : 400;
|
|
20
|
+
const statusCode = error.toLowerCase().endsWith("not found") ? 404 : 400;
|
|
23
21
|
return response.status(statusCode).json({ message: error });
|
|
24
22
|
}
|
|
25
23
|
if (error.name === "UnauthorizedError") return response.status(401).json({ message: "Invalid Token" });
|
|
26
24
|
console.error(error);
|
|
27
25
|
return response.status(500).json({ message: error.message });
|
|
28
26
|
}
|
|
29
|
-
|
|
30
27
|
//#endregion
|
|
31
28
|
//#region src/image/image.handler.ts
|
|
32
29
|
const IMAGE_STRATEGY = process.env.IMAGE_STRATEGY ? process.env.IMAGE_STRATEGY : ImageStrategy.Mixer;
|
|
@@ -63,9 +60,7 @@ function imageHandler({ domains = [], sizes = [], quality = IMAGE_QUALITY }) {
|
|
|
63
60
|
if (imageOptions.url.indexOf("http") === -1 && isVercel()) response.redirect(307, imageOptions.url);
|
|
64
61
|
const originalImageBuffer = await getOriginalImage(imageOptions.url, domains);
|
|
65
62
|
if (originalImageBuffer) {
|
|
66
|
-
const
|
|
67
|
-
const resizedImage = await resizeImage(originalImage, imageOptions);
|
|
68
|
-
const compressedImage = resizedImage.webp({ quality: imageOptions.q });
|
|
63
|
+
const compressedImage = (await resizeImage(sharp(originalImageBuffer, { animated: isGIF(imageOptions.url) }), imageOptions)).webp({ quality: imageOptions.q });
|
|
69
64
|
compressedImage.toFile(cachedName).then(() => console.log("imageHandler.sharp.cached", cachedName)).catch((error) => console.error("imageHandler.sharp.error", cachedName, error));
|
|
70
65
|
const data = await compressedImage.toBuffer();
|
|
71
66
|
response.setHeader("Content-Type", "image/webp");
|
|
@@ -97,20 +92,14 @@ function imageGetHandler({ domains = [], sizes = [], quality = IMAGE_QUALITY })
|
|
|
97
92
|
return getJsonErrorResponse(400, error);
|
|
98
93
|
}
|
|
99
94
|
const cachedName = urlToCachedName(imageOptions);
|
|
100
|
-
if (fs.existsSync(cachedName))
|
|
101
|
-
const cachedImage = await getCachedImage(cachedName);
|
|
102
|
-
return getImageResponse(cachedImage);
|
|
103
|
-
}
|
|
95
|
+
if (fs.existsSync(cachedName)) return getImageResponse(await getCachedImage(cachedName));
|
|
104
96
|
if (isInvalidRefererValue(request.headers.get("referer"))) return new Response("you are not authorized to perform this action", { status: 401 });
|
|
105
97
|
if (imageOptions.url.indexOf("http") === -1 && isVercel()) return NextResponse.redirect(new URL(imageOptions.url, request.url), 307);
|
|
106
98
|
const originalImageBuffer = await getOriginalImage(imageOptions.url, domains);
|
|
107
99
|
if (originalImageBuffer) {
|
|
108
|
-
const
|
|
109
|
-
const resizedImage = await resizeImage(originalImage, imageOptions);
|
|
110
|
-
const compressedImage = resizedImage.webp({ quality: imageOptions.q });
|
|
100
|
+
const compressedImage = (await resizeImage(sharp(originalImageBuffer, { animated: isGIF(imageOptions.url) }), imageOptions)).webp({ quality: imageOptions.q });
|
|
111
101
|
compressedImage.toFile(cachedName).then(() => console.log("imageHandler.sharp.cached", cachedName)).catch((error) => console.error("imageHandler.sharp.error", cachedName, error));
|
|
112
|
-
|
|
113
|
-
return getImageResponse(data);
|
|
102
|
+
return getImageResponse(await compressedImage.toBuffer());
|
|
114
103
|
}
|
|
115
104
|
return getJsonErrorResponse(404, "image not found");
|
|
116
105
|
} catch (error) {
|
|
@@ -134,9 +123,8 @@ function isInvalidRefererValue(referer) {
|
|
|
134
123
|
}
|
|
135
124
|
async function getImageOptionsNext(request, context, options) {
|
|
136
125
|
const params = await context.params;
|
|
137
|
-
const query = Object.fromEntries(request.nextUrl.searchParams.entries());
|
|
138
126
|
return getImageOptionsFromQuery({
|
|
139
|
-
...
|
|
127
|
+
...Object.fromEntries(request.nextUrl.searchParams.entries()),
|
|
140
128
|
slugs: params.slugs
|
|
141
129
|
}, options);
|
|
142
130
|
}
|
|
@@ -176,9 +164,8 @@ function getImageOptionsFromQuery(query, options) {
|
|
|
176
164
|
}
|
|
177
165
|
async function getEncryptedImageOptionsNext(request, context, options) {
|
|
178
166
|
const params = await context.params;
|
|
179
|
-
const query = Object.fromEntries(request.nextUrl.searchParams.entries());
|
|
180
167
|
return getEncryptedImageOptionsFromQuery({
|
|
181
|
-
...
|
|
168
|
+
...Object.fromEntries(request.nextUrl.searchParams.entries()),
|
|
182
169
|
slugs: params.slugs
|
|
183
170
|
}, options);
|
|
184
171
|
}
|
|
@@ -214,11 +201,8 @@ async function resizeImage(originalImage, imageOptions) {
|
|
|
214
201
|
if (resizeHeight && (focalX !== IMAGE_FOCAL_X || focalY !== IMAGE_FOCAL_Y)) {
|
|
215
202
|
const { width: originalWidth, height: originalHeight } = await originalImage.metadata();
|
|
216
203
|
if (originalWidth && originalHeight) {
|
|
217
|
-
const
|
|
218
|
-
const
|
|
219
|
-
const prioritizeHeight = resizeAspectRatio < originalAspectRatio;
|
|
220
|
-
const imageToResize = originalImage.clone();
|
|
221
|
-
const scaledImage = imageToResize.resize(prioritizeHeight ? {
|
|
204
|
+
const prioritizeHeight = resizeWidth / resizeHeight < originalWidth / originalHeight;
|
|
205
|
+
const scaledImage = originalImage.clone().resize(prioritizeHeight ? {
|
|
222
206
|
height: resizeHeight,
|
|
223
207
|
fit: sharp.fit.cover
|
|
224
208
|
} : {
|
|
@@ -296,9 +280,9 @@ function getCachedImage(pathname) {
|
|
|
296
280
|
}
|
|
297
281
|
function urlToFileName(url) {
|
|
298
282
|
const segments = url.split("?");
|
|
299
|
-
const path
|
|
283
|
+
const path = segments[0];
|
|
300
284
|
const query = segments[1];
|
|
301
|
-
let uniqueName = path
|
|
285
|
+
let uniqueName = path.replace("://", "_").replace(/\.|\/|\?|=/g, "_");
|
|
302
286
|
uniqueName = uniqueName.substring(0, uniqueName.lastIndexOf("_")) + (query ? "_" + query.replace(/\.|\/|\?|=/g, "_") : "") + "." + uniqueName.substring(uniqueName.lastIndexOf("_") + 1, uniqueName.length);
|
|
303
287
|
return uniqueName;
|
|
304
288
|
}
|
|
@@ -310,15 +294,14 @@ function urlToCachedName(options) {
|
|
|
310
294
|
const parsedName = path.parse(fileName);
|
|
311
295
|
const cacheDir = isVercel() ? path.join("/tmp", ".cache") : path.join(process.cwd(), ".cache");
|
|
312
296
|
if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir);
|
|
313
|
-
|
|
314
|
-
return cachedName;
|
|
297
|
+
return options.h ? path.join(cacheDir, `${parsedName.name}_w_${options.w}_h_${options.h || 0}_x_${options.x || IMAGE_FOCAL_X}_y_${options.y || IMAGE_FOCAL_Y}_q_${options.q}${parsedName.ext}`) : path.join(cacheDir, `${parsedName.name}_w_${options.w}_q_${options.q}${parsedName.ext}`);
|
|
315
298
|
}
|
|
316
299
|
function isInvalidDomain(src, domains) {
|
|
317
300
|
if (IMAGE_STRATEGY === ImageStrategy.Token) return false;
|
|
318
301
|
const url = new URL(src);
|
|
319
302
|
return !domains.includes(url.hostname);
|
|
320
303
|
}
|
|
321
|
-
|
|
322
304
|
//#endregion
|
|
323
305
|
export { imageGetHandler, imageHandler };
|
|
306
|
+
|
|
324
307
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["handler: IApiHandler","request: NextApiRequest","response: NextApiResponse","method: ICrudMethod","error: any","IMAGE_STRATEGY: ImageStrategy","request: NextApiRequest","response: NextApiResponse","imageOptions: IImageOptions","error: any","request: NextRequest","context: IImageHandlerNextContext","src: string","referer?: string | null","options: IImageHandlerOptions","query: Partial<Record<'origin' | 'search' | 'w' | 'h' | 'x' | 'y' | 'q', string | string[] | undefined>> & {\r\n slugs?: string[];\r\n }","query: Partial<Record<'origin' | 'search' | 't', string | string[] | undefined>> & {\r\n slugs?: string[];\r\n }","originalImage: sharp.Sharp","url: string","domains: string[]","data: Buffer<ArrayBufferLike>","status: number","message: unknown","pathname: string","path","options: IImageOptions"],"sources":["../src/api/api.handler.ts","../src/image/image.handler.ts"],"sourcesContent":["import { NextApiRequest, NextApiResponse } from 'next';\r\n\r\nexport type ICrudMethod = 'get' | 'post' | 'put' | 'patch' | 'delete' | string;\r\n\r\nexport type IHandler = (request: NextApiRequest, response: NextApiResponse) => Promise<any>;\r\n\r\nexport type IApiHandler = Record<ICrudMethod, IHandler>;\r\n\r\nexport function apiHandler(handler: IApiHandler): any {\r\n return async (request: NextApiRequest, response: NextApiResponse): Promise<any> => {\r\n const method: ICrudMethod = (request.method || 'GET').toLowerCase() as ICrudMethod;\r\n // check handler supports HTTP method\r\n if (!handler[method]) {\r\n return response.status(405).end(`Method ${method} Not Allowed`);\r\n }\r\n try {\r\n // global middleware\r\n // await jwtMiddleware(request, response);\r\n // route handler\r\n await handler[method](request, response);\r\n } catch (error) {\r\n // console.log('error', error);\r\n // global error handler\r\n return errorHandler(error, response);\r\n }\r\n };\r\n}\r\n\r\nexport function errorHandler(error: any, response: NextApiResponse): Promise<any> | void {\r\n if (typeof (error) === 'string') {\r\n // custom application error\r\n const is404 = error.toLowerCase().endsWith('not found');\r\n const statusCode = is404 ? 404 : 400;\r\n return response.status(statusCode).json({ message: error });\r\n }\r\n if (error.name === 'UnauthorizedError') {\r\n // jwt authentication error\r\n return response.status(401).json({ message: 'Invalid Token' });\r\n }\r\n // default to 500 server error\r\n console.error(error);\r\n return response.status(500).json({ message: error.message });\r\n}\r\n","// Importing the 'fs' module to work with the file system\r\nimport * as fs from 'fs';\r\n// Importing the 'NextApiRequest' and 'NextApiResponse' from 'next' for handling API requests and responses\r\nimport { NextApiRequest, NextApiResponse } from 'next';\r\nimport { NextRequest, NextResponse } from 'next/server';\r\n// Importing the 'path' module to work with file and directory paths\r\nimport * as path from 'path';\r\n// Importing 'sharp' for image processing\r\nimport sharp from 'sharp';\r\n// Importing 'apiHandler' from '../api/api.handler'\r\nimport { decryptImageResize, IImageResize, ImageStrategy } from '@websolutespa/bom-core';\r\nimport { apiHandler } from '../api/api.handler';\r\n\r\nconst IMAGE_STRATEGY: ImageStrategy = process.env.IMAGE_STRATEGY ?\r\n process.env.IMAGE_STRATEGY as ImageStrategy :\r\n ImageStrategy.Mixer;\r\n\r\nconst IMAGE_MAX_AGE = 604800;\r\nconst IMAGE_FOCAL_X = 50;\r\nconst IMAGE_FOCAL_Y = 50;\r\nconst IMAGE_QUALITY = 90;\r\n\r\nexport type IImageHandlerOptions = {\r\n domains: string[];\r\n sizes: number[];\r\n quality: number;\r\n};\r\n\r\nexport type IImageOptions = IImageResize & {\r\n origin: string;\r\n search: string;\r\n url: string;\r\n};\r\n\r\ntype IImageHandlerNextContext = {\r\n params: Promise<{ slugs?: string[] }> | { slugs?: string[] };\r\n};\r\n\r\nexport function imageHandler({\r\n domains = [],\r\n sizes = [],\r\n quality = IMAGE_QUALITY,\r\n}: IImageHandlerOptions) {\r\n // Returning the 'apiHandler' function with a configuration object\r\n return apiHandler({\r\n // Defining the 'get' method for handling GET requests\r\n get: async (request: NextApiRequest, response: NextApiResponse) => {\r\n try {\r\n let imageOptions: IImageOptions;\r\n try {\r\n imageOptions = (IMAGE_STRATEGY === ImageStrategy.Token ?\r\n getEncryptedImageOptions(request, { domains, sizes, quality }) :\r\n getImageOptions(request, { domains, sizes, quality }));\r\n } catch (error) {\r\n return response.status(400).json({\r\n errors: [{\r\n message: error,\r\n }],\r\n });\r\n }\r\n // Generating a cached name for the image\r\n const cachedName = urlToCachedName(imageOptions);\r\n // Checking if the cached image exists\r\n if (fs.existsSync(cachedName)) {\r\n // console.log('imageHandler.fromCache', cachedName);\r\n // Reading the cached image\r\n const cachedImage = await getCachedImage(cachedName);\r\n // Setting the 'Content-Type' header to 'image/webp'\r\n response.setHeader('Content-Type', 'image/webp');\r\n // Setting the 'Expires' header to the current time plus the maximum age of the image (in seconds)\r\n response.setHeader('Expires', new Date(Date.now() + IMAGE_MAX_AGE * 1000).toUTCString());\r\n // Setting the 'Cache-Control' header to 'public' and setting 'max-age' to the maximum age of the image\r\n response.setHeader('Cache-Control', `public, max-age=${IMAGE_MAX_AGE}`);\r\n // Ending the response and sending the cached image\r\n return response.send(cachedImage);\r\n }\r\n // Check referrer\r\n if (isInvalidReferer(request)) {\r\n return response.status(401).send('you are not authorized to perform this action');\r\n }\r\n // Temporarily fix Vercel local image issue\r\n if (imageOptions.url.indexOf('http') === -1 && isVercel()) {\r\n response.redirect(307, imageOptions.url);\r\n }\r\n // Reading the original image\r\n const originalImageBuffer = await getOriginalImage(imageOptions.url, domains);\r\n // Checking if the original image exists\r\n if (originalImageBuffer) {\r\n // Original image\r\n const originalImage = sharp(originalImageBuffer, {\r\n animated: isGIF(imageOptions.url),\r\n });\r\n // Resizing the original image and converting it to WebP format\r\n const resizedImage = await resizeImage(originalImage, imageOptions);\r\n // Converting resized image to WebP format\r\n const compressedImage = resizedImage\r\n .webp({\r\n quality: imageOptions.q,\r\n // nearLossless: true,\r\n });\r\n // Saving the resized image to the cache\r\n compressedImage.toFile(cachedName)\r\n .then(() => console.log('imageHandler.sharp.cached', cachedName))\r\n .catch((error: any) => console.error('imageHandler.sharp.error', cachedName, error));\r\n // Converting the resized image to a buffer\r\n const data = await compressedImage.toBuffer();\r\n // Setting the 'Content-Type' header to 'image/webp'\r\n response.setHeader('Content-Type', 'image/webp');\r\n // Setting the 'Expires' header to the current time plus the maximum age of the image (in seconds)\r\n response.setHeader('Expires', new Date(Date.now() + IMAGE_MAX_AGE * 1000).toUTCString());\r\n // Setting the 'Cache-Control' header to 'public' and setting 'max-age' to the maximum age of the image\r\n response.setHeader('Cache-Control', `public, max-age=${IMAGE_MAX_AGE}`);\r\n // Sending data in the response\r\n return response.end(data);\r\n }\r\n } catch (error: any) {\r\n // Logging an error message and throwing the error\r\n console.error('imageHandler.error', error);\r\n return response.status(error.status || 500).json({\r\n errors: [{\r\n message: error.message || error.name || error.statusText || 'An error occurred',\r\n }],\r\n });\r\n }\r\n },\r\n });\r\n}\r\n\r\nexport function imageGetHandler({\r\n domains = [],\r\n sizes = [],\r\n quality = IMAGE_QUALITY,\r\n}: IImageHandlerOptions) {\r\n return async (request: NextRequest, context: IImageHandlerNextContext): Promise<Response> => {\r\n try {\r\n let imageOptions: IImageOptions;\r\n try {\r\n imageOptions = (IMAGE_STRATEGY === ImageStrategy.Token ?\r\n await getEncryptedImageOptionsNext(request, context, { domains, sizes, quality }) :\r\n await getImageOptionsNext(request, context, { domains, sizes, quality }));\r\n } catch (error) {\r\n return getJsonErrorResponse(400, error);\r\n }\r\n const cachedName = urlToCachedName(imageOptions);\r\n if (fs.existsSync(cachedName)) {\r\n const cachedImage = await getCachedImage(cachedName);\r\n return getImageResponse(cachedImage);\r\n }\r\n if (isInvalidRefererValue(request.headers.get('referer'))) {\r\n return new Response('you are not authorized to perform this action', {\r\n status: 401,\r\n });\r\n }\r\n if (imageOptions.url.indexOf('http') === -1 && isVercel()) {\r\n return NextResponse.redirect(new URL(imageOptions.url, request.url), 307);\r\n }\r\n const originalImageBuffer = await getOriginalImage(imageOptions.url, domains);\r\n if (originalImageBuffer) {\r\n const originalImage = sharp(originalImageBuffer, {\r\n animated: isGIF(imageOptions.url),\r\n });\r\n const resizedImage = await resizeImage(originalImage, imageOptions);\r\n const compressedImage = resizedImage\r\n .webp({\r\n quality: imageOptions.q,\r\n });\r\n compressedImage.toFile(cachedName)\r\n .then(() => console.log('imageHandler.sharp.cached', cachedName))\r\n .catch((error: any) => console.error('imageHandler.sharp.error', cachedName, error));\r\n const data = await compressedImage.toBuffer();\r\n return getImageResponse(data);\r\n }\r\n return getJsonErrorResponse(404, 'image not found');\r\n } catch (error: any) {\r\n console.error('imageHandler.error', error);\r\n return getJsonErrorResponse(\r\n error.status || 500,\r\n error.message || error.name || error.statusText || 'An error occurred'\r\n );\r\n }\r\n };\r\n}\r\n\r\nfunction isGIF(src: string): boolean {\r\n return src.indexOf('.gif') !== -1;\r\n}\r\n\r\nfunction isInvalidReferer(request: NextApiRequest): boolean {\r\n const { headers } = request;\r\n const referer = headers.referer;\r\n return isInvalidRefererValue(referer);\r\n}\r\n\r\nfunction isInvalidRefererValue(referer?: string | null): boolean {\r\n const publicUrl = process.env.NEXT_PUBLIC_URL || '';\r\n // console.log('referef', referer, 'publicUrl', publicUrl, 'IMAGE_STRATEGY', IMAGE_STRATEGY, ImageStrategy.Token);\r\n if (\r\n IMAGE_STRATEGY !== ImageStrategy.Token &&\r\n referer &&\r\n publicUrl !== 'https://' &&\r\n referer.indexOf(publicUrl) !== 0\r\n ) {\r\n return true;\r\n }\r\n return false;\r\n}\r\n\r\nasync function getImageOptionsNext(\r\n request: NextRequest,\r\n context: IImageHandlerNextContext,\r\n options: IImageHandlerOptions\r\n): Promise<IImageOptions> {\r\n const params = await context.params;\r\n const query = Object.fromEntries(request.nextUrl.searchParams.entries());\r\n return getImageOptionsFromQuery({\r\n ...query,\r\n slugs: params.slugs,\r\n }, options);\r\n}\r\n\r\nfunction getImageOptions(request: NextApiRequest, options: IImageHandlerOptions): IImageOptions {\r\n return getImageOptionsFromQuery(request.query, options);\r\n}\r\n\r\nfunction getImageOptionsFromQuery(\r\n query: Partial<Record<'origin' | 'search' | 'w' | 'h' | 'x' | 'y' | 'q', string | string[] | undefined>> & {\r\n slugs?: string[];\r\n },\r\n options: IImageHandlerOptions\r\n): IImageOptions {\r\n // Destructuring 'slugs', 'origin', 'search', 'w', 'h', 'x', 'y', 'q' from the request query\r\n const { slugs, origin = '', search = '', w, h, x, y, q } = query;\r\n // Checking slugs\r\n if (!Array.isArray(slugs) || slugs.length === 0) {\r\n // console.error('imageHandler', 'missing slugs parameter');\r\n throw 'missing slugs parameter';\r\n }\r\n // Parsing 'w' to an integer and assigning it to 'width'\r\n const width = typeof w === 'string' ? parseInt(w) : undefined;\r\n // Checking if 'width' is not defined\r\n if (!width) {\r\n // Logging an error message and returning a 400 status code with a message\r\n // console.error('imageHandler', 'missing w parameter');\r\n throw 'missing w parameter';\r\n }\r\n // if (width > 2048) {\r\n if (!options.sizes.includes(width)) {\r\n throw 'invalid w parameter';\r\n }\r\n // Parsing 'h' to an integer and assigning it to 'height'\r\n const height = typeof h === 'string' ? parseInt(h) : undefined;\r\n // Parsing 'x' to an integer and assigning it to 'focalX'\r\n const focalX = typeof x === 'string' ? parseInt(x) : IMAGE_FOCAL_X;\r\n // Parsing 'y' to an integer and assigning it to 'focalY'\r\n const focalY = typeof y === 'string' ? parseInt(y) : IMAGE_FOCAL_Y;\r\n // Parsing 'q' to an integer, ensuring it's between 30 and 100, and assigning it to 'imageQuality'\r\n const imageQuality = Math.max(30, Math.min(100, (typeof q === 'string' ? parseInt(q) : options.quality)));\r\n // if (width > 2048) {\r\n if (imageQuality !== options.quality) {\r\n throw 'invalid q parameter';\r\n }\r\n // Generating url\r\n const url = origin + '/' + slugs.join('/') + search;\r\n //\r\n return height ? {\r\n w: width,\r\n h: height,\r\n x: focalX,\r\n y: focalY,\r\n q: imageQuality,\r\n src: url,\r\n url: url,\r\n origin: String(origin),\r\n search: String(search),\r\n } : {\r\n w: width,\r\n q: imageQuality,\r\n src: url,\r\n url: url,\r\n origin: String(origin),\r\n search: String(search),\r\n };\r\n}\r\n\r\nasync function getEncryptedImageOptionsNext(\r\n request: NextRequest,\r\n context: IImageHandlerNextContext,\r\n options: IImageHandlerOptions\r\n): Promise<IImageOptions> {\r\n const params = await context.params;\r\n const query = Object.fromEntries(request.nextUrl.searchParams.entries());\r\n return getEncryptedImageOptionsFromQuery({\r\n ...query,\r\n slugs: params.slugs,\r\n }, options);\r\n}\r\n\r\nfunction getEncryptedImageOptions(request: NextApiRequest, options: IImageHandlerOptions): IImageOptions {\r\n return getEncryptedImageOptionsFromQuery(request.query, options);\r\n}\r\n\r\nfunction getEncryptedImageOptionsFromQuery(\r\n query: Partial<Record<'origin' | 'search' | 't', string | string[] | undefined>> & {\r\n slugs?: string[];\r\n },\r\n options: IImageHandlerOptions\r\n): IImageOptions {\r\n // Destructuring 'slugs', 'origin', 'search', 't' from the request query\r\n const { slugs, origin = '', search = '', t } = query;\r\n // Checking slugs\r\n if (!Array.isArray(slugs) || slugs.length === 0) {\r\n // console.error('imageHandler', 'missing slugs parameter');\r\n throw 'missing slugs parameter';\r\n }\r\n if (typeof t !== 'string') {\r\n throw 'missing t parameter';\r\n }\r\n const imageResize = decryptImageResize(t);\r\n /*\r\n // Checking if 'w' is not defined\r\n if (!imageResize.w) {\r\n throw 'missing w parameter';\r\n }\r\n // if (width > 2048) {\r\n if (!options.sizes.includes(imageResize.w)) {\r\n throw 'invalid w parameter';\r\n }\r\n // Ensuring 'q' is between 30 and 100, and assigning it to 'imageQuality'\r\n const imageQuality = Math.max(30, Math.min(100, imageResize.q || options.quality));\r\n if (imageQuality !== options.quality) {\r\n throw 'invalid q parameter';\r\n }\r\n */\r\n imageResize.src = decodeURIComponent(imageResize.src);\r\n // Generating url\r\n const url = decodeURIComponent(origin + '/' + slugs.join('/') + search);\r\n //\r\n if (imageResize.src !== url) {\r\n throw `invalid src parameter ${imageResize.src} ${url}`;\r\n }\r\n //\r\n return {\r\n ...imageResize,\r\n url: url,\r\n origin: String(origin),\r\n search: String(search),\r\n };\r\n}\r\n\r\nasync function resizeImage(originalImage: sharp.Sharp, imageOptions: IImageOptions): Promise<sharp.Sharp> {\r\n const { w: resizeWidth, h: resizeHeight, x: focalX = IMAGE_FOCAL_X, y: focalY = IMAGE_FOCAL_Y } = imageOptions;\r\n const defaultResizeOptions = () => {\r\n return originalImage.resize({\r\n width: resizeWidth,\r\n height: resizeHeight ? resizeHeight : undefined,\r\n fit: sharp.fit.cover,\r\n position: sharp.strategy.attention,\r\n withoutEnlargement: true,\r\n });\r\n };\r\n if (resizeHeight && (focalX !== IMAGE_FOCAL_X || focalY !== IMAGE_FOCAL_Y)) {\r\n const { width: originalWidth, height: originalHeight } = await originalImage.metadata();\r\n if (originalWidth && originalHeight) {\r\n const resizeAspectRatio = resizeWidth / resizeHeight;\r\n const originalAspectRatio = originalWidth / originalHeight;\r\n const prioritizeHeight = resizeAspectRatio < originalAspectRatio;\r\n const imageToResize = originalImage.clone();\r\n // Scale the image up or down to fit the resize dimensions\r\n const scaledImage = imageToResize.resize(prioritizeHeight ? {\r\n height: resizeHeight,\r\n fit: sharp.fit.cover,\r\n } : {\r\n width: resizeWidth,\r\n fit: sharp.fit.cover,\r\n });\r\n const { info: scaledImageInfo } = await scaledImage.toBuffer({ resolveWithObject: true });\r\n // extractWidth\r\n const extractWidth = resizeWidth ?? scaledImageInfo.width;\r\n const maxOffsetX = scaledImageInfo.width - extractWidth;\r\n const leftFocalEdge = Math.round(\r\n scaledImageInfo.width * (focalX / 100) - extractWidth / 2\r\n );\r\n // extractX\r\n const extractX = Math.min(Math.max(0, leftFocalEdge), maxOffsetX);\r\n // extractHeight\r\n const extractHeight = resizeHeight ?? scaledImageInfo.height;\r\n const maxOffsetY = scaledImageInfo.height - extractHeight;\r\n const topFocalEdge = Math.round(\r\n scaledImageInfo.height * (focalY / 100) - extractHeight / 2\r\n );\r\n // extractY\r\n const extractY = Math.min(Math.max(0, topFocalEdge), maxOffsetY);\r\n // Extract the focal area from the scaled image\r\n return scaledImage.extract({\r\n left: extractX,\r\n top: extractY,\r\n width: extractWidth,\r\n height: extractHeight,\r\n });\r\n } else {\r\n return defaultResizeOptions();\r\n }\r\n } else {\r\n return defaultResizeOptions();\r\n }\r\n}\r\n\r\n// Defining the 'getOriginalImage' function\r\nfunction getOriginalImage(url: string, domains: string[]): Promise<Buffer> {\r\n // Returning a new promise\r\n return new Promise((resolve, reject) => {\r\n // Checking if 'url' starts with 'http'\r\n if (url.indexOf('http') === 0) {\r\n if (isInvalidDomain(url, domains)) {\r\n return reject({ status: 401, message: 'invalid origin' });\r\n }\r\n // Fetching the image from the URL\r\n fetch(url, {\r\n method: 'GET',\r\n mode: 'cors',\r\n headers: {\r\n 'Access-Control-Allow-Origin': '*',\r\n },\r\n })\r\n .then(response => {\r\n if (!response.ok) {\r\n return reject({ status: response.status, message: response.statusText });\r\n }\r\n response.arrayBuffer().then(buffer => {\r\n return resolve(Buffer.from(buffer));\r\n }).catch(error => {\r\n return reject(error);\r\n });\r\n })\r\n .catch(error => {\r\n // Rejecting the promise with the error\r\n return reject(error);\r\n });\r\n } else {\r\n // Reading the image from the file system\r\n const filePath = path.join(process.cwd(), 'public', url);\r\n // console.log('imageHandler.getOriginalImage.filePath', filePath);\r\n fs.readFile(filePath, (error, data) => {\r\n if (error) {\r\n return reject(error);\r\n }\r\n // Resolving the promise with the image data\r\n return resolve(data);\r\n });\r\n }\r\n });\r\n}\r\n\r\nfunction getImageResponse(data: Buffer<ArrayBufferLike>): Response {\r\n return new Response(data as BodyInit, {\r\n headers: {\r\n 'Cache-Control': `public, max-age=${IMAGE_MAX_AGE}`,\r\n 'Content-Type': 'image/webp',\r\n Expires: new Date(Date.now() + IMAGE_MAX_AGE * 1000).toUTCString(),\r\n },\r\n });\r\n}\r\n\r\nfunction getJsonErrorResponse(status: number, message: unknown): Response {\r\n return Response.json({\r\n errors: [{\r\n message,\r\n }],\r\n }, {\r\n status,\r\n });\r\n}\r\n\r\n// Defining the 'getCachedImage' function\r\nfunction getCachedImage(pathname: string): Promise<Buffer> {\r\n // Returning a new promise\r\n return new Promise((resolve, reject) => {\r\n // Reading the image from the file system\r\n fs.readFile(pathname, (error, data) => {\r\n if (error) {\r\n // Rejecting the promise with the error\r\n return reject(error);\r\n }\r\n // Resolving the promise with the image data\r\n return resolve(data);\r\n });\r\n });\r\n}\r\n\r\n// Defining the 'urlToFileName' function\r\nfunction urlToFileName(url: string): string {\r\n // Replacing useless characters with UNDERSCORE\r\n const segments = url.split('?');\r\n const path = segments[0]!;\r\n const query = segments[1];\r\n let uniqueName = path.replace('://', '_').replace(/\\.|\\/|\\?|=/g, '_');\r\n // Replacing last UNDERSCORE with a DOT\r\n uniqueName = uniqueName.substring(0, uniqueName.lastIndexOf('_'))\r\n + (query ? '_' + query.replace(/\\.|\\/|\\?|=/g, '_') : '')\r\n + '.' + uniqueName.substring(uniqueName.lastIndexOf('_') + 1, uniqueName.length);\r\n // Returning the unique name\r\n return uniqueName;\r\n}\r\n\r\nfunction isVercel(): boolean {\r\n return process && process.env && String(process.env.VERCEL) === '1';\r\n}\r\n\r\n// Defining the 'urlToCachedName' function\r\nfunction urlToCachedName(options: IImageOptions): string {\r\n // Converting the URL to a file name\r\n const fileName = urlToFileName(options.url);\r\n // Parsing the file name\r\n const parsedName = path.parse(fileName);\r\n // Defining the cache directory\r\n const cacheDir = isVercel() ?\r\n path.join('/tmp', '.cache') :\r\n path.join(process.cwd(), '.cache');\r\n // Checking if the cache directory exists\r\n if (!fs.existsSync(cacheDir)) {\r\n // Creating the cache directory\r\n fs.mkdirSync(cacheDir);\r\n }\r\n // Generating the cached name\r\n const cachedName = options.h ?\r\n path.join(cacheDir, `${parsedName.name}_w_${options.w}_h_${options.h || 0}_x_${options.x || IMAGE_FOCAL_X}_y_${options.y || IMAGE_FOCAL_Y}_q_${options.q}${parsedName.ext}`) :\r\n path.join(cacheDir, `${parsedName.name}_w_${options.w}_q_${options.q}${parsedName.ext}`);\r\n // Returning the cached name\r\n return cachedName;\r\n}\r\n\r\nfunction isInvalidDomain(src: string, domains: string[]): boolean {\r\n if (IMAGE_STRATEGY === ImageStrategy.Token) {\r\n return false;\r\n }\r\n const url = new URL(src);\r\n return !domains.includes(url.hostname);\r\n}\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/api/api.handler.ts","../src/image/image.handler.ts"],"sourcesContent":["import { NextApiRequest, NextApiResponse } from 'next';\r\n\r\nexport type ICrudMethod = 'get' | 'post' | 'put' | 'patch' | 'delete' | string;\r\n\r\nexport type IHandler = (request: NextApiRequest, response: NextApiResponse) => Promise<any>;\r\n\r\nexport type IApiHandler = Record<ICrudMethod, IHandler>;\r\n\r\nexport function apiHandler(handler: IApiHandler): any {\r\n return async (request: NextApiRequest, response: NextApiResponse): Promise<any> => {\r\n const method: ICrudMethod = (request.method || 'GET').toLowerCase() as ICrudMethod;\r\n // check handler supports HTTP method\r\n if (!handler[method]) {\r\n return response.status(405).end(`Method ${method} Not Allowed`);\r\n }\r\n try {\r\n // global middleware\r\n // await jwtMiddleware(request, response);\r\n // route handler\r\n await handler[method](request, response);\r\n } catch (error) {\r\n // console.log('error', error);\r\n // global error handler\r\n return errorHandler(error, response);\r\n }\r\n };\r\n}\r\n\r\nexport function errorHandler(error: any, response: NextApiResponse): Promise<any> | void {\r\n if (typeof (error) === 'string') {\r\n // custom application error\r\n const is404 = error.toLowerCase().endsWith('not found');\r\n const statusCode = is404 ? 404 : 400;\r\n return response.status(statusCode).json({ message: error });\r\n }\r\n if (error.name === 'UnauthorizedError') {\r\n // jwt authentication error\r\n return response.status(401).json({ message: 'Invalid Token' });\r\n }\r\n // default to 500 server error\r\n console.error(error);\r\n return response.status(500).json({ message: error.message });\r\n}\r\n","// Importing the 'fs' module to work with the file system\r\nimport * as fs from 'fs';\r\n// Importing the 'NextApiRequest' and 'NextApiResponse' from 'next' for handling API requests and responses\r\nimport { NextApiRequest, NextApiResponse } from 'next';\r\nimport { NextRequest, NextResponse } from 'next/server';\r\n// Importing the 'path' module to work with file and directory paths\r\nimport * as path from 'path';\r\n// Importing 'sharp' for image processing\r\nimport sharp from 'sharp';\r\n// Importing 'apiHandler' from '../api/api.handler'\r\nimport { decryptImageResize, IImageResize, ImageStrategy } from '@websolutespa/bom-core';\r\nimport { apiHandler } from '../api/api.handler';\r\n\r\nconst IMAGE_STRATEGY: ImageStrategy = process.env.IMAGE_STRATEGY ?\r\n process.env.IMAGE_STRATEGY as ImageStrategy :\r\n ImageStrategy.Mixer;\r\n\r\nconst IMAGE_MAX_AGE = 604800;\r\nconst IMAGE_FOCAL_X = 50;\r\nconst IMAGE_FOCAL_Y = 50;\r\nconst IMAGE_QUALITY = 90;\r\n\r\nexport type IImageHandlerOptions = {\r\n domains: string[];\r\n sizes: number[];\r\n quality: number;\r\n};\r\n\r\nexport type IImageOptions = IImageResize & {\r\n origin: string;\r\n search: string;\r\n url: string;\r\n};\r\n\r\ntype IImageHandlerNextContext = {\r\n params: Promise<{ slugs?: string[] }> | { slugs?: string[] };\r\n};\r\n\r\nexport function imageHandler({\r\n domains = [],\r\n sizes = [],\r\n quality = IMAGE_QUALITY,\r\n}: IImageHandlerOptions) {\r\n // Returning the 'apiHandler' function with a configuration object\r\n return apiHandler({\r\n // Defining the 'get' method for handling GET requests\r\n get: async (request: NextApiRequest, response: NextApiResponse) => {\r\n try {\r\n let imageOptions: IImageOptions;\r\n try {\r\n imageOptions = (IMAGE_STRATEGY === ImageStrategy.Token ?\r\n getEncryptedImageOptions(request, { domains, sizes, quality }) :\r\n getImageOptions(request, { domains, sizes, quality }));\r\n } catch (error) {\r\n return response.status(400).json({\r\n errors: [{\r\n message: error,\r\n }],\r\n });\r\n }\r\n // Generating a cached name for the image\r\n const cachedName = urlToCachedName(imageOptions);\r\n // Checking if the cached image exists\r\n if (fs.existsSync(cachedName)) {\r\n // console.log('imageHandler.fromCache', cachedName);\r\n // Reading the cached image\r\n const cachedImage = await getCachedImage(cachedName);\r\n // Setting the 'Content-Type' header to 'image/webp'\r\n response.setHeader('Content-Type', 'image/webp');\r\n // Setting the 'Expires' header to the current time plus the maximum age of the image (in seconds)\r\n response.setHeader('Expires', new Date(Date.now() + IMAGE_MAX_AGE * 1000).toUTCString());\r\n // Setting the 'Cache-Control' header to 'public' and setting 'max-age' to the maximum age of the image\r\n response.setHeader('Cache-Control', `public, max-age=${IMAGE_MAX_AGE}`);\r\n // Ending the response and sending the cached image\r\n return response.send(cachedImage);\r\n }\r\n // Check referrer\r\n if (isInvalidReferer(request)) {\r\n return response.status(401).send('you are not authorized to perform this action');\r\n }\r\n // Temporarily fix Vercel local image issue\r\n if (imageOptions.url.indexOf('http') === -1 && isVercel()) {\r\n response.redirect(307, imageOptions.url);\r\n }\r\n // Reading the original image\r\n const originalImageBuffer = await getOriginalImage(imageOptions.url, domains);\r\n // Checking if the original image exists\r\n if (originalImageBuffer) {\r\n // Original image\r\n const originalImage = sharp(originalImageBuffer, {\r\n animated: isGIF(imageOptions.url),\r\n });\r\n // Resizing the original image and converting it to WebP format\r\n const resizedImage = await resizeImage(originalImage, imageOptions);\r\n // Converting resized image to WebP format\r\n const compressedImage = resizedImage\r\n .webp({\r\n quality: imageOptions.q,\r\n // nearLossless: true,\r\n });\r\n // Saving the resized image to the cache\r\n compressedImage.toFile(cachedName)\r\n .then(() => console.log('imageHandler.sharp.cached', cachedName))\r\n .catch((error: any) => console.error('imageHandler.sharp.error', cachedName, error));\r\n // Converting the resized image to a buffer\r\n const data = await compressedImage.toBuffer();\r\n // Setting the 'Content-Type' header to 'image/webp'\r\n response.setHeader('Content-Type', 'image/webp');\r\n // Setting the 'Expires' header to the current time plus the maximum age of the image (in seconds)\r\n response.setHeader('Expires', new Date(Date.now() + IMAGE_MAX_AGE * 1000).toUTCString());\r\n // Setting the 'Cache-Control' header to 'public' and setting 'max-age' to the maximum age of the image\r\n response.setHeader('Cache-Control', `public, max-age=${IMAGE_MAX_AGE}`);\r\n // Sending data in the response\r\n return response.end(data);\r\n }\r\n } catch (error: any) {\r\n // Logging an error message and throwing the error\r\n console.error('imageHandler.error', error);\r\n return response.status(error.status || 500).json({\r\n errors: [{\r\n message: error.message || error.name || error.statusText || 'An error occurred',\r\n }],\r\n });\r\n }\r\n },\r\n });\r\n}\r\n\r\nexport function imageGetHandler({\r\n domains = [],\r\n sizes = [],\r\n quality = IMAGE_QUALITY,\r\n}: IImageHandlerOptions) {\r\n return async (request: NextRequest, context: IImageHandlerNextContext): Promise<Response> => {\r\n try {\r\n let imageOptions: IImageOptions;\r\n try {\r\n imageOptions = (IMAGE_STRATEGY === ImageStrategy.Token ?\r\n await getEncryptedImageOptionsNext(request, context, { domains, sizes, quality }) :\r\n await getImageOptionsNext(request, context, { domains, sizes, quality }));\r\n } catch (error) {\r\n return getJsonErrorResponse(400, error);\r\n }\r\n const cachedName = urlToCachedName(imageOptions);\r\n if (fs.existsSync(cachedName)) {\r\n const cachedImage = await getCachedImage(cachedName);\r\n return getImageResponse(cachedImage);\r\n }\r\n if (isInvalidRefererValue(request.headers.get('referer'))) {\r\n return new Response('you are not authorized to perform this action', {\r\n status: 401,\r\n });\r\n }\r\n if (imageOptions.url.indexOf('http') === -1 && isVercel()) {\r\n return NextResponse.redirect(new URL(imageOptions.url, request.url), 307);\r\n }\r\n const originalImageBuffer = await getOriginalImage(imageOptions.url, domains);\r\n if (originalImageBuffer) {\r\n const originalImage = sharp(originalImageBuffer, {\r\n animated: isGIF(imageOptions.url),\r\n });\r\n const resizedImage = await resizeImage(originalImage, imageOptions);\r\n const compressedImage = resizedImage\r\n .webp({\r\n quality: imageOptions.q,\r\n });\r\n compressedImage.toFile(cachedName)\r\n .then(() => console.log('imageHandler.sharp.cached', cachedName))\r\n .catch((error: any) => console.error('imageHandler.sharp.error', cachedName, error));\r\n const data = await compressedImage.toBuffer();\r\n return getImageResponse(data);\r\n }\r\n return getJsonErrorResponse(404, 'image not found');\r\n } catch (error: any) {\r\n console.error('imageHandler.error', error);\r\n return getJsonErrorResponse(\r\n error.status || 500,\r\n error.message || error.name || error.statusText || 'An error occurred'\r\n );\r\n }\r\n };\r\n}\r\n\r\nfunction isGIF(src: string): boolean {\r\n return src.indexOf('.gif') !== -1;\r\n}\r\n\r\nfunction isInvalidReferer(request: NextApiRequest): boolean {\r\n const { headers } = request;\r\n const referer = headers.referer;\r\n return isInvalidRefererValue(referer);\r\n}\r\n\r\nfunction isInvalidRefererValue(referer?: string | null): boolean {\r\n const publicUrl = process.env.NEXT_PUBLIC_URL || '';\r\n // console.log('referef', referer, 'publicUrl', publicUrl, 'IMAGE_STRATEGY', IMAGE_STRATEGY, ImageStrategy.Token);\r\n if (\r\n IMAGE_STRATEGY !== ImageStrategy.Token &&\r\n referer &&\r\n publicUrl !== 'https://' &&\r\n referer.indexOf(publicUrl) !== 0\r\n ) {\r\n return true;\r\n }\r\n return false;\r\n}\r\n\r\nasync function getImageOptionsNext(\r\n request: NextRequest,\r\n context: IImageHandlerNextContext,\r\n options: IImageHandlerOptions\r\n): Promise<IImageOptions> {\r\n const params = await context.params;\r\n const query = Object.fromEntries(request.nextUrl.searchParams.entries());\r\n return getImageOptionsFromQuery({\r\n ...query,\r\n slugs: params.slugs,\r\n }, options);\r\n}\r\n\r\nfunction getImageOptions(request: NextApiRequest, options: IImageHandlerOptions): IImageOptions {\r\n return getImageOptionsFromQuery(request.query, options);\r\n}\r\n\r\nfunction getImageOptionsFromQuery(\r\n query: Partial<Record<'origin' | 'search' | 'w' | 'h' | 'x' | 'y' | 'q', string | string[] | undefined>> & {\r\n slugs?: string[];\r\n },\r\n options: IImageHandlerOptions\r\n): IImageOptions {\r\n // Destructuring 'slugs', 'origin', 'search', 'w', 'h', 'x', 'y', 'q' from the request query\r\n const { slugs, origin = '', search = '', w, h, x, y, q } = query;\r\n // Checking slugs\r\n if (!Array.isArray(slugs) || slugs.length === 0) {\r\n // console.error('imageHandler', 'missing slugs parameter');\r\n throw 'missing slugs parameter';\r\n }\r\n // Parsing 'w' to an integer and assigning it to 'width'\r\n const width = typeof w === 'string' ? parseInt(w) : undefined;\r\n // Checking if 'width' is not defined\r\n if (!width) {\r\n // Logging an error message and returning a 400 status code with a message\r\n // console.error('imageHandler', 'missing w parameter');\r\n throw 'missing w parameter';\r\n }\r\n // if (width > 2048) {\r\n if (!options.sizes.includes(width)) {\r\n throw 'invalid w parameter';\r\n }\r\n // Parsing 'h' to an integer and assigning it to 'height'\r\n const height = typeof h === 'string' ? parseInt(h) : undefined;\r\n // Parsing 'x' to an integer and assigning it to 'focalX'\r\n const focalX = typeof x === 'string' ? parseInt(x) : IMAGE_FOCAL_X;\r\n // Parsing 'y' to an integer and assigning it to 'focalY'\r\n const focalY = typeof y === 'string' ? parseInt(y) : IMAGE_FOCAL_Y;\r\n // Parsing 'q' to an integer, ensuring it's between 30 and 100, and assigning it to 'imageQuality'\r\n const imageQuality = Math.max(30, Math.min(100, (typeof q === 'string' ? parseInt(q) : options.quality)));\r\n // if (width > 2048) {\r\n if (imageQuality !== options.quality) {\r\n throw 'invalid q parameter';\r\n }\r\n // Generating url\r\n const url = origin + '/' + slugs.join('/') + search;\r\n //\r\n return height ? {\r\n w: width,\r\n h: height,\r\n x: focalX,\r\n y: focalY,\r\n q: imageQuality,\r\n src: url,\r\n url: url,\r\n origin: String(origin),\r\n search: String(search),\r\n } : {\r\n w: width,\r\n q: imageQuality,\r\n src: url,\r\n url: url,\r\n origin: String(origin),\r\n search: String(search),\r\n };\r\n}\r\n\r\nasync function getEncryptedImageOptionsNext(\r\n request: NextRequest,\r\n context: IImageHandlerNextContext,\r\n options: IImageHandlerOptions\r\n): Promise<IImageOptions> {\r\n const params = await context.params;\r\n const query = Object.fromEntries(request.nextUrl.searchParams.entries());\r\n return getEncryptedImageOptionsFromQuery({\r\n ...query,\r\n slugs: params.slugs,\r\n }, options);\r\n}\r\n\r\nfunction getEncryptedImageOptions(request: NextApiRequest, options: IImageHandlerOptions): IImageOptions {\r\n return getEncryptedImageOptionsFromQuery(request.query, options);\r\n}\r\n\r\nfunction getEncryptedImageOptionsFromQuery(\r\n query: Partial<Record<'origin' | 'search' | 't', string | string[] | undefined>> & {\r\n slugs?: string[];\r\n },\r\n options: IImageHandlerOptions\r\n): IImageOptions {\r\n // Destructuring 'slugs', 'origin', 'search', 't' from the request query\r\n const { slugs, origin = '', search = '', t } = query;\r\n // Checking slugs\r\n if (!Array.isArray(slugs) || slugs.length === 0) {\r\n // console.error('imageHandler', 'missing slugs parameter');\r\n throw 'missing slugs parameter';\r\n }\r\n if (typeof t !== 'string') {\r\n throw 'missing t parameter';\r\n }\r\n const imageResize = decryptImageResize(t);\r\n /*\r\n // Checking if 'w' is not defined\r\n if (!imageResize.w) {\r\n throw 'missing w parameter';\r\n }\r\n // if (width > 2048) {\r\n if (!options.sizes.includes(imageResize.w)) {\r\n throw 'invalid w parameter';\r\n }\r\n // Ensuring 'q' is between 30 and 100, and assigning it to 'imageQuality'\r\n const imageQuality = Math.max(30, Math.min(100, imageResize.q || options.quality));\r\n if (imageQuality !== options.quality) {\r\n throw 'invalid q parameter';\r\n }\r\n */\r\n imageResize.src = decodeURIComponent(imageResize.src);\r\n // Generating url\r\n const url = decodeURIComponent(origin + '/' + slugs.join('/') + search);\r\n //\r\n if (imageResize.src !== url) {\r\n throw `invalid src parameter ${imageResize.src} ${url}`;\r\n }\r\n //\r\n return {\r\n ...imageResize,\r\n url: url,\r\n origin: String(origin),\r\n search: String(search),\r\n };\r\n}\r\n\r\nasync function resizeImage(originalImage: sharp.Sharp, imageOptions: IImageOptions): Promise<sharp.Sharp> {\r\n const { w: resizeWidth, h: resizeHeight, x: focalX = IMAGE_FOCAL_X, y: focalY = IMAGE_FOCAL_Y } = imageOptions;\r\n const defaultResizeOptions = () => {\r\n return originalImage.resize({\r\n width: resizeWidth,\r\n height: resizeHeight ? resizeHeight : undefined,\r\n fit: sharp.fit.cover,\r\n position: sharp.strategy.attention,\r\n withoutEnlargement: true,\r\n });\r\n };\r\n if (resizeHeight && (focalX !== IMAGE_FOCAL_X || focalY !== IMAGE_FOCAL_Y)) {\r\n const { width: originalWidth, height: originalHeight } = await originalImage.metadata();\r\n if (originalWidth && originalHeight) {\r\n const resizeAspectRatio = resizeWidth / resizeHeight;\r\n const originalAspectRatio = originalWidth / originalHeight;\r\n const prioritizeHeight = resizeAspectRatio < originalAspectRatio;\r\n const imageToResize = originalImage.clone();\r\n // Scale the image up or down to fit the resize dimensions\r\n const scaledImage = imageToResize.resize(prioritizeHeight ? {\r\n height: resizeHeight,\r\n fit: sharp.fit.cover,\r\n } : {\r\n width: resizeWidth,\r\n fit: sharp.fit.cover,\r\n });\r\n const { info: scaledImageInfo } = await scaledImage.toBuffer({ resolveWithObject: true });\r\n // extractWidth\r\n const extractWidth = resizeWidth ?? scaledImageInfo.width;\r\n const maxOffsetX = scaledImageInfo.width - extractWidth;\r\n const leftFocalEdge = Math.round(\r\n scaledImageInfo.width * (focalX / 100) - extractWidth / 2\r\n );\r\n // extractX\r\n const extractX = Math.min(Math.max(0, leftFocalEdge), maxOffsetX);\r\n // extractHeight\r\n const extractHeight = resizeHeight ?? scaledImageInfo.height;\r\n const maxOffsetY = scaledImageInfo.height - extractHeight;\r\n const topFocalEdge = Math.round(\r\n scaledImageInfo.height * (focalY / 100) - extractHeight / 2\r\n );\r\n // extractY\r\n const extractY = Math.min(Math.max(0, topFocalEdge), maxOffsetY);\r\n // Extract the focal area from the scaled image\r\n return scaledImage.extract({\r\n left: extractX,\r\n top: extractY,\r\n width: extractWidth,\r\n height: extractHeight,\r\n });\r\n } else {\r\n return defaultResizeOptions();\r\n }\r\n } else {\r\n return defaultResizeOptions();\r\n }\r\n}\r\n\r\n// Defining the 'getOriginalImage' function\r\nfunction getOriginalImage(url: string, domains: string[]): Promise<Buffer> {\r\n // Returning a new promise\r\n return new Promise((resolve, reject) => {\r\n // Checking if 'url' starts with 'http'\r\n if (url.indexOf('http') === 0) {\r\n if (isInvalidDomain(url, domains)) {\r\n return reject({ status: 401, message: 'invalid origin' });\r\n }\r\n // Fetching the image from the URL\r\n fetch(url, {\r\n method: 'GET',\r\n mode: 'cors',\r\n headers: {\r\n 'Access-Control-Allow-Origin': '*',\r\n },\r\n })\r\n .then(response => {\r\n if (!response.ok) {\r\n return reject({ status: response.status, message: response.statusText });\r\n }\r\n response.arrayBuffer().then(buffer => {\r\n return resolve(Buffer.from(buffer));\r\n }).catch(error => {\r\n return reject(error);\r\n });\r\n })\r\n .catch(error => {\r\n // Rejecting the promise with the error\r\n return reject(error);\r\n });\r\n } else {\r\n // Reading the image from the file system\r\n const filePath = path.join(process.cwd(), 'public', url);\r\n // console.log('imageHandler.getOriginalImage.filePath', filePath);\r\n fs.readFile(filePath, (error, data) => {\r\n if (error) {\r\n return reject(error);\r\n }\r\n // Resolving the promise with the image data\r\n return resolve(data);\r\n });\r\n }\r\n });\r\n}\r\n\r\nfunction getImageResponse(data: Buffer<ArrayBufferLike>): Response {\r\n return new Response(data as BodyInit, {\r\n headers: {\r\n 'Cache-Control': `public, max-age=${IMAGE_MAX_AGE}`,\r\n 'Content-Type': 'image/webp',\r\n Expires: new Date(Date.now() + IMAGE_MAX_AGE * 1000).toUTCString(),\r\n },\r\n });\r\n}\r\n\r\nfunction getJsonErrorResponse(status: number, message: unknown): Response {\r\n return Response.json({\r\n errors: [{\r\n message,\r\n }],\r\n }, {\r\n status,\r\n });\r\n}\r\n\r\n// Defining the 'getCachedImage' function\r\nfunction getCachedImage(pathname: string): Promise<Buffer> {\r\n // Returning a new promise\r\n return new Promise((resolve, reject) => {\r\n // Reading the image from the file system\r\n fs.readFile(pathname, (error, data) => {\r\n if (error) {\r\n // Rejecting the promise with the error\r\n return reject(error);\r\n }\r\n // Resolving the promise with the image data\r\n return resolve(data);\r\n });\r\n });\r\n}\r\n\r\n// Defining the 'urlToFileName' function\r\nfunction urlToFileName(url: string): string {\r\n // Replacing useless characters with UNDERSCORE\r\n const segments = url.split('?');\r\n const path = segments[0]!;\r\n const query = segments[1];\r\n let uniqueName = path.replace('://', '_').replace(/\\.|\\/|\\?|=/g, '_');\r\n // Replacing last UNDERSCORE with a DOT\r\n uniqueName = uniqueName.substring(0, uniqueName.lastIndexOf('_'))\r\n + (query ? '_' + query.replace(/\\.|\\/|\\?|=/g, '_') : '')\r\n + '.' + uniqueName.substring(uniqueName.lastIndexOf('_') + 1, uniqueName.length);\r\n // Returning the unique name\r\n return uniqueName;\r\n}\r\n\r\nfunction isVercel(): boolean {\r\n return process && process.env && String(process.env.VERCEL) === '1';\r\n}\r\n\r\n// Defining the 'urlToCachedName' function\r\nfunction urlToCachedName(options: IImageOptions): string {\r\n // Converting the URL to a file name\r\n const fileName = urlToFileName(options.url);\r\n // Parsing the file name\r\n const parsedName = path.parse(fileName);\r\n // Defining the cache directory\r\n const cacheDir = isVercel() ?\r\n path.join('/tmp', '.cache') :\r\n path.join(process.cwd(), '.cache');\r\n // Checking if the cache directory exists\r\n if (!fs.existsSync(cacheDir)) {\r\n // Creating the cache directory\r\n fs.mkdirSync(cacheDir);\r\n }\r\n // Generating the cached name\r\n const cachedName = options.h ?\r\n path.join(cacheDir, `${parsedName.name}_w_${options.w}_h_${options.h || 0}_x_${options.x || IMAGE_FOCAL_X}_y_${options.y || IMAGE_FOCAL_Y}_q_${options.q}${parsedName.ext}`) :\r\n path.join(cacheDir, `${parsedName.name}_w_${options.w}_q_${options.q}${parsedName.ext}`);\r\n // Returning the cached name\r\n return cachedName;\r\n}\r\n\r\nfunction isInvalidDomain(src: string, domains: string[]): boolean {\r\n if (IMAGE_STRATEGY === ImageStrategy.Token) {\r\n return false;\r\n }\r\n const url = new URL(src);\r\n return !domains.includes(url.hostname);\r\n}\r\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@websolutespa/bom-mixer-image",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Mixer Image module of the BOM Repository",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bom",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"url": "git://github.com/websolutespa/bom.git"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
|
-
"lint": "eslint
|
|
16
|
+
"lint": "eslint .",
|
|
17
17
|
"test": "vitest run",
|
|
18
18
|
"test-watch": "vitest",
|
|
19
19
|
"coverage:integration": "npm run test -- --coverage.enabled --coverage.provider=istanbul --coverage.all",
|
|
@@ -26,19 +26,22 @@
|
|
|
26
26
|
"dependencies": {},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"@websolutespa/bom-core": "^3.0.1",
|
|
29
|
-
"next": "16.
|
|
29
|
+
"next": "^16.3.3",
|
|
30
30
|
"sharp": "0.34.2"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/sharp": "^0.30.4",
|
|
34
|
-
"@
|
|
35
|
-
"@websolutespa/
|
|
36
|
-
"@websolutespa/
|
|
37
|
-
"@websolutespa/
|
|
38
|
-
"
|
|
34
|
+
"@vitest/coverage-istanbul": "5.0.5",
|
|
35
|
+
"@websolutespa/bom-cli": "^3.1.0",
|
|
36
|
+
"@websolutespa/eslint-config": "^3.1.0",
|
|
37
|
+
"@websolutespa/test": "^3.1.0",
|
|
38
|
+
"@websolutespa/tsconfig": "^3.1.0",
|
|
39
|
+
"eslint": "10.13.0",
|
|
39
40
|
"npm-run-all": "4.1.5",
|
|
40
41
|
"sharp": "0.34.2",
|
|
41
|
-
"
|
|
42
|
+
"tsdown": "0.21.0",
|
|
43
|
+
"typescript": "6.0.3",
|
|
44
|
+
"vitest": "5.0.5"
|
|
42
45
|
},
|
|
43
46
|
"publishConfig": {
|
|
44
47
|
"access": "public"
|