Skip to content

Commit

Permalink
feat(provider): imgproxy
Browse files Browse the repository at this point in the history
resolves #378
  • Loading branch information
shadow81627 committed Aug 14, 2021
1 parent 026be95 commit 2cb8fc4
Show file tree
Hide file tree
Showing 11 changed files with 17,138 additions and 12,717 deletions.
22 changes: 22 additions & 0 deletions docs/content/en/4.providers/imgproxy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
title: Imgproxy Provider
description: 'Nuxt Image has first class integration with Imgproxy'
navigation:
title: Imgproxy
---

Integration between [Imgproxy](https://imgproxy.net/) and the image module.

To use this provider you just need to specify the base url of your service in Imgproxy.

```js{}[nuxt.config.js]
export default {
image: {
imgix: {
baseURL: 'http://imgproxy.example.com',
key: 'xxxxxxxxxxxxxx',
salt: 'xxxxxxxxxxxxxx'
}
}
}
```
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
},
"dependencies": {
"consola": "^2.15.3",
"create-hmac": "^1.1.7",
"defu": "^5.0.0",
"fs-extra": "^10.0.0",
"hasha": "^5.2.2",
Expand All @@ -49,6 +50,7 @@
"@nuxt/typescript-build": "latest",
"@nuxt/typescript-runtime": "latest",
"@nuxtjs/eslint-config-typescript": "latest",
"@types/create-hmac": "^1.1.0",
"@types/fs-extra": "latest",
"@types/jest": "latest",
"@types/lru-cache": "latest",
Expand Down
5 changes: 5 additions & 0 deletions playground/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ export default <NuxtConfig> {
imgix: {
baseURL: 'https://assets.imgix.net'
},
imgproxy: {
baseURL: 'https://cc.cz/',
key: 'xxxxxxxxxxxxxx',
salt: 'xxxxxxxxxxxxxx'
},
imagekit: {
baseURL: 'https://ik.imagekit.io/demo'
},
Expand Down
12 changes: 12 additions & 0 deletions playground/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,18 @@ export const providers: Provider[] = [
}
]
},
// imgproxy
{
name: 'imgproxy',
samples: [
{
src: '/wp-content/uploads/2021/08/messi-psg.jpg',
width: 300,
height: 300,
fit: 'cover'
}
]
},
// Glide
{
name: 'glide',
Expand Down
1 change: 1 addition & 0 deletions src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const BuiltInProviders = [
'glide',
'imagekit',
'imgix',
'imgproxy',
'ipx',
'netlify',
'prismic',
Expand Down
77 changes: 77 additions & 0 deletions src/runtime/providers/imgproxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { joinURL, withBase } from 'ufo'
import createHmac from 'create-hmac'
import type { ProviderGetImage } from 'src'
import defu from 'defu'
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 (value: Buffer | string) {
return Buffer.from(value)
.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 = 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 const getImage: ProviderGetImage = (src, { modifiers = {}, baseURL = '/', key, salt } = {}) => {
const mergeModifiers = defu(modifiers, defaultModifiers)
const encodedUrl = urlSafeBase64(src)
const path = joinURL('/', operationsGenerator(mergeModifiers), encodedUrl)
const signature = sign(salt, path, key)

return {
url: withBase(joinURL(signature, path), baseURL)
}
}
1 change: 1 addition & 0 deletions src/types/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface ImageProviders {
glide?: any
imagekit?: any
imgix?: any
imgproxy?: any
prismic?: any
twicpics?: any
storyblok?: any,
Expand Down
6 changes: 6 additions & 0 deletions test/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const images = [
fastly: { url: '/test.png' },
glide: { url: '/test.png' },
imgix: { url: '/test.png' },
imgproxy: { url: 'k1phiL1JxYH8hB7DOX7K5bQN5QROoVosx4S5Xy8Au6o/rt:fill/w:0/h:0/g:no/el:1/f:webp/L3Rlc3QucG5n' },
unsplash: { url: '/test.png' },
imagekit: { url: '/test.png' },
netlify: { url: '/test.png' },
Expand All @@ -21,6 +22,7 @@ export const images = [
fastly: { url: '/test.png?width=200' },
glide: { url: '/test.png?w=200' },
imgix: { url: '/test.png?w=200' },
imgproxy: { url: 'P-05fCqcIXSL-KUDmMYj6P1EHK4_fDMsgVMDdLwAFsU/rt:fill/w:200/h:0/g:no/el:1/f:webp/L3Rlc3QucG5n' },
unsplash: { url: '/test.png?w=200' },
imagekit: { url: '/test.png?tr=w-200' },
netlify: { url: '/test.png?w=200&nf_resize=fit' },
Expand All @@ -35,6 +37,7 @@ export const images = [
fastly: { url: '/test.png?height=200' },
glide: { url: '/test.png?h=200' },
imgix: { url: '/test.png?h=200' },
imgproxy: { url: '_rK05RiABM_FNjd-3avlzcopKMEpoW6zTixZTT_P6ns/rt:fill/w:0/h:200/g:no/el:1/f:webp/L3Rlc3QucG5n' },
unsplash: { url: '/test.png?h=200' },
imagekit: { url: '/test.png?tr=h-200' },
netlify: { url: '/test.png?h=200&nf_resize=fit' },
Expand All @@ -49,6 +52,7 @@ export const images = [
fastly: { url: '/test.png?width=200&height=200' },
glide: { url: '/test.png?w=200&h=200' },
imgix: { url: '/test.png?w=200&h=200' },
imgproxy: { url: 'ppLBz-0jEEfN2Mt36w4dU5rkA5H0BXEby8CKWZqkMNA/rt:fill/w:200/h:200/g:no/el:1/f:webp/L3Rlc3QucG5n' },
unsplash: { url: '/test.png?w=200&h=200' },
imagekit: { url: '/test.png?tr=w-200,h-200' },
netlify: { url: '/test.png?w=200&h=200&nf_resize=fit' },
Expand All @@ -63,6 +67,7 @@ export const images = [
fastly: { url: '/test.png?width=200&height=200&fit=bounds' },
glide: { url: '/test.png?w=200&h=200&fit=contain' },
imgix: { url: '/test.png?w=200&h=200&fit=fill' },
imgproxy: { url: 'GKWuPmkmcJ2RaaMd8AVdvbwGaXImEndxZ_ONykbNOMw/rt:contain/w:200/h:200/g:no/el:1/f:webp/L3Rlc3QucG5n' },
unsplash: { url: '/test.png?w=200&h=200&fit=fill' },
imagekit: { url: '/test.png?tr=w-200,h-200,cm-pad_resize' },
netlify: { url: '/test.png?w=200&h=200&nf_resize=fit' },
Expand All @@ -77,6 +82,7 @@ export const images = [
fastly: { url: '/test.png?width=200&height=200&fit=bounds&format=jpeg' },
glide: { url: '/test.png?w=200&h=200&fit=contain&fm=jpeg' },
imgix: { url: '/test.png?w=200&h=200&fit=fill&fm=jpeg' },
imgproxy: { url: 'pgVf-gjVDwYJzY1bv3ZxGBjHa4QlQrWEyoGM7GST7N4/rt:contain/w:200/h:200/g:no/el:1/f:jpeg/L3Rlc3QucG5n' },
unsplash: { url: '/test.png?w=200&h=200&fit=fill&fm=jpeg' },
imagekit: { url: '/test.png?tr=w-200,h-200,cm-pad_resize,f-jpeg' },
netlify: { url: '/test.png?w=200&h=200&nf_resize=fit' },
Expand Down
15 changes: 15 additions & 0 deletions test/unit/providers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as twicpics from '~/runtime/providers/twicpics'
import * as fastly from '~/runtime/providers/fastly'
import * as glide from '~/runtime/providers/glide'
import * as imgix from '~/runtime/providers/imgix'
import * as imgproxy from '~/runtime/providers/imgproxy'
import * as unsplash from '~/runtime/providers/unsplash'
import * as imagekit from '~/runtime/providers/imagekit'
import * as netlify from '~/runtime/providers/netlify'
Expand Down Expand Up @@ -136,6 +137,20 @@ describe('Providers', () => {
}
})

test('imgproxy', () => {
const providerOptions = {
baseURL: '',
key: 'xxxxxxxxxxxxxx',
salt: 'xxxxxxxxxxxxxx'
}

for (const image of images) {
const [, modifiers] = image.args
const generated = imgproxy.getImage('/test.png', { modifiers, ...providerOptions }, emptyContext)
expect(generated).toMatchObject(image.imgproxy)
}
})

test('unsplash', () => {
const providerOptions = {
baseURL: ''
Expand Down
7 changes: 6 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@
"strict": true,
"baseUrl": ".",
"moduleResolution": "Node",
"lib": [
"ESNext",
"ESNext.AsyncIterable",
"DOM"
],
"esModuleInterop": true,
"resolveJsonModule": true,
"types": ["node", "jest"],
"types": ["node", "jest", "@types/create-hmac"],
"paths": {
"~image/*": ["src/runtime/*"],
"~image": ["src/runtime"]
Expand Down

0 comments on commit 2cb8fc4

Please sign in to comment.