New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

hapi-darwin

Package Overview
Dependencies
Maintainers
2
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

hapi-darwin - npm Package Compare versions

Comparing version 1.0.2 to 1.1.0

_config.yml

263

index.js

@@ -1,251 +0,36 @@

/* Hapi-darwin 1.0.2 */
/* Multiple mode have lots of problems */
/*
darwin dont need eventEmitter because we using async...await
additional darwin handling errors with try...catch
*/
'use strict';
import os from 'os';
import * as fs from 'fs';
import mkdirp from 'mkdirp';
import { promisify } from 'util';
import { basename, extname, join } from 'path';
import fileType from 'file-type';
import readChunk from 'read-chunk';
import imageMagick from 'imagemagick';
const uploader = require('./lib/uploader');
const internals = {};
const register = (server, options, next) => {
const sizeOf = promisify(require('image-size'));
internals.uploader = (file, options = {}) => {
if (!file) throw new Error('No file(s) recived');
internals.options = Object.assign({
tmp: os.tmpdir(),
name: undefined,
filter: /\.(jpg|jpeg|png|gif)$/,
multiple: false,
options = {
dest: `./images`,
names: undefined,
safeName: true,
delOrginal: false, // when using `imageVersions`
formats: ['jpeg', 'png', 'gif', 'webp'],
maxFiles: 1,
minFileSize: 0.001 * 1024 * 1024, // 10 KB
maxFileSize: 1 * 1024 * 1024, // 1 MB
minHeight: 10, // in pixels
maxHeight: 10000, // in pixels
minWidth: 10, // in pixels
maxWidth: 10000, // in pixels
destination: './public/storage/null',
urlDestination: 'https://hapi-darwin/public/storage/null',
// imageVersions: {
// ['thumbnail-720']: {
// width: 2 * 720,
// height: 720,
// imageArgs: ['-auto-orient']
// },
// ['thumbnail-360']: {
// width: 2 * 360,
// height: 360,
// imageArgs: ['-auto-orient']
// }
// },
imageVersions: undefined
}, internals.options, options);
minPixels: 1e2,
maxPixels: 1e6,
versions: [],
addOriginal: false,
...options
};
if (Array.isArray(file) && internals.options.multiple) {
server.expose({
uploader: (files, opts = {}) => uploader(files, { ...options, ...opts })
});
if (file.length > internals.options.maxFiles) throw new Error('Maximum files recived');
const promises = file.map(each => internals._fileHandler(each));
return Promise.all(promises);
} else {
if (file.length > 1) throw new Error('Maximum files recived');
return internals._fileHandler(file);
}
}
internals.setOptions = (options) => {
internals.options = Object.assign({}, options);
next && next();
};
internals.validateBeforReciving = (fileName) => {
if (!fileName.match(internals.options.filter)) return false;
return true;
register.attributes = {
once: true,
pkg: require('./package.json'),
name: '@ecmacommunity/hapi-darwin'
};
internals.validateAfterRecived = async (fileDetails) => {
const dimensions = await sizeOf(fileDetails.path);
const buffer = readChunk.sync(fileDetails.path, 0, 4100);
const file = fileType(buffer);
let error;
if (internals.options.minFileSize && internals.options.minFileSize > fileDetails.size) {
error = 'File is too small';
} else if (internals.options.maxFileSize && internals.options.maxFileSize < fileDetails.size) {
error = 'File is too big';
} else if (!internals.options.filter.test(`x.${file.ext}`)) {
error = 'Filetype not allowed';
} else if (internals.options.minHeight && internals.options.minHeight > dimensions.height) {
error = `Minimum height should be ${internals.options.minHeight}px`;
} else if (internals.options.maxHeight && internals.options.maxHeight < dimensions.height) {
error = `Maximum height should be ${internals.options.maxHeight}px`;
} else if (internals.options.minWidth && internals.options.minWidth > dimensions.width) {
error = `Minimum width should be ${internals.options.minWidth}px`;
} else if (internals.options.maxWidth && internals.options.maxWidth < dimensions.width) {
error = `Maximum width should be ${internals.options.maxWidth}px`;
}
return error;
module.exports = {
register,
...register.attributes
};
internals.safeNameFs = async (filename) => {
// Prevent directory traversal and creating hidden system files:
let name = basename(filename.toString()).replace(/^\.+/, '');
// Prevent overwriting existing files:
while (fs.existsSync(join(internals.options.destination, name))) {
name = name.replace(/(?:(?:_\(([\d]+)\))?(\.[^.]+))?$/, (s, index, ext) => `_(${(parseInt(index, 10) || 0) + 1})${ext || ''}`);
}
return name;
};
internals._fileHandler = async (file) => {
const orignalname = file.hapi.filename;
let filename = internals.options.name ? `${internals.options.name}${extname(orignalname)}` : orignalname;
if (internals.options.safeName) {
filename = await internals.safeNameFs(filename);
}
const path = join(internals.options.destination, filename);
const tmpName = `_darwin_${Date.now()}_${filename}`;
const tmpPath = join(internals.options.tmp, tmpName);
const fileStream = fs.createWriteStream(tmpPath);
const imageVersions = internals.options.imageVersions ? Object.keys(internals.options.imageVersions) : [];
if (!internals.validateBeforReciving(orignalname)) {
throw new Error('Filetype not allowed');
}
return new Promise((resolve, reject) => {
file.on('error', err => {
reject(err);
});
file.pipe(fileStream);
file.on('end', async err => {
const fileDetails = {
fieldname: file.hapi.name,
originalname: file.hapi.filename,
filename,
mimetype: file.hapi.headers['content-type'],
destination: internals.options.destination,
urlDestination: internals.options.urlDestination,
path,
url: join(internals.options.urlDestination, filename),
size: fs.statSync(tmpPath).size,
delOrginal: internals.options.delOrginal ? true : false,
versions: imageVersions.length > 0 ? {} : undefined
}
const finish = () => { resolve(fileDetails) }
const validate = await internals.validateAfterRecived({
path: tmpPath,
size: fileDetails.size
});
if (validate) {
await internals.deleteFileFs(tmpPath);
return reject(new Error(validate));
}
mkdirp(internals.options.destination, (err, made) => {
fs.rename(tmpPath, path, err => {
if (!err) {
generateVersions();
} else {
const is = fs.createReadStream(tmpPath);
const os = fs.createWriteStream(path);
is.on('end', async err => {
if (!err) {
await internals.deleteFileFs(path);
generateVersions();
}
});
is.pipe(os);
}
});
});
const generateVersions = () => {
if (internals.options.imageVersions === undefined) finish();
else {
let counter = 0;
imageVersions.forEach(version => {
mkdirp(join(internals.options.destination, version), (err, made) => {
const opts = internals.options.imageVersions[version];
imageMagick.resize({
width: opts.width,
height: opts.height,
srcPath: join(internals.options.destination, filename),
dstPath: join(internals.options.destination, version, filename),
customArgs: opts.imageArgs || ['-auto-orient']
}, async (err, stdout, stderr) => {
if (err) throw err;
counter++;
fileDetails.versions[version] = {
destination: join(internals.options.destination, version),
urlDestination: join(internals.options.urlDestination, version),
path: join(internals.options.destination, version, filename),
url: join(internals.options.urlDestination, version, filename)
}
if (imageVersions.length === counter) {
if (internals.options.delOrginal) await internals.deleteFileFs(path);
finish();
}
});
});
});
}
}
})
})
}
internals.deleteFileFs = async (filePath) => {
fs.unlinkSync(filePath);
};
export function register(server, options, next) {
internals.setOptions(options);
server.expose('uploader', internals.uploader);
server.expose('deleteFileFs', internals.deleteFileFs);
next();
}
exports.register.attributes = {
name: 'hapi-darwin'
};
{
"name": "hapi-darwin",
"version": "1.0.2",
"version": "1.1.0",
"description": "simple image storage",
"main": "index.js",
"scripts": {
"test": "lab -c -L",
"test-cover": "lab -c -r html -o ./test/artifacts/coverage.html && open ./test/artifacts/coverage.html"
"test": "lab -c -L -a code"
},
"repository": {
"type": "git",
"url": "https://github.com/tavousi/hapi-darwin"
"url": "https://github.com/escommunity/hapi-darwin.git"
},
"author": "Seyed Amirhossein Tavousi",
"license": "MIT",
"keywords": [
"hapi",
"upload"
],
"bugs": {
"url": "https://github.com/tavousi/hapi-darwin/issues"
"url": "https://github.com/escommunity/hapi-darwin/issues"
},
"homepage": "https://github.com/tavousi/hapi-darwin",
"homepage": "https://github.com/escommunity/hapi-darwin",
"license": "MIT",
"engines": {
"node": ">=4.0.0"
"node": ">=8.0.0"
},
"dependencies": {
"globby": "^8.0.0",
"mkdirp": "^0.5.0",
"sharp": "^0.20.0"
},
"devDependencies": {
"hapi": "15.x.x",
"code": "3.x.x",
"lab": "11.x.x"
},
"dependencies": {
"file-type": "^7.4.0",
"image-size": "^0.6.1",
"imagemagick": "^0.1.3",
"mkdirp": "^0.5.1",
"read-chunk": "^2.1.0"
"code": "^5.0.0",
"del": "^3.0.0",
"eslint-plugin-hapi": "^4.1.0",
"hapi": "^17.0.0",
"lab": "^15.0.0"
}
}
# hapi-darwin
image storage
A Hapi plugin for image storage
[![Dependency Status](https://david-dm.org/tavousi/hapi-darwin.svg)](https://david-dm.org/tavousi/hapi-darwin)
[![devDependency Status](https://david-dm.org/tavousi/hapi-darwin/dev-status.svg?theme=shields.io)](https://david-dm.org/tavousi/hapi-darwin?type=dev)
[![Build Status](https://travis-ci.org/tavousi/hapi-darwin.svg?branch=master)](https://travis-ci.org/tavousi/hapi-darwin)
[![Dependency Status](https://david-dm.org/ecmascriptforever/hapi-darwin.svg)](https://david-dm.org/ecmascriptforever/hapi-darwin)
[![devDependency Status](https://david-dm.org/ecmascriptforever/hapi-darwin.svg?theme=shields.io)](https://david-dm.org/ecmascriptforever/hapi-darwin?type=dev)
[![Build Status](https://travis-ci.org/ecmascriptforever/hapi-darwin.svg?branch=master)](https://travis-ci.org/ecmascriptforever/hapi-darwin)
## Install
```bash
$ npm install hapi-darwin
$ npm install @ecmacommunity/hapi-darwin
```
## Register
```js
await server.register({
plugin: require('@ecmacommunity/hapi-darwin'),
options: {
// Any uploader method options
dest: './path/to/destination',
formats: ['jpeg', 'png']
}
});
```
## Usage
```js
server.route({
method: 'POST',
path: '/upload',
config: {
payload: {
maxBytes: 1110.5 * 1024 * 1024, // 500KB
output: 'stream',
allow: 'multipart/form-data' // important
},
cors: {
origin: ['*'],
credentials: true,
additionalHeaders: ['content-type']
}
},
handler: async function (request, reply) {
'use strict';
try {
const uploader = request.server.plugins['hapi-darwin'].uploader;
server.route({
method: 'POST',
path: '/path/to/endpoint',
options: {
payload: {
allow: 'multipart/form-data',
output: 'stream'
}
},
handler: async ({ payload }, h) => {
const data = request.payload;
const file = data['avatar'];
const { uploader } = server.plugins['@ecmacommunity/hapi-darwin'];
const fileName = `${Date.now()}-drwin`;
try {
return await uploader(payload.avatar, { names: 'avatar' });
} catch (err) {
// ...
}
}
})
```
const fileOptions = {
name: fileName,
destination: './public/storage/course'
}
## API
const result = await uploader(file, fileOptions);
### uploader(files, [options])
reply(result);
Returns a Promise for `object` or `object[]` or `object[][]` with:
} catch (err) {
reply(Boom.badRequest(err.message, err));
}
- `filename` (string) - corrected filename
- `path` (string) - absolute path of uploaded version
}
});
```
#### files
Type: `Readable` `Readable[]`
## Config
#### options
Type: `object`
##### dest
Type: `string`
Default: `./images`
Destination directory.
##### names
Type: `string` `string[]`
Default: `files.hapi.filename`.
Name(s) of input files to be used.
##### safeName
Type: `boolean`
Default: `true`
Whether replace new file with older exist or generate a unique filename.
##### formats
Type: `string[]`
Default: `['jpeg', 'png', 'gif', 'webp']`
File types that are allowed (it don't check extension).
##### maxFiles
Type: `number`
Default: `1`
Maximum input files can be uploaded.
##### minPixels
Type: `number`
Default: `1e2`
Minimum pixels can be manipulated.
##### maxPixels
Type: `number`
Default: `1e6`
Maximum pixels can be manipulated.
##### versions
Type: `object[]`
Default: `[]`
Define `array` of version `object` with:
- `width` (number) - pixels width the version image should be. Use `null` or `undefined` to auto-scale the width to match the height.
- `height` (number) - pixels height the version image should be. Use `null` or `undefined` to auto-scale the height to match the width.
- `enlargement` (boolean) - enlarge the output image if the input image width or height are already less than the required dimensions; default is false.
- `suffix` (string) - suffix of version filename.
Example:
```js
internals.options = Object.assign({
tmp: os.tmpdir(),
name: undefined,
filter: /\.(jpg|jpeg|png|gif)$/,
multiple: false,
safeName: true,
delOrginal: false, // when using `imageVersions`
maxFiles: 1,
minFileSize: 0.001 * 1024 * 1024, // 10 KB
maxFileSize: 1 * 1024 * 1024, // 1 MB
minHeight: 10, // in pixels
maxHeight: 10000, // in pixels
minWidth: 10, // in pixels
maxWidth: 10000, // in pixels
destination: './public/storage/null',
urlDestination: 'https://hapi-darwin/public/storage/null',
imageVersions: {
['thumbnail-720']: {
width: 2 * 720,
height: 720,
imageArgs: ['-auto-orient']
},
['thumbnail-360']: {
width: 2 * 360,
height: 360,
imageArgs: ['-auto-orient']
}
},
// imageVersions: undefined
}, internals.options, options);
versions: [
{ width: 128, height: 128 },
{ width: 64, suffix: 'thumbnail' }
]
```
##### addOriginal
Type: `boolean`
Default: `false`
Whether adding the original input file to the destination directory or not.
## License
MIT
'use strict';
const Lab = require('lab');
const Code = require('code');
const Hapi = require('hapi');
const Plugin = require('..');
const { expect } = require('code');
const { describe, it } = exports.lab = require('lab').script();
const Plugin = require('../');
const lab = exports.lab = Lab.script();
describe('Plugin Registration', () => {
it('should registers successfully and expose upload method', () => {
lab.experiment('Plugin Registration', () => {
const server = new Hapi.Server();
lab.test('it registers successfully', (done) => {
expect(async () => await server.register(Plugin)).to.not.throws();
const server = new Hapi.Server();
server.register(Plugin, (err) => {
const { uploader } = server.plugins[Plugin.name];
Code.expect(err).to.not.exist();
done();
});
expect(uploader).to.be.function();
expect(uploader()).to.rejects();
});
});

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc