kraken-js
Version:
An express-based Node.js web application bootstrapping module.
128 lines (111 loc) • 4.78 kB
JavaScript
/*───────────────────────────────────────────────────────────────────────────*\
│ Copyright 2016 PayPal │
│ │
│hh ,'""`. │
│ / _ _ \ Licensed under the Apache License, Version 2.0 (the "License"); │
│ |(@)(@)| you may not use this file except in compliance with the License. │
│ ) __ ( You may obtain a copy of the License at │
│ /,'))((`.\ │
│(( (( )) )) http://www.apache.org/licenses/LICENSE-2.0 │
│ `\ `)(' /' │
│ │
│ Unless required by applicable law or agreed to in writing, software │
│ distributed under the License is distributed on an "AS IS" BASIS, │
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
│ See the License for the specific language governing permissions and │
│ limitations under the License. │
\*───────────────────────────────────────────────────────────────────────────*/
;
var fs = require('fs'),
formidable = require('formidable'),
{ firstValues } = require('formidable/src/helpers/firstValues.js'),
debug = require('debuglog')('kraken/middleware/multipart');
var IncomingForm = formidable.IncomingForm;
/**
* Filters requests for only those that are `multipart/form-data`
* @param fn the handler to invoke
* @returns {Function}
*/
function filter(fn) {
return function multipart(req, res, next) {
var contentType = req.headers['content-type'];
if (typeof contentType === 'string' && ~contentType.indexOf('multipart/form-data')) {
debug('received multipart request');
fn.apply(null, arguments);
return;
}
next();
};
}
/**
* simple forEach that enumerates an object's properties
* @param obj the object to enumerate
* @param callback the callback to invoke, with the signature `callback(value, key, obj)`
*/
function forEachValue(obj, callback) {
Object.keys(obj).forEach(function (item) {
callback(obj[item], item, obj);
});
}
/**
* Cleanup handler factory
* @param files the list of files to clean up
* @returns {function(this:null)}
*/
function cleanify(files) {
return forEachValue.bind(null, files, funlink);
}
/**
* Removes the provided file from the filesystem
* @param file a formidable File object
*/
function funlink(file) {
// Handle both formidable 1.x and 3.x file object properties
var path = file.filepath ?? file.path; // 3.x uses filepath, 1.x uses path
var name = file.originalFilename ?? file.name; // 3.x uses originalFilename, 1.x uses name
debug('removing', name);
if (typeof path === 'string') {
fs.unlink(path, function (err) {
if (err) {
debug('Failed to remove ' + path);
debug(err);
}
});
}
}
module.exports = function (config) {
config = config || {};
return filter(function (req, res, next) {
var form = new IncomingForm(config);
form.parse(req, function (err, fields, files) {
if (err) {
next(err);
return;
}
// Use firstValues to handle formidable 3.x array format
req.body = firstValues(form, fields);
req.files = addBackwardCompatibility(firstValues(form, files));
res.once('finish', cleanify(req.files));
next();
});
});
};
/**
* Add backward compatibility properties to file objects
* @param files the files object from formidable
*/
function addBackwardCompatibility(files) {
if (!files) {
return {};
}
Object.keys(files).forEach(function(key) {
var file = files[key];
if (file) {
// Add backward compatibility properties for both formidable versions
file.path ??= file.filepath;
file.name ??= file.originalFilename;
file.type ??= file.mimetype;
}
});
return files;
}