diff --git a/imgproxy-signer.mjs b/imgproxy-signer.mjs new file mode 100644 index 0000000..e8afeba --- /dev/null +++ b/imgproxy-signer.mjs @@ -0,0 +1,53 @@ +import crypto from 'node:crypto'; + +export function createImgproxySigner(key, salt) { + const keyBuf = Buffer.from(key, 'hex'); + const saltBuf = Buffer.from(salt, 'hex'); + + function signPath(path) { + const signaturePayload = Buffer.concat([saltBuf, Buffer.from(path)]); + const signature = crypto.createHmac('sha256', keyBuf).update(signaturePayload).digest(); + const signatureB64 = signature.toString('base64url'); + return `/${signatureB64}${path}`; + } + + function buildUrl(baseUrl, storagePath, preset = 'display') { + if (!baseUrl || !storagePath) { + throw new Error('baseUrl and storagePath are required'); + } + + let width, height, quality, format; + switch (preset) { + case 'vision': + width = 768; + height = 768; + quality = 60; + format = 'jpg'; + break; + case 'thumb': + width = 256; + height = 256; + quality = 70; + format = 'jpg'; + break; + case 'display': + default: + width = 1280; + height = 1280; + quality = 85; + format = 'jpg'; + break; + } + + const encodedPath = encodeURIComponent(`local://${storagePath}`); + const unsignedPath = `/unsafe/${width}x${height}/${quality}/${format}/${encodedPath}`; + const signedPath = signPath(unsignedPath); + + const baseUrlNormalized = String(baseUrl).replace(/\/$/, ''); + return `${baseUrlNormalized}${signedPath}`; + } + + return { buildUrl, signPath }; +} + +export default createImgproxySigner;