hedera-web/back/common/models/image.js

158 lines
4.9 KiB
JavaScript

const md5 = require('md5');
const fs = require('fs-extra');
const sharp = require('sharp');
module.exports = Self => {
Self.remoteMethod('upload', {
description: 'Uploads an image',
accessType: 'WRITE',
accepts: [
{
arg: 'ctx',
type: 'Object',
http: {source: 'context'}
}
],
returns: {
type: Self.modelName,
description: 'The resulting file instance',
root: true,
},
http: {
path: `/upload`,
verb: 'POST'
}
});
Self.upload = async ctx => {
let app = Self.app;
let $ = app.models;
let storageConnector = app.dataSources.storage.connector;
let tx = await Self.beginTransaction({});
let myOptions = {transaction: tx};
async function getContainer(name) {
let container;
try {
container = await $.Container.getContainer(name);
} catch (err) {
if (err.code === 'ENOENT') {
container = await $.Container.createContainer({
name: name
});
} else throw err;
}
return container;
}
try {
// Upload file to temporary path
let tempContainer = await getContainer('temp');
let uploaded = await $.Container.upload(tempContainer.name, ctx.req, ctx.result, {});
let files = Object.values(uploaded.files).map(file => file[0]);
let args = {};
for (let key in uploaded.fields)
args[key] = uploaded.fields[key][0];
if (!/^[a-z0-9_]+$/.test(args.name))
throw new Error('Bad file name');
let collection = await $.ImageCollection.findOne({
fields: [
'id',
'name',
'maxWidth',
'maxHeight',
'model',
'property'
],
where: {name: args.collectionFk},
include: {
relation: 'sizes',
scope: {
fields: ['width', 'height', 'crop']
}
}
});
let md5Hash = md5(args.name).substring(0, 4);
let md5Path = md5Hash.match(/(..?)/g).join('/');
let rootPath = storageConnector.client.root;
let file = files[0];
let data = {
name: args.name,
collectionFk: args.collectionFk
};
let newImage = await Self.upsertWithWhere(data, {
name: args.name,
collectionFk: args.collectionFk,
updated: (new Date).getTime()
}, myOptions);
// Resizes and saves the image
let extension = file.name.split('.').pop().toLowerCase();
let srcPath = `${rootPath}/${tempContainer.name}/${file.name}`;
let collectionDir = `${rootPath}/${args.collectionFk}`;
let dstDir = `${collectionDir}/${md5Path}/${args.name}`;
let dstFile = `full.${extension}`;
let resizeOpts = {
withoutEnlargement: true,
fit: 'inside'
};
await fs.mkdir(dstDir, {recursive: true});
await sharp(srcPath)
.resize(collection.maxWidth, collection.maxHeight, resizeOpts)
.toFile(`${dstDir}/${dstFile}`);
let sizes = collection.sizes();
for (let size of sizes) {
let dstFile = `${size.width}x${size.height}.${extension}`;
let resizeOpts = {
withoutEnlargement: true,
fit: size.crop ? 'cover' : 'inside'
};
await sharp(srcPath)
.resize(size.width, size.height, resizeOpts)
.toFile(`${dstDir}/${dstFile}`);
}
// Updates items with matching id, when option is checked
if (args.updateMatching === 'true') {
if (!collection.model || !collection.property)
throw new Error('Matching model settings not defined');
let model = app.models[collection.model];
if (!model)
throw new Error('Matching model not found');
let item = await model.findById(args.name, null, myOptions);
if (item)
await item.updateAttribute(
collection.property,
args.name,
myOptions
);
}
await fs.unlink(srcPath);
await tx.commit();
return newImage;
} catch (e) {
await tx.rollback();
throw e;
}
};
};