WIP - merges media storage

This commit is contained in:
James
2019-05-06 17:45:13 -04:00
8 changed files with 144 additions and 80 deletions

14
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,14 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}/demo/init.js"
}
]
}

View File

@@ -0,0 +1,69 @@
import mkdirp from 'mkdirp';
import { resizeAndSave } from '../utils/imageResizer';
import httpStatus from 'http-status';
import modelById from '../resolvers/modelById';
export async function update(req, res, next, config) {
req.model.setDefaultLocale(req.locale);
let doc = await modelById(req, { returnRawDoc: true });
if (!doc)
return res.status(httpStatus.NOT_FOUND).send('Not Found');
Object.keys(req.body).forEach(e => {
doc[e] = req.body[e];
});
if (req.files && req.files.file) {
doc['filename'] = req.files.file.name;
let outputFilepath = `${config.staticDir}/${req.files.file.name}`;
let moveError = await req.files.file.mv(outputFilepath);
if (moveError) return res.status(500).send(moveError);
doc['sizes'] = await resizeAndSave(config, req.files.file);
}
doc.save((saveError) => {
if (saveError)
return res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ error: saveError });
return res.json({
message: 'success',
result: doc.toJSON({ virtuals: true })
});
});
}
export async function upload(req, res, next, config) {
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send('No files were uploaded.');
}
mkdirp(config.staticDir, (err) => {
if (err) {
console.error(err);
res.status(500).send('Upload failed.');
}
});
let outputFilepath = `${config.staticDir}/${req.files.file.name}`;
let moveError = await req.files.file.mv(outputFilepath);
if (moveError) return res.status(500).send(moveError);
let outputSizes = await resizeAndSave(config, req.files.file);
req.model.create({
name: req.body.name,
caption: req.body.caption,
description: req.body.description,
filename: req.files.file.name,
sizes: outputSizes
}, (mediaCreateError, result) => {
if (mediaCreateError)
return res.status(500).json({ error: mediaCreateError });
return res.status(201)
.json({
message: 'success',
result: result.toJSON({ virtuals: true })
});
});
}

View File

@@ -1,44 +0,0 @@
import mkdirp from 'mkdirp';
import { resize } from '../../src/utils/imageResizer';
import Media from '../models/Media.model';
function upload(req, res, next, config) {
if (Object.keys(req.files).length === 0) {
return res.status(400).send('No files were uploaded.');
}
mkdirp(config.staticDir, (err) => {
if (err) {
console.error(err);
res.status(500).send('Upload failed.');
}
});
let outputFilepath = `${config.staticDir}/${req.files.file.name}`;
req.files.file.mv(outputFilepath, (err) => {
if (err) return res.status(500).send(err);
if (req.files.file.mimetype.split('/')[0] === 'image') {
resize(config, req.files.file);
}
Media.create({
name: req.files.file.name,
filename: req.files.file.name
}, (err, result) => {
if (err)
return res.status(500).json({ error: err });
return res.status(201)
.json({
message: 'success',
result: {
id: result.id,
name: result.name
}
});
});
})
}
export default { upload };

View File

@@ -1,15 +1,28 @@
import mongoose from 'mongoose'; import mongoose from 'mongoose';
import buildQuery from '../plugins/buildQuery'; import buildQuery from '../plugins/buildQuery';
import paginate from '../plugins/paginate'; import paginate from '../plugins/paginate';
import internationalization from '../plugins/internationalization';
const MediaSchema = new mongoose.Schema({ const mediaModelLoader = (config) => {
name: { type: String }, const MediaSchema = new mongoose.Schema({
caption: { type: String }, name: { type: String, intl: true },
description: { type: String }, caption: { type: String, intl: true },
filename: { type: String }, description: { type: String, intl: true },
}); filename: { type: String },
sizes: [{
height: { type: Number},
width: { type: Number},
_id: false
}]
},
{ timestamps: true }
);
MediaSchema.plugin(paginate); MediaSchema.plugin(paginate);
MediaSchema.plugin(buildQuery); MediaSchema.plugin(buildQuery);
MediaSchema.plugin(internationalization, config.localization);
export default mongoose.model('Media', MediaSchema); return mongoose.model('Media', MediaSchema);
};
export default mediaModelLoader;

View File

