salix/modules/item/back/methods/item-image-queue/download.js

192 lines
6.0 KiB
JavaScript

const axios = require('axios');
const uuid = require('uuid');
const fs = require('fs/promises');
const {createWriteStream} = require('fs');
const path = require('path');
const gm = require('gm');
module.exports = Self => {
Self.remoteMethod('download', {
description: 'Processes the image download queue',
accessType: 'WRITE',
http: {
path: `/download`,
verb: 'POST',
},
});
Self.download = async() => {
const models = Self.app.models;
const tempContainer = await models.TempContainer.container(
'salix-image'
);
const tempPath = path.join(
tempContainer.client.root,
tempContainer.name
);
const maxAttempts = 3;
const collectionName = 'catalog';
const tx = await Self.beginTransaction({});
let tempFilePath;
let queueRow;
try {
const myOptions = {transaction: tx};
queueRow = await Self.findOne(
{
fields: ['id', 'itemFk', 'url', 'attempts'],
where: {
url: {neq: null},
attempts: {
lt: maxAttempts,
},
},
order: 'priority, attempts, updated',
},
myOptions
);
if (!queueRow) return;
const collection = await models.ImageCollection.findOne(
{
fields: [
'id',
'maxWidth',
'maxHeight',
'model',
'property',
],
where: {name: collectionName},
include: {
relation: 'sizes',
scope: {
fields: ['width', 'height', 'crop'],
},
},
},
myOptions
);
const fileName = `${uuid.v4()}.png`;
tempFilePath = path.join(tempPath, fileName);
// Insert image row
await models.Image.create(
{
name: fileName,
collectionFk: collectionName,
updated: Date.vnNow(),
},
myOptions
);
// Update item
const model = models[collection.model];
if (!model) throw new Error('No matching model found');
const item = await model.findById(queueRow.itemFk, null, myOptions);
if (item) {
await item.updateAttribute(
collection.property,
fileName,
myOptions
);
}
// Download remote image
const response = await axios.get(queueRow.url, {
responseType: 'stream',
});
const writeStream = createWriteStream(tempFilePath);
await new Promise((resolve, reject) => {
writeStream.on('open', () => response.data.pipe(writeStream));
writeStream.on('finish', () => resolve());
writeStream.on('error', error => reject(error));
});
// Resize
const container = await models.ImageContainer.container(
collectionName
);
const rootPath = container.client.root;
const collectionDir = path.join(rootPath, collectionName);
// To max size
const {maxWidth, maxHeight} = collection;
const fullSizePath = path.join(collectionDir, 'full');
const toFullSizePath = `${fullSizePath}/${fileName}`;
await fs.mkdir(fullSizePath, {recursive: true});
await new Promise((resolve, reject) => {
gm(tempFilePath)
.resize(maxWidth, maxHeight, '>')
.setFormat('png')
.write(toFullSizePath, function(err) {
if (err) reject(err);
if (!err) resolve();
});
});
// To collection sizes
for (const size of collection.sizes()) {
const {width, height} = size;
const sizePath = path.join(collectionDir, `${width}x${height}`);
const toSizePath = `${sizePath}/${fileName}`;
await fs.mkdir(sizePath, {recursive: true});
await new Promise((resolve, reject) => {
const gmInstance = gm(tempFilePath);
if (size.crop) {
gmInstance
.resize(width, height, '^')
.gravity('Center')
.crop(width, height);
}
if (!size.crop) gmInstance.resize(width, height, '>');
gmInstance
.setFormat('png')
.write(toSizePath, function(err) {
if (err) reject(err);
if (!err) resolve();
});
});
}
try {
await fs.unlink(tempFilePath);
} catch (error) { }
await queueRow.destroy(myOptions);
// Restart queue
Self.download();
await tx.commit();
} catch (error) {
await tx.rollback();
if (queueRow.attempts < maxAttempts) {
await queueRow.updateAttributes({
error: error,
attempts: queueRow.attempts + 1,
updated: Date.vnNew(),
});
}
try {
await fs.unlink(tempFilePath);
} catch (error) { }
Self.download();
}
};
};