hapi-darwin
Advanced tools
Comparing version 1.0.1 to 1.0.2
205
index.js
@@ -1,19 +0,13 @@ | ||
/* | ||
* @tavousi / created as plugin for hapijs | ||
* Hapi-darwin@v1.0.1 simple image storage | ||
* focused on upload single image with imagemagick* | ||
* ~fetures : just look apis ;) | ||
* ~Problems : | ||
* if set name in options for multiple mode you get bad results* | ||
* if your file is duplicate, the darwin cant rename it* | ||
* if you want to upload single file i suggest darwin*** | ||
* ~You need more ? | ||
* write yourself ! | ||
/* 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 async from 'async'; | ||
import mkdirp from 'mkdirp'; | ||
import { extname, join } from 'path'; | ||
import { promisify } from 'util'; | ||
import { basename, extname, join } from 'path'; | ||
import fileType from 'file-type'; | ||
@@ -25,23 +19,37 @@ import readChunk from 'read-chunk'; | ||
const sizeOf = promisify(require('image-size')); | ||
internals.uploader = (file, options = {}) => { | ||
if (!file) throw new Error('No file(s) recived !'); | ||
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, | ||
// safeName: false, Soon | ||
safeName: true, | ||
delOrginal: false, // when using `imageVersions` | ||
maxFiles: 1, | ||
minFileSize: 0.001 * 1024 * 1024, // 10 KB | ||
maxFileSize: 1 * 1024 * 1024, // 1 MB | ||
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-80']: { | ||
width: 80, | ||
height: 80, | ||
imageArgs: ['-auto-orient'] | ||
} | ||
} | ||
// 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); | ||
@@ -51,3 +59,3 @@ | ||
if (file.length > internals.options.maxFiles) throw new Error('Maximum files recived !'); | ||
if (file.length > internals.options.maxFiles) throw new Error('Maximum files recived'); | ||
@@ -59,3 +67,3 @@ const promises = file.map(each => internals._fileHandler(each)); | ||
} else { | ||
if (file.length > 1) throw new Error('Maximum files recived !'); | ||
if (file.length > 1) throw new Error('Maximum files recived'); | ||
@@ -75,3 +83,4 @@ return internals._fileHandler(file); | ||
internals.validateAfterRecived = (fileDetails) => { | ||
internals.validateAfterRecived = async (fileDetails) => { | ||
const dimensions = await sizeOf(fileDetails.path); | ||
const buffer = readChunk.sync(fileDetails.path, 0, 4100); | ||
@@ -88,19 +97,51 @@ const file = fileType(buffer); | ||
} else if (!internals.options.filter.test(`x.${file.ext}`)) { | ||
error = 'Filetype (chunk) not allowed'; | ||
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; | ||
return error; | ||
}; | ||
internals._fileHandler = (file) => { | ||
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; | ||
const filename = internals.options.name ? `${internals.options.name}${extname(orignalname)}` : orignalname; | ||
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 fileStream = fs.createWriteStream(path); | ||
const imageVersions = Object.keys(internals.options.imageVersions); | ||
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('type not allowed'); | ||
throw new Error('Filetype not allowed'); | ||
} | ||
@@ -127,3 +168,4 @@ | ||
url: join(internals.options.urlDestination, filename), | ||
size: fs.statSync(path).size, | ||
size: fs.statSync(tmpPath).size, | ||
delOrginal: internals.options.delOrginal ? true : false, | ||
versions: imageVersions.length > 0 ? {} : undefined | ||
@@ -134,37 +176,63 @@ } | ||
const validate = await internals.validateAfterRecived(fileDetails); | ||
const validate = await internals.validateAfterRecived({ | ||
path: tmpPath, | ||
size: fileDetails.size | ||
}); | ||
if (!validate) { | ||
fs.unlinkSync(fileDetails.path); | ||
return reject(validate); | ||
if (validate) { | ||
await internals.deleteFileFs(tmpPath); | ||
return reject(new Error(validate)); | ||
} | ||
if (imageVersions.length === 0) finish(); | ||
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); | ||
} | ||
}); | ||
}); | ||
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'] | ||
}, (err, stdout, stderr) => { | ||
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++; | ||
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) | ||
} | ||
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) finish(); | ||
if (imageVersions.length === counter) { | ||
if (internals.options.delOrginal) await internals.deleteFileFs(path); | ||
finish(); | ||
} | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
} | ||
} | ||
}) | ||
@@ -174,2 +242,6 @@ }) | ||
internals.deleteFileFs = async (filePath) => { | ||
fs.unlinkSync(filePath); | ||
}; | ||
export function register(server, options, next) { | ||
@@ -179,3 +251,4 @@ | ||
server.expose('uploader', internals.uploader); | ||
server.expose('uploader', internals.uploader); | ||
server.expose('deleteFileFs', internals.deleteFileFs); | ||
@@ -186,3 +259,3 @@ next(); | ||
exports.register.attributes = { | ||
pkg: require('./package.json') | ||
}; | ||
name: 'hapi-darwin' | ||
}; |
{ | ||
"name": "hapi-darwin", | ||
"version": "1.0.1", | ||
"description": "image storage", | ||
"version": "1.0.2", | ||
"description": "simple image storage", | ||
"main": "index.js", | ||
@@ -27,3 +27,10 @@ "scripts": { | ||
"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" | ||
} | ||
} |
@@ -64,4 +64,40 @@ # hapi-darwin | ||
## Config | ||
```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); | ||
``` | ||
## License | ||
MIT |
14202
214
103
5
+ Addedfile-type@^7.4.0
+ Addedimage-size@^0.6.1
+ Addedimagemagick@^0.1.3
+ Addedmkdirp@^0.5.1
+ Addedread-chunk@^2.1.0
+ Addedfile-type@7.7.1(transitive)
+ Addedimage-size@0.6.3(transitive)
+ Addedimagemagick@0.1.3(transitive)
+ Addedminimist@1.2.8(transitive)
+ Addedmkdirp@0.5.6(transitive)
+ Addedpify@3.0.0(transitive)
+ Addedread-chunk@2.1.0(transitive)
+ Addedsafe-buffer@5.2.1(transitive)