@@ -1,6 +1,6 @@
import httpStatus from 'http-status'; import httpStatus from 'http-status';
import { modelById } from '../resolvers'; import { modelById } from '../resolvers';
import {createAutopopulateOptions} from '../helpers/mongoose/createAutopopulateOptions'; import { createAutopopulateOptions } from '../helpers/mongoose/createAutopopulateOptions';
const findOne = (req, res) => { const findOne = (req, res) => {
@@ -10,7 +10,7 @@ const findOne = (req, res) => {
locale: req.locale, locale: req.locale,
fallback: req.query['fallback-locale'] fallback: req.query['fallback-locale']
}; };
modelById(query, {...createAutopopulateOptions(req.query.depth)}) modelById(query, { ...createAutopopulateOptions(req.query.depth) })
.then(doc => res.json(doc)) .then(doc => res.json(doc))
.catch(err => res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ error: err })); .catch(err => res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ error: err }));
}; };

View File

@@ -1,4 +1,4 @@
const modelById = (query, options)=> { const modelById = (query, options) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
query.Model.findOne({ _id: query.id }, {}, options, (err, doc) => { query.Model.findOne({ _id: query.id }, {}, options, (err, doc) => {
@@ -11,10 +11,12 @@ const modelById = (query, options)=> {
if (query.locale) { if (query.locale) {
doc.setLocale(query.locale, query.fallback); doc.setLocale(query.locale, query.fallback);
const json = doc.toJSON({ virtuals: true }); result = doc.toJSON({ virtuals: true });
result = json;
} }
resolve(result);
resolve(options.returnRawDoc
? doc
: result);
}) })
}) })
}; };

View File

@@ -1,25 +1,32 @@
import express from 'express'; import express from 'express';
import passport from 'passport'; import passport from 'passport';
import uploadsCtrl from '../controllers/uploads.controller'; import { upload, update } from '../controllers/media.controller';
import { query } from '../requestHandlers'; import { query } from '../requestHandlers';
import bindModel from '../middleware/bindModel'; import bindModel from '../middleware/bindModel';
import Media from '../models/Media.model'; import mediaModelLoader from '../models/Media.model';
const router = express.Router(); const router = express.Router();
const mediaRoutes = config => { const mediaRoutes = config => {
router.all('*', bindModel(Media)); const mediaModel = mediaModelLoader(config); // Needs config for intl
router.all('*', bindModel(mediaModel));
router router
.route('') .route('')
.post( .post(
passport.authenticate('jwt', { session: false }), passport.authenticate('jwt', { session: false }),
(req, res, next) => uploadsCtrl.upload(req, res, next, config) (req, res, next) => upload(req, res, next, config)
);
router
.route('/:_id')
.put(
passport.authenticate('jwt', { session: false }),
(req, res, next) => update(req, res, next, config)
); );
router.route('') router.route('')
.get( .get(
passport.authenticate('jwt', { session: false }),
query query
); );

View File

@@ -1,5 +1,6 @@
import sharp from 'sharp'; import sharp from 'sharp';
import sizeOf from 'image-size'; const { promisify } = require('util');
const sizeOf = promisify(require('image-size'));
function getOutputImageName(sourceImage, size) { function getOutputImageName(sourceImage, size) {
let extension = sourceImage.split('.').pop(); let extension = sourceImage.split('.').pop();
@@ -7,25 +8,27 @@ function getOutputImageName(sourceImage, size) {
return `${filenameWithoutExtension}-${size.width}x${size.height}.${extension}`; return `${filenameWithoutExtension}-${size.width}x${size.height}.${extension}`;
} }
export function resize(config, file) { export async function resizeAndSave(config, file) {
let sourceImage = `${config.staticDir}/${file.name}`; let sourceImage = `${config.staticDir}/${file.name}`;
sizeOf(sourceImage, (err, dimensions) => { let outputSizes = [];
for (let size of config.imageSizes) { try {
if (size.width > dimensions.width) { const dimensions = await sizeOf(sourceImage);
console.log(`${size.width} is greater than actual width ${dimensions.width}`); for (let desiredSize of config.imageSizes) {
if (desiredSize.width > dimensions.width) {
continue; continue;
} }
let outputImageName = getOutputImageName(sourceImage, size); let outputImageName = getOutputImageName(sourceImage, desiredSize);
sharp(sourceImage) await sharp(sourceImage)
.resize(size.width, size.height, { .resize(desiredSize.width, desiredSize.height, {
position: size.crop || 'centre' position: desiredSize.crop || 'centre'
}) })
.toFile(outputImageName, (err) => { .toFile(outputImageName);
if (err) console.log('Error writing resized file', err); outputSizes.push({ height: desiredSize.height, width: desiredSize.width });
console.log(`Resized image from ${dimensions.width}x${dimensions.height} to ${size.width}x${size.height}`);
});
} }
}); } catch (e) {
console.log('error in resize and save', e.message);
}
return outputSizes;
} }