├── .editorconfig
├── .gitattributes
├── .gitignore
├── README.md
├── admin
├── build
│ ├── main.js
│ └── main.js.map
└── src
│ ├── containers
│ ├── App
│ │ ├── actions.js
│ │ ├── constants.js
│ │ ├── index.js
│ │ ├── reducer.js
│ │ └── selectors.js
│ ├── HomePage
│ │ ├── actions.js
│ │ ├── constants.js
│ │ ├── index.js
│ │ ├── reducer.js
│ │ ├── saga.js
│ │ ├── selectors.js
│ │ └── styles.scss
│ └── NotFoundPage
│ │ └── index.js
│ └── translations
│ ├── ar.json
│ ├── de.json
│ ├── en.json
│ ├── es.json
│ ├── fr.json
│ ├── it.json
│ ├── ko.json
│ ├── nl.json
│ ├── pl.json
│ ├── pt-BR.json
│ ├── pt.json
│ ├── ru.json
│ ├── tr.json
│ ├── zh-Hans.json
│ └── zh.json
├── config
├── functions
│ └── bootstrap.js
└── routes.json
├── controllers
└── Images.js
├── middlewares
└── images
│ ├── defaults.json
│ └── index.js
├── package.json
└── services
└── Images.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = false
6 | indent_style = space
7 | indent_size = 2
8 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # From https://github.com/Danimoth/gitattributes/blob/master/Web.gitattributes
2 |
3 | # Handle line endings automatically for files detected as text
4 | # and leave all files detected as binary untouched.
5 | * text=auto
6 |
7 | #
8 | # The above will handle all files NOT found below
9 | #
10 |
11 | #
12 | ## These files are text and should be normalized (Convert crlf => lf)
13 | #
14 |
15 | # source code
16 | *.php text
17 | *.css text
18 | *.sass text
19 | *.scss text
20 | *.less text
21 | *.styl text
22 | *.js text eol=lf
23 | *.coffee text
24 | *.json text
25 | *.htm text
26 | *.html text
27 | *.xml text
28 | *.svg text
29 | *.txt text
30 | *.ini text
31 | *.inc text
32 | *.pl text
33 | *.rb text
34 | *.py text
35 | *.scm text
36 | *.sql text
37 | *.sh text
38 | *.bat text
39 |
40 | # templates
41 | *.ejs text
42 | *.hbt text
43 | *.jade text
44 | *.haml text
45 | *.hbs text
46 | *.dot text
47 | *.tmpl text
48 | *.phtml text
49 |
50 | # git config
51 | .gitattributes text
52 | .gitignore text
53 | .gitconfig text
54 |
55 | # code analysis config
56 | .jshintrc text
57 | .jscsrc text
58 | .jshintignore text
59 | .csslintrc text
60 |
61 | # misc config
62 | *.yaml text
63 | *.yml text
64 | .editorconfig text
65 |
66 | # build config
67 | *.npmignore text
68 | *.bowerrc text
69 |
70 | # Heroku
71 | Procfile text
72 | .slugignore text
73 |
74 | # Documentation
75 | *.md text
76 | LICENSE text
77 | AUTHORS text
78 |
79 |
80 | #
81 | ## These files are binary and should be left untouched
82 | #
83 |
84 | # (binary is a macro for -text -diff)
85 | *.png binary
86 | *.jpg binary
87 | *.jpeg binary
88 | *.gif binary
89 | *.ico binary
90 | *.mov binary
91 | *.mp4 binary
92 | *.mp3 binary
93 | *.flv binary
94 | *.fla binary
95 | *.swf binary
96 | *.gz binary
97 | *.zip binary
98 | *.7z binary
99 | *.ttf binary
100 | *.eot binary
101 | *.woff binary
102 | *.pyc binary
103 | *.pdf binary
104 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Don't check auto-generated stuff into git
2 | coverage
3 | node_modules
4 | stats.json
5 | package-lock.json
6 |
7 | # Cruft
8 | .DS_Store
9 | npm-debug.log
10 | .idea
11 |
12 | package-lock.json
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Strapi images plugin
2 |
3 | Images plugin was made because I needed to fetch image-uploads in an exact size, determined by the client.
4 |
5 | ## Installation
6 |
7 | Due to a limitation in strapis installation script, you will have to install this plugin manually.
8 |
9 | In the root of your project, run the following:
10 | ```
11 | npm install strapi-plugin-images --no-save
12 | ```
13 | *This will install the plugin node `node_modules` folder*
14 |
15 | ```
16 | mv node_modules/strapi-plugin-images plugins/images
17 | ```
18 | *In order for strapi to use this plugin, it has to be moved to the folder `plugins`*
19 |
20 | ```
21 | cp node_modules/strapi-generate-plugin/templates/gitignore plugins/images/.gitignore
22 | ```
23 | *To keep a clean project, you'll need the `.gitignore` plugin template from strapi*
24 |
25 | ## Configuration
26 |
27 | When plugin has been installed, you need to allow access to the `GET: images` endpoint.
28 |
29 | 1. Navigate to Users & Permissions.
30 | 2. Pick the role you would like to give permission.
31 | 3. Scroll down to the section **Images**.
32 | 4. Check the `get` endpoint, and press save.
33 |
34 | ## Usage
35 |
36 | When the plugin is installed, it will add a `resize_url` field to each attachment in the response. When the attachment is of a supported mime-type, the field will contain a url for the resizing endpoint.
37 |
38 | ### Resizing endpoint
39 |
40 | This endpoint can be called with some transformation parameters.
41 |
42 | Examples:
43 |
44 | ```HOST_NAME/images/${image_id}?size=120x120```
45 | This example will resize the image to 120px by 120px. It will by default use the "cover" mode.
46 |
47 | ```HOST_NAME/images/${image_id}?size=120x120&mode=contain```
48 | This example will resize the image to 120px by 120px. This is explicitly configured to use the contain mode.
49 |
50 | ## Credits
51 |
52 | This plugin is basically a strapi implementation of [Jimp](https://github.com/oliver-moran/jimp)
53 |
54 | Checkout the docs.
55 |
--------------------------------------------------------------------------------
/admin/build/main.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"main.js","sources":["webpack:///main.js"],"mappings":"AAAA;;;;;AAKA;;;;;;;;AAQA;;;;;AAKA","sourceRoot":""}
--------------------------------------------------------------------------------
/admin/src/containers/App/actions.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * App actions
4 | *
5 | */
6 |
--------------------------------------------------------------------------------
/admin/src/containers/App/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * App constants
4 | *
5 | */
6 |
--------------------------------------------------------------------------------
/admin/src/containers/App/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * This component is the skeleton around the actual pages, and should only
4 | * contain code that should be seen on all pages. (e.g. navigation bar)
5 | *
6 | */
7 |
8 | import React from 'react';
9 | import PropTypes from 'prop-types';
10 | import { connect } from 'react-redux';
11 | import { createStructuredSelector } from 'reselect';
12 | import { Switch, Route } from 'react-router-dom';
13 | import { bindActionCreators, compose } from 'redux';
14 |
15 | // Utils
16 | import { pluginId } from 'app';
17 |
18 | // Containers
19 | import HomePage from 'containers/HomePage';
20 | import NotFoundPage from 'containers/NotFoundPage';
21 |
22 |
23 | class App extends React.Component {
24 |
25 | render() {
26 | return (
27 |
28 |
29 |
30 |
31 |
32 |
33 | );
34 | }
35 | }
36 |
37 | App.contextTypes = {
38 | plugins: PropTypes.object,
39 | router: PropTypes.object.isRequired,
40 | updatePlugin: PropTypes.func,
41 | };
42 |
43 | App.propTypes = {
44 | history: PropTypes.object.isRequired,
45 | };
46 |
47 | export function mapDispatchToProps(dispatch) {
48 | return bindActionCreators(
49 | {},
50 | dispatch,
51 | );
52 | }
53 |
54 | const mapStateToProps = createStructuredSelector({});
55 |
56 | // Wrap the component to inject dispatch and state into it
57 | const withConnect = connect(mapStateToProps, mapDispatchToProps);
58 |
59 | export default compose(
60 | withConnect,
61 | )(App);
62 |
--------------------------------------------------------------------------------
/admin/src/containers/App/reducer.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * App reducer
4 | *
5 | */
6 |
7 | import { fromJS } from 'immutable';
8 |
9 | const initialState = fromJS({});
10 |
11 | function appReducer(state = initialState, action) {
12 | switch (action.type) {
13 | default:
14 | return state;
15 | }
16 | }
17 |
18 | export default appReducer;
19 |
--------------------------------------------------------------------------------
/admin/src/containers/App/selectors.js:
--------------------------------------------------------------------------------
1 | // import { createSelector } from 'reselect';
2 |
3 | /**
4 | * Direct selector to the list state domain
5 | */
6 |
7 | // const selectGlobalDomain = () => state => state.get('global');
8 |
9 | export {};
10 |
--------------------------------------------------------------------------------
/admin/src/containers/HomePage/actions.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * HomePage actions
4 | *
5 | */
6 |
7 | import { DEFAULT_ACTION } from './constants';
8 |
9 | export function defaultAction() {
10 | return {
11 | type: DEFAULT_ACTION,
12 | };
13 | }
14 |
--------------------------------------------------------------------------------
/admin/src/containers/HomePage/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * HomePage constants
4 | *
5 | */
6 |
7 | export const DEFAULT_ACTION = 'HomePage/DEFAULT_ACTION';
8 |
--------------------------------------------------------------------------------
/admin/src/containers/HomePage/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * HomePage
4 | *
5 | */
6 |
7 | import React from 'react';
8 | import PropTypes from 'prop-types';
9 | import { connect } from 'react-redux';
10 | import { createStructuredSelector } from 'reselect';
11 | import { injectIntl } from 'react-intl';
12 | import { bindActionCreators, compose } from 'redux';
13 |
14 | import injectReducer from 'utils/injectReducer';
15 | import injectSaga from 'utils/injectSaga';
16 |
17 | // Selectors
18 | import selectHomePage from './selectors';
19 |
20 | // Styles
21 | import styles from './styles.scss';
22 |
23 | import reducer from './reducer';
24 | import saga from './saga';
25 |
26 | export class HomePage extends React.Component {
27 | render() {
28 | return (
29 |
30 |
Images plugin
31 |
32 | );
33 | }
34 | }
35 |
36 | HomePage.contextTypes = {
37 | router: PropTypes.object,
38 | };
39 |
40 | HomePage.propTypes = {
41 | // homePage: PropTypes.object,
42 | };
43 |
44 | function mapDispatchToProps(dispatch) {
45 | return bindActionCreators(
46 | {
47 | // Your actions here
48 | },
49 | dispatch,
50 | );
51 | }
52 |
53 | const mapStateToProps = createStructuredSelector({
54 | homePage: selectHomePage(),
55 | });
56 |
57 | const withConnect = connect(mapStateToProps, mapDispatchToProps);
58 |
59 | const withReducer = injectReducer({ key: 'homePage', reducer });
60 | const withSaga = injectSaga({ key: 'homePage', saga });
61 |
62 | export default compose(
63 | withReducer,
64 | withSaga,
65 | withConnect,
66 | )(injectIntl(HomePage));
67 |
--------------------------------------------------------------------------------
/admin/src/containers/HomePage/reducer.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * HomePage reducer
4 | *
5 | */
6 |
7 | import { fromJS } from 'immutable';
8 |
9 | import { DEFAULT_ACTION } from './constants';
10 |
11 | const initialState = fromJS({});
12 |
13 | function homePageReducer(state = initialState, action) {
14 | switch (action.type) {
15 | case DEFAULT_ACTION:
16 | return state;
17 | default:
18 | return state;
19 | }
20 | }
21 |
22 | export default homePageReducer;
23 |
--------------------------------------------------------------------------------
/admin/src/containers/HomePage/saga.js:
--------------------------------------------------------------------------------
1 | // import { LOCATION_CHANGE } from 'react-router-redux';
2 | // import { takeLatest, put, fork, take, cancel } from 'redux-saga/effects';
3 |
4 | // Individual exports for testing
5 | export function* defaultSaga() {
6 | }
7 |
8 | // All sagas to be loaded
9 | export default defaultSaga;
10 |
--------------------------------------------------------------------------------
/admin/src/containers/HomePage/selectors.js:
--------------------------------------------------------------------------------
1 | import { createSelector } from 'reselect';
2 |
3 | /**
4 | * Direct selector to the homePage state domain
5 | */
6 | const selectHomePageDomain = () => state => state.get('homePage');
7 |
8 | /**
9 | * Default selector used by HomePage
10 | */
11 |
12 | const selectHomePage = () => createSelector(
13 | selectHomePageDomain(),
14 | (substate) => substate.toJS(),
15 | );
16 |
17 | export default selectHomePage;
18 |
--------------------------------------------------------------------------------
/admin/src/containers/HomePage/styles.scss:
--------------------------------------------------------------------------------
1 | .homePage {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/admin/src/containers/NotFoundPage/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * NotFoundPage
3 | *
4 | * This is the page we show when the user visits a url that doesn't have a route
5 | *
6 | * NOTE: while this component should technically be a stateless functional
7 | * component (SFC), hot reloading does not currently support SFCs. If hot
8 | * reloading is not a neccessity for you then you can refactor it and remove
9 | * the linting exception.
10 | */
11 |
12 | import React from 'react';
13 |
14 | import NotFound from 'components/NotFound';
15 |
16 | export default class NotFoundPage extends React.Component {
17 | render() {
18 | return ;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/admin/src/translations/ar.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/de.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/admin/src/translations/en.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/es.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/fr.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/it.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/ko.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/nl.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/pl.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/admin/src/translations/pt-BR.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/pt.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/ru.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/tr.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/admin/src/translations/zh-Hans.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/zh.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/config/functions/bootstrap.js:
--------------------------------------------------------------------------------
1 | module.exports = async cb => {
2 | cb();
3 | };
--------------------------------------------------------------------------------
/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "method": "GET",
5 | "path": "/:_id",
6 | "handler": "Images.get",
7 | "config": {
8 | "policies": []
9 | }
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/controllers/Images.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Images.js controller
5 | *
6 | * @description: A set of functions called "actions" of the `images` plugin.
7 | */
8 |
9 | module.exports = {
10 |
11 | /**
12 | * Get and process an image
13 | *
14 | * @return {Object}
15 | */
16 |
17 | get: async (ctx) => {
18 | const ImagesService = strapi.plugins['images'].services.images;
19 | const {
20 | size,
21 | mode = 'cover',
22 | } = ctx.query;
23 | const entity = await strapi.plugins['upload'].services.upload.fetch(ctx.params);
24 | const {
25 | mime,
26 | } = entity;
27 | if (!entity) return ctx.notFound(); // Upload was not found
28 | if (!ImagesService.supportedMime(mime)) return ctx.noContent(); // Mime of the upload is not supported
29 |
30 | try {
31 | const image = await ImagesService.read(entity);
32 | await ImagesService
33 | .process(image, {
34 | size,
35 | mode,
36 | });
37 | const imageBuffer = await ImagesService.getBuffer(image, mime);
38 | ctx.set('Content-Type', mime);
39 | ctx.send(imageBuffer);
40 | } catch (error) {
41 | ctx.internalServerError();
42 | }
43 | }
44 | };
45 |
--------------------------------------------------------------------------------
/middlewares/images/defaults.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": {
3 | "enabled": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/middlewares/images/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const _ = require('lodash');
3 |
4 | function extractAttachmentKeys(jsonBody) {
5 | const recursiveMap = (value, key) => {
6 | if (_.isArray(value)) {
7 | return _.map(value, recursiveMap).map((keyIndex) => [key, keyIndex].join(','));
8 | } else {
9 | if (_.has(value, 'mime')) return key;
10 | }
11 | };
12 |
13 | return _(jsonBody)
14 | .mapValues(recursiveMap)
15 | .filter((imageKey) => !!imageKey)
16 | .flatten()
17 | .value();
18 | }
19 |
20 | module.exports = strapi => ({
21 | initialize: (cb) => {
22 | const ImagesService = strapi.plugins['images'].services.images;
23 | strapi.app.use(async (ctx, next) => {
24 | await next();
25 | if (ctx.get('Content-Type') != 'application/json' || !ctx.body) return;
26 | const responseBody = JSON.parse(JSON.stringify(ctx.body));
27 | const attachmentKeys = extractAttachmentKeys(responseBody);
28 | if (_.size(attachmentKeys) < 1) return;
29 | _.forEach(attachmentKeys, (key) => {
30 | const objectPath = key.split(',');
31 | const attachment = _.get(responseBody, objectPath);
32 | if ( ImagesService.supportedMime(_.get(attachment, ['mime']))) {
33 | const resizeRoute = ImagesService.resizeURL(_.get(attachment, ['_id']));
34 | _.set(responseBody, [...objectPath, 'resize_url'], resizeRoute);
35 | } else {
36 | _.set(responseBody, [...objectPath, 'resize_url'], null);
37 | }
38 | });
39 | ctx.body = responseBody;
40 | });
41 | cb();
42 | }
43 | });
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "strapi-plugin-images",
3 | "version": "0.0.5",
4 | "description": "This plugin delivers more image-features to your apis.",
5 | "strapi": {
6 | "name": "images",
7 | "icon": "plug",
8 | "description": "Description of images plugin."
9 | },
10 | "scripts": {
11 | "analyze:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf stats.json",
12 | "preanalyze": "npm run analyze:clean",
13 | "analyze": "node ./node_modules/strapi-helper-plugin/lib/internals/scripts/analyze.js",
14 | "prebuild": "npm run build:clean",
15 | "build:dev": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=development node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress",
16 | "build": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=production node node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress",
17 | "build:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf admin/build",
18 | "start": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=development node ./node_modules/strapi-helper-plugin/lib/server",
19 | "generate": "node ./node_modules/plop/plop.js --plopfile node_modules/strapi-helper-plugin/lib/internals/generators/index.js",
20 | "lint": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/eslint --ignore-path .gitignore --ignore-pattern '/admin/build/' --config ./node_modules/strapi-helper-plugin/lib/internals/eslint/.eslintrc.json admin",
21 | "prettier": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/prettier --single-quote --trailing-comma es5 --write \"{admin,__{tests,mocks}__}/**/*.js\"",
22 | "test": "npm run lint",
23 | "prepublishOnly": "npm run build"
24 | },
25 | "dependencies": {
26 | "jimp": "^0.5.4",
27 | "lodash": "^4.17.11"
28 | },
29 | "devDependencies": {
30 | "strapi-helper-plugin": "3.0.0-alpha.14.3"
31 | },
32 | "author": {
33 | "name": "Kristian Froelund",
34 | "email": "",
35 | "url": ""
36 | },
37 | "repository": {
38 | "type": "git",
39 | "url": "https://github.com/Froelund/strapi-plugin-images.git"
40 | },
41 | "maintainers": [
42 | {
43 | "name": "Kristian Froelund",
44 | "email": "",
45 | "url": ""
46 | }
47 | ],
48 | "engines": {
49 | "node": ">= 9.0.0",
50 | "npm": ">= 5.3.0"
51 | },
52 | "license": "MIT"
53 | }
54 |
--------------------------------------------------------------------------------
/services/Images.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const Jimp = require('jimp');
3 | const path = require('path');
4 | const _ = require('lodash');
5 | /**
6 | * Images.js service
7 | *
8 | * @description:
9 | */
10 | const SUPPORTED_MIMES = [Jimp.MIME_PNG, Jimp.MIME_TIFF, Jimp.MIME_JPEG, Jimp.MIME_BMP, Jimp.MIME_X_MS_BMP, Jimp.MIME_GIF];
11 | module.exports = {
12 | SUPPORTED_MIMES,
13 | supportedMime: (mime) => _.isString(mime) && _.includes(SUPPORTED_MIMES, mime),
14 | read: async (entity) => {
15 | const {
16 | provider,
17 | url,
18 | } = entity;
19 | if (provider == 'local') {
20 | const filePath = path.join(strapi.config.appPath, 'public', url);
21 | return Jimp.read(filePath);
22 | } else {
23 | return Jimp.read(url);
24 | }
25 | },
26 | process: async (image, options) => {
27 | const {
28 | size,
29 | mode = 'cover'
30 | } = options;
31 | if (size) {
32 | const [width, height] = size.split('x').map((dimString) => parseInt(dimString));
33 | switch (mode) {
34 | case 'contain':
35 | await image.contain(width, height);
36 | break;
37 | case 'cover':
38 | await image.cover(width, height);
39 | break;
40 | }
41 | }
42 | },
43 | getBuffer: async (image, mime) => {
44 | return new Promise((resolve, reject) => {
45 | image.getBuffer(mime, (err, buffer) => {
46 | if (err) return reject(err);
47 | return resolve(buffer);
48 | });
49 | });
50 | },
51 | resizeURL: (imageID) => {
52 | return `/images/${imageID}`;
53 | }
54 | };
55 |
--------------------------------------------------------------------------------