Merge pull request #928 from jessopb/xformFileOnServe
implements querystring image transformation
This commit is contained in:
commit
ed9d037155
5 changed files with 555 additions and 399 deletions
802
package-lock.json
generated
802
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -46,6 +46,7 @@
|
|||
"express-http-context": "^1.2.0",
|
||||
"generate-password": "^1.4.1",
|
||||
"get-video-dimensions": "^1.0.0",
|
||||
"gm": "^1.23.1",
|
||||
"helmet": "^3.15.0",
|
||||
"image-size": "^0.6.3",
|
||||
"inquirer": "^5.2.0",
|
||||
|
|
|
@ -15,9 +15,20 @@ const BLOCKED_CLAIM = 'BLOCKED_CLAIM';
|
|||
const NO_FILE = 'NO_FILE';
|
||||
const CONTENT_UNAVAILABLE = 'CONTENT_UNAVAILABLE';
|
||||
|
||||
const { publishing: { serveOnlyApproved, approvedChannels } } = require('@config/siteConfig');
|
||||
const {
|
||||
publishing: { serveOnlyApproved, approvedChannels },
|
||||
} = require('@config/siteConfig');
|
||||
|
||||
const getClaimIdAndServeAsset = (channelName, channelClaimId, claimName, claimId, originalUrl, ip, res, headers) => {
|
||||
const getClaimIdAndServeAsset = (
|
||||
channelName,
|
||||
channelClaimId,
|
||||
claimName,
|
||||
claimId,
|
||||
originalUrl,
|
||||
ip,
|
||||
res,
|
||||
headers
|
||||
) => {
|
||||
getClaimId(channelName, channelClaimId, claimName, claimId)
|
||||
.then(fullClaimId => {
|
||||
claimId = fullClaimId;
|
||||
|
@ -39,19 +50,27 @@ const getClaimIdAndServeAsset = (channelName, channelClaimId, claimName, claimId
|
|||
.then(claim => {
|
||||
let claimDataValues = claim.dataValues;
|
||||
|
||||
if (serveOnlyApproved && !isApprovedChannel({ longId: claimDataValues.publisher_id || claimDataValues.certificateId }, approvedChannels)) {
|
||||
if (
|
||||
serveOnlyApproved &&
|
||||
!isApprovedChannel(
|
||||
{ longId: claimDataValues.publisher_id || claimDataValues.certificateId },
|
||||
approvedChannels
|
||||
)
|
||||
) {
|
||||
throw new Error(CONTENT_UNAVAILABLE);
|
||||
}
|
||||
|
||||
let outpoint = claimDataValues.outpoint || `${claimDataValues.transaction_hash_id}:${claimDataValues.vout}`;
|
||||
let outpoint =
|
||||
claimDataValues.outpoint ||
|
||||
`${claimDataValues.transaction_hash_id}:${claimDataValues.vout}`;
|
||||
logger.debug('Outpoint:', outpoint);
|
||||
return db.Blocked.isNotBlocked(outpoint).then(() => {
|
||||
// If content was found, is approved, and not blocked - log a view.
|
||||
if (headers && headers['user-agent'] && /LBRY/.test(headers['user-agent']) === false) {
|
||||
db.Views.create({
|
||||
time : Date.now(),
|
||||
isChannel : false,
|
||||
claimId : claimDataValues.claim_id || claimDataValues.claimId,
|
||||
time: Date.now(),
|
||||
isChannel: false,
|
||||
claimId: claimDataValues.claim_id || claimDataValues.claimId,
|
||||
publisherId: claimDataValues.publisher_id || claimDataValues.certificateId,
|
||||
ip,
|
||||
});
|
||||
|
@ -70,7 +89,7 @@ const getClaimIdAndServeAsset = (channelName, channelClaimId, claimName, claimId
|
|||
if (!fileRecord) {
|
||||
throw NO_FILE;
|
||||
}
|
||||
serveFile(fileRecord.dataValues, res);
|
||||
serveFile(fileRecord.dataValues, res, originalUrl);
|
||||
})
|
||||
.catch(error => {
|
||||
if (error === NO_CLAIM) {
|
||||
|
@ -98,7 +117,8 @@ const getClaimIdAndServeAsset = (channelName, channelClaimId, claimName, claimId
|
|||
logger.debug('claim was blocked');
|
||||
return res.status(451).json({
|
||||
success: false,
|
||||
message: 'In response to a complaint we received under the US Digital Millennium Copyright Act, we have blocked access to this content from our applications. For more details, see https://lbry.io/faq/dmca',
|
||||
message:
|
||||
'In response to a complaint we received under the US Digital Millennium Copyright Act, we have blocked access to this content from our applications. For more details, see https://lbry.io/faq/dmca',
|
||||
});
|
||||
}
|
||||
if (error === NO_FILE) {
|
||||
|
|
|
@ -1,19 +1,46 @@
|
|||
const logger = require('winston');
|
||||
const transformImage = require('./transformImage');
|
||||
const serveFile = async ({ filePath, fileType }, res, originalUrl) => {
|
||||
const queryObject = {};
|
||||
// TODO: replace quick/dirty try catch with better practice
|
||||
try {
|
||||
originalUrl
|
||||
.split('?')[1]
|
||||
.split('&')
|
||||
.map(pair => {
|
||||
if (pair.includes('=')) {
|
||||
let parr = pair.split('=');
|
||||
queryObject[parr[0]] = parr[1];
|
||||
} else queryObject[pair] = true;
|
||||
});
|
||||
} catch (e) {}
|
||||
|
||||
const serveFile = ({ filePath, fileType }, res) => {
|
||||
if (!fileType) {
|
||||
logger.error(`no fileType provided for ${filePath}`);
|
||||
}
|
||||
|
||||
let mediaType = fileType ? fileType.substr(0, fileType.indexOf('/')) : '';
|
||||
const transform =
|
||||
mediaType === 'image' && queryObject.hasOwnProperty('h') && queryObject.hasOwnProperty('w');
|
||||
|
||||
const sendFileOptions = {
|
||||
headers: {
|
||||
'X-Content-Type-Options' : 'nosniff',
|
||||
'Content-Type' : fileType,
|
||||
'Access-Control-Allow-Origin' : '*',
|
||||
'X-Content-Type-Options': 'nosniff',
|
||||
'Content-Type': fileType,
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept',
|
||||
},
|
||||
};
|
||||
logger.debug(`fileOptions for ${filePath}:`, sendFileOptions);
|
||||
if (transform) {
|
||||
logger.debug(`transforming and sending file`);
|
||||
|
||||
let xformed = await transformImage(filePath, queryObject);
|
||||
res.status(200).set(sendFileOptions.headers);
|
||||
res.end(xformed, 'binary');
|
||||
} else {
|
||||
res.status(200).sendFile(filePath, sendFileOptions);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = serveFile;
|
||||
|
|
76
server/controllers/assets/utils/transformImage.js
Normal file
76
server/controllers/assets/utils/transformImage.js
Normal file
|
@ -0,0 +1,76 @@
|
|||
const gm = require('gm');
|
||||
const logger = require('winston');
|
||||
const imageMagick = gm.subClass({ imageMagick: true });
|
||||
const { getImageHeightAndWidth } = require('../../../utils/imageProcessing');
|
||||
|
||||
module.exports = function transformImage(path, queryObj) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let { h: cHeight = null } = queryObj;
|
||||
let { w: cWidth = null } = queryObj;
|
||||
let { t: transform = null } = queryObj;
|
||||
let { x: xOrigin = null } = queryObj;
|
||||
let { y: yOrigin = null } = queryObj;
|
||||
let oHeight,
|
||||
oWidth = null;
|
||||
try {
|
||||
getImageHeightAndWidth(path).then(hwarr => {
|
||||
oHeight = hwarr[0];
|
||||
oWidth = hwarr[1];
|
||||
// conditional logic here
|
||||
if (transform === 'crop') {
|
||||
resolve(_cropCenter(path, cWidth, cHeight, oWidth, oHeight));
|
||||
} else if (transform === 'stretch') {
|
||||
imageMagick(path)
|
||||
.resize(cWidth, cHeight, '!')
|
||||
.toBuffer(null, (err, buf) => {
|
||||
resolve(buf);
|
||||
});
|
||||
} else {
|
||||
// resize scaled
|
||||
imageMagick(path)
|
||||
.resize(cWidth, cHeight)
|
||||
.toBuffer(null, (err, buf) => {
|
||||
resolve(buf);
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function _cropCenter(path, cropWidth, cropHeight, originalWidth, originalHeight) {
|
||||
let oAspect = originalWidth / originalHeight;
|
||||
let cAspect = cropWidth / cropHeight;
|
||||
let resizeX,
|
||||
resizeY,
|
||||
xpoint,
|
||||
ypoint = null;
|
||||
|
||||
if (oAspect >= cAspect) {
|
||||
// if crop is narrower aspect than original
|
||||
resizeY = cropHeight;
|
||||
xpoint = (oAspect * cropHeight) / 2 - cropWidth / 2;
|
||||
ypoint = 0;
|
||||
} else {
|
||||
// if crop is wider aspect than original
|
||||
resizeX = cropWidth;
|
||||
xpoint = 0;
|
||||
ypoint = cropWidth / oAspect / 2 - cropHeight / 2;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
imageMagick(path)
|
||||
.resize(resizeX, resizeY)
|
||||
.crop(cropWidth, cropHeight, xpoint, ypoint)
|
||||
.toBuffer(null, (err, buf) => {
|
||||
resolve(buf);
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue