image
image copied to clipboard
Add support provider "imgproxy"
It's free, fast and opensource stadealone server for resizising images.
https://imgproxy.net/
I'm currently testing my custom version of the provider, maybe it will help the developers. @pi0
$ yarn add create-hmac
nuxt.config.js
export default {
// ....
image: {
providers: {
customProvider: {
name: 'imgproxy',
provider: './src/providers/imgproxy.js',
options: {
baseURL: 'http://localhost:4000',
key: 'xxxxxxxxxxxxxx',
salt: 'xxxxxxxxxxxxxx',
}
}
},
provider: 'imgproxy',
},
// ....
}
imgproxy.js
import {joinURL} from 'ufo';
import createHmac from 'create-hmac';
import {createOperationsGenerator} from '~image'
const operationsGenerator = createOperationsGenerator({
keyMap: {
resize: 'rs',
size: 's',
fit: 'rt',
width: 'w',
height: 'h',
dpr: 'dpr',
enlarge: 'el',
extend: 'ex',
gravity: 'g',
crop: 'c',
padding: 'pd',
trim: 't',
rotate: 'rot',
quality: 'q',
maxBytes: 'mb',
background: 'bg',
backgroundAlpha: 'bga',
blur: 'bl',
sharpen: 'sh',
watermark: 'wm',
preset: 'pr',
cacheBuster: 'cb',
stripMetadata: 'sm',
stripColorProfile: 'scp',
autoRotate: 'ar',
filename: 'fn',
format: 'f',
},
formatter: (key, value) => `${key}:${value}`
})
function urlSafeBase64(string) {
return Buffer.from(string)
.toString('base64')
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_')
}
const hexDecode = (hex) => Buffer.from(hex, 'hex')
function sign(salt, target, secret) {
const hmac = createHmac('sha256', hexDecode(secret))
hmac.update(hexDecode(salt))
hmac.update(target)
return urlSafeBase64(hmac.digest())
}
const defaultModifiers = {
fit: 'fill',
width: 0,
height: 0,
gravity: 'no',
enlarge: 1,
format: 'webp',
}
export function getImage(src, {modifiers, baseURL, key, salt} = {}) {
const mergeModifiers = {...defaultModifiers, ...modifiers}
const encodedUrl = urlSafeBase64(src)
const path = joinURL('/', operationsGenerator(mergeModifiers), encodedUrl)
const signature = sign(salt, path, key)
return {
url: joinURL(baseURL, signature, path)
}
}
Example of usage
<template>
<div class="md:container md:mx-auto md:max-w-screen-lg">
<nuxt-img src="https://cc.cz/wp-content/uploads/2021/08/messi-psg.jpg" />
</div>
</template>
<script>
export default {
name: 'Homepage'
}
</script>
Cheers! 🍷 ⚽
Thanks, but what is createOperationsGenerator
? How does this function look like?
I found
import { createOperationsGenerator } from '@nuxt/image/dist/runtime/utils/index';
Thanks a lot!
Update for nuxt 3:
create-hmac doesnt work fine with vite, thats why easiest way it is create-hmac → hash.js
function sign(salt: string, target: string, secret: string) {
const hmac = hash.hmac(hash.sha256, hexDecode(secret));
hmac.update(hexDecode(salt));
hmac.update(target);
return urlSafeBase64(hmac.digest());
}
And you should yarn add buffer
and import { Buffer } from 'buffer'
for isomorph Buffer
Full version of imageproxy provider for nuxt3 and nuxt/image v1
import { joinURL } from "ufo";
import { createOperationsGenerator } from "@nuxt/image-edge/dist/runtime/utils/index";
import { ProviderGetImage } from "@nuxt/image-edge";
import { Buffer } from "buffer";
import * as hash from "hash.js";
const operationsGenerator = createOperationsGenerator({
keyMap: {
resize: "rs",
size: "s",
fit: "rt",
width: "w",
height: "h",
dpr: "dpr",
enlarge: "el",
extend: "ex",
gravity: "g",
crop: "c",
padding: "pd",
trim: "t",
rotate: "rot",
quality: "q",
maxBytes: "mb",
background: "bg",
backgroundAlpha: "bga",
blur: "bl",
sharpen: "sh",
watermark: "wm",
preset: "pr",
cacheBuster: "cb",
stripMetadata: "sm",
stripColorProfile: "scp",
autoRotate: "ar",
filename: "fn",
format: "f",
},
formatter: (key, value) => `${key}:${value}`,
});
function urlSafeBase64(string) {
return Buffer.from(string, "utf8")
.toString("base64")
.replace(/=/g, "")
.replace(/\+/g, "-")
.replace(/\//g, "_");
}
const hexDecode = (hex: string) => Buffer.from(hex, "hex");
function sign(salt: string, target: string, secret: string) {
const hmac = hash.hmac(hash.sha256, hexDecode(secret));
hmac.update(hexDecode(salt));
hmac.update(target);
return urlSafeBase64(hmac.digest());
}
const defaultModifiers = {
fit: "fill",
width: 0,
height: 0,
gravity: "no",
enlarge: 1,
format: "webp",
};
export const getImage: ProviderGetImage = (src, options) => {
const { modifiers, imgProxyUrl, imgProxySalt, imgProxyKey } = options;
const mergeModifiers = { ...defaultModifiers, ...modifiers };
const encodedUrl = urlSafeBase64(src);
const path = joinURL("/", operationsGenerator(mergeModifiers), encodedUrl);
const signature = sign(imgProxySalt, path, imgProxyKey);
return {
url: joinURL(imgProxyUrl, signature, path),
};
};
Hello @adinvadim, Where should I add the config that you have shared ? Is it still working?
Hi! @casualmatt
Add this file to any directory and add this configuration to nuxt.config.ts
image: {
providers: {
imgproxy: {
name: "imgproxy",
provider: "~~/utils/nuxt-image/imgproxy.provider",
options: {
imgProxyUrl: ...,
imgProxyKey: ...,
imgProxySalt: ...,
},
},
},
},

Hi @adinvadim. How does getSafeConfigEnv look like?
@Sevochka it is just my simple utils for working with env variables. May be is not great, because I am using this code from my nuxt 2 codebase.
export const getSafeEnv = (key: string, defaultValue?: string) => {
const result = process.env[key];
if (result != null) {
return result;
} else if (defaultValue !== undefined) {
return defaultValue;
} else {
throw new Error(`Env variable "${key}" is required`);
}
};
export const getSafeConfigEnv = (key: string, defaultValue?: string) => {
try {
return getSafeEnv(key, defaultValue);
} catch (e) {
if (isBuild) {
return null;
} else {
throw e;
}
}
};
I would welcome a PR to add an imgproxy
provider. 🙏
Given the code provided above, I imagine it would be a straightforward implementation...
@danielroe I have mine working as the custom provider; I can open a PR; the only question I have is how to sign the URL without publishing the key so that imgproxy can't be exploited, right now I allow on imgproxy just my domain as source but I don't think is a clean way to do it.
Perhaps we can run a docker container within the github actions workflow?