-
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(users, uploads, lib): init uploadx example with gridfs & stream ✨
- Loading branch information
1 parent
7f78c00
commit e991ecb
Showing
32 changed files
with
685 additions
and
118 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,70 @@ | ||
module.exports.imageFileFilter = (req, file, callback) => { | ||
if (file.mimetype !== 'image/png' && file.mimetype !== 'image/jpg' && file.mimetype !== 'image/jpeg' && file.mimetype !== 'image/gif') { | ||
const err = new Error(); | ||
err.code = 'UNSUPPORTED_MEDIA_TYPE'; | ||
return callback(err, false); | ||
/** | ||
* Module dependencies. | ||
*/ | ||
const path = require('path'); | ||
const crypto = require('crypto'); | ||
const multer = require('multer'); | ||
const { createBucket } = require('mongoose-gridfs'); | ||
|
||
const AppError = require(path.resolve('./lib/helpers/AppError')); | ||
|
||
let storage; | ||
|
||
/** | ||
* @desc File filter | ||
* @param {Array} formats - array of accepted mimetype string | ||
* @return {callback} | ||
*/ | ||
const fileFilter = (formats) => (req, file, callback) => { | ||
if (formats.includes(file.mimetype)) callback(null, true); | ||
else callback(new AppError(`Only ${formats} images allowed`, { code: 'SERVICE_ERROR' }), false); | ||
}; | ||
|
||
/** | ||
* set Strorage | ||
*/ | ||
module.exports.setStorage = () => { | ||
storage = createBucket({ | ||
bucketName: 'uploads', | ||
model: 'Uploads', | ||
}); | ||
}; | ||
|
||
/** | ||
* @desc file upload middleware | ||
* @param {String} name - key data name in form-data | ||
* @param {Object} config - multer config | ||
* @return {callback} | ||
*/ | ||
module.exports.create = (name, config) => async (req, res, next) => { | ||
// set options | ||
const options = config || {}; | ||
if (options.formats) { | ||
options.fileFilter = fileFilter(options.formats); | ||
delete options.formats; | ||
} | ||
callback(null, true); | ||
// set storage | ||
options.storage = storage; | ||
// upload | ||
const upload = multer(options).single(name); | ||
upload(req, res, (err) => { | ||
if (err) { | ||
req.multerErr = err; | ||
next(); | ||
} else { | ||
next(); | ||
} | ||
}); | ||
}; | ||
|
||
/** | ||
* @desc Generate file name | ||
* @param {String} filename - original filename | ||
* @return {resolve} | ||
*/ | ||
module.exports.generateFileName = (filename) => new Promise((resolve, reject) => { | ||
crypto.randomBytes(32, (err, buf) => { | ||
if (err) reject(new AppError('Error generateFileName', { code: 'SERVICE_ERROR' })); | ||
resolve(buf.toString('hex') + path.extname(filename)); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/** | ||
* Module dependencies | ||
*/ | ||
const path = require('path'); | ||
|
||
const errors = require(path.resolve('./lib/helpers/errors')); | ||
const responses = require(path.resolve('./lib/helpers/responses')); | ||
const UploadsService = require('../services/uploads.service'); | ||
|
||
/** | ||
* @desc Endpoint to get an upload by fileName | ||
* @param {Object} req - Express request object | ||
* @param {Object} res - Express response object | ||
*/ | ||
exports.get = async (req, res) => { | ||
try { | ||
const stream = await UploadsService.getStream({ _id: req.upload._id }); | ||
if (!stream) responses.error(res, 404, 'Not Found', 'No Upload with that identifier can been found')(); | ||
stream.on('error', (err) => { | ||
responses.error(res, 422, 'Unprocessable Entity', errors.getMessage(err))(err); | ||
}); | ||
res.set('Content-Type', req.upload.contentType); | ||
stream.pipe(res); | ||
} catch (err) { | ||
responses.error(res, 422, 'Unprocessable Entity', errors.getMessage(err))(err); | ||
} | ||
}; | ||
|
||
/** | ||
* @desc Endpoint to delete an upload | ||
* @param {Object} req - Express request object | ||
* @param {Object} res - Express response object | ||
*/ | ||
exports.delete = async (req, res) => { | ||
try { | ||
await UploadsService.delete({ _id: req.upload._id }); | ||
responses.success(res, 'upload deleted')(); | ||
} catch (err) { | ||
responses.error(res, 422, 'Unprocessable Entity', errors.getMessage(err))(err); | ||
} | ||
}; | ||
|
||
/** | ||
* @desc MiddleWare to ask the service the uppload for this uploadName | ||
* @param {Object} req - Express request object | ||
* @param {Object} res - Express response object | ||
* @param {Function} next - Express next middleware function | ||
* @param {String} filename - upload filename | ||
*/ | ||
exports.uploadByName = async (req, res, next, uploadName) => { | ||
try { | ||
const upload = await UploadsService.get(uploadName); | ||
if (!upload) responses.error(res, 404, 'Not Found', 'No Upload with that name has been found')(); | ||
else { | ||
req.upload = upload; | ||
req.isOwner = upload.metadata.user; // used if we proteck road by isOwner policy | ||
next(); | ||
} | ||
} catch (err) { | ||
next(err); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/** | ||
* Module dependencies | ||
*/ | ||
const mongoose = require('mongoose'); | ||
|
||
const Schema = mongoose.Schema; | ||
|
||
/** | ||
* Data Model Mongoose | ||
*/ | ||
const UploadsMongoose = new Schema({ | ||
length: Number, | ||
chunkSize: Number, | ||
uploadDate: Date, | ||
md5: String, | ||
filename: String, | ||
contentType: String, | ||
metadata: { | ||
user: { | ||
type: Schema.ObjectId, | ||
ref: 'User', | ||
}, | ||
}, | ||
}, { strict: false }); | ||
|
||
mongoose.model('Uploads', UploadsMongoose, 'uploads.files'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
/** | ||
* Module dependencies | ||
* */ | ||
const path = require('path'); | ||
|
||
const policy = require(path.resolve('./lib/middlewares/policy')); | ||
|
||
/** | ||
* Invoke Uploads Permissions | ||
*/ | ||
exports.invokeRolesPolicies = () => { | ||
policy.Acl.allow([{ | ||
roles: ['user', 'admin'], | ||
allows: [{ | ||
resources: '/api/uploads/:uploadName', | ||
permissions: ['get', 'delete'], | ||
}], | ||
}]); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/** | ||
* Module dependencies | ||
*/ | ||
const mongoose = require('mongoose'); | ||
const { createModel } = require('mongoose-gridfs'); | ||
const path = require('path'); | ||
|
||
const AppError = require(path.resolve('./lib/helpers/AppError')); | ||
const Attachment = createModel({ bucketName: 'uploads' }); | ||
const Uploads = mongoose.model('Uploads'); | ||
|
||
|
||
/** | ||
* @desc Function to get all upload in db with filter or not | ||
* @return {Array} uploads | ||
*/ | ||
exports.list = (filter) => Uploads.find(filter).select('filename uploadDate contentType').sort('-createdAt').exec(); | ||
|
||
/** | ||
* @desc Function to get an upload from db | ||
* @param {String} id | ||
* @return {Stream} upload | ||
*/ | ||
exports.get = (uploadName) => Uploads.findOne({ filename: uploadName }).exec(); | ||
|
||
/** | ||
* @desc Function to get an upload from db | ||
* @param {String} id | ||
* @return {Stream} upload | ||
*/ | ||
exports.getStream = (upload) => Attachment.read(upload); | ||
|
||
/** | ||
* @desc Function to update an upload in db | ||
* @param {ObjectID} upload ID | ||
* @param {Object} update | ||
* @return {Object} upload updated | ||
*/ | ||
exports.update = (id, update) => Uploads.findOneAndUpdate({ _id: id }, update, { new: true }).exec(); | ||
|
||
/** | ||
* @desc Function to delete an upload from db | ||
* @param {String} id | ||
* @return {Object} confirmation of delete | ||
*/ | ||
exports.delete = async (upload) => { | ||
if (!upload._id) upload = await Uploads.findOne({ filename: upload.filename }).exec(); | ||
if (upload) { | ||
Attachment.unlink(upload._id, (err, unlinked) => { | ||
if (err) throw new AppError('Upload: delete error', { code: 'REPOSITORY_ERROR', details: err }); | ||
return unlinked; | ||
}); | ||
} | ||
}; | ||
|
||
/** | ||
* @desc Function to delete uploads of one user in db | ||
* @param {Object} filter | ||
* @return {Object} confirmation of delete | ||
*/ | ||
exports.deleteMany = async (filter) => { | ||
const uploads = await this.list(filter); | ||
uploads.forEach((upload) => { | ||
Attachment.unlink(upload._id, (err, unlinked) => { | ||
if (err) throw new AppError('Upload: delete error', { code: 'REPOSITORY_ERROR', details: err }); | ||
return unlinked; | ||
}); | ||
}); | ||
return { deletedCount: uploads.length }; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/** | ||
* Module dependencies | ||
*/ | ||
const passport = require('passport'); | ||
const path = require('path'); | ||
|
||
const policy = require(path.resolve('./lib/middlewares/policy')); | ||
const uploads = require('../controllers/uploads.controller'); | ||
|
||
/** | ||
* Routes | ||
*/ | ||
module.exports = (app) => { | ||
// classic crud | ||
app.route('/api/uploads/:uploadName').all(passport.authenticate('jwt'), policy.isAllowed) | ||
.get(uploads.get) | ||
.delete(policy.isOwner, uploads.delete); // delete | ||
|
||
// Finish by binding the task middleware | ||
app.param('uploadName', uploads.uploadByName); | ||
}; |
Oops, something went wrong.