documents and validates image resize #933
|
@ -260,9 +260,11 @@ Spee.ch has a few types of URL formats that return different assets from the LBR
|
|||
- retrieve the controlling `LBRY` claim:
|
||||
- https://spee.ch/`claim`
|
||||
- https://spee.ch/`claim`.`ext` (serve)
|
||||
- https://spee.ch/`claim`.`ext`&`querystring` (serve transformed)
|
||||
- retrieve a specific `LBRY` claim:
|
||||
- https://spee.ch/`claim_id`/`claim`
|
||||
- https://spee.ch/`claim_id`/`claim`.`ext` (serve)
|
||||
- https://spee.ch/`claim_id`/`claim`.`ext`&`querystring` (serve transformed)
|
||||
- retrieve all contents for the controlling `LBRY` channel
|
||||
- https://spee.ch/`@channel`
|
||||
- a specific `LBRY` channel
|
||||
|
@ -270,9 +272,15 @@ Spee.ch has a few types of URL formats that return different assets from the LBR
|
|||
- retrieve a specific claim within the controlling `LBRY` channel
|
||||
- https://spee.ch/`@channel`/`claim`
|
||||
- https://spee.ch/`@channel`/`claim`.`ext` (serve)
|
||||
- https://spee.ch/`@channel`/`claim`.`ext`&`querystring` (serve)
|
||||
- retrieve a specific claim within a specific `LBRY` channel
|
||||
- https://spee.ch/`@channel`:`channel_id`/`claim`
|
||||
- https://spee.ch/`@channel`:`channel_id`/`claim`.`ext` (serve)
|
||||
- https://spee.ch/`@channel`:`channel_id`/`claim`.`ext`&`querystring` (serve)
|
||||
- `querystring` can include the following transformation values separated by `&`
|
||||
- h=`number` (defines height)
|
||||
- w=`number` (defines width)
|
||||
- t=`crop` or `stretch` (defines transformation - missing implies constrained proportions)
|
||||
|
||||
### Dependencies
|
||||
|
||||
|
|
|
@ -49,6 +49,10 @@
|
|||
}
|
||||
},
|
||||
"serving": {
|
||||
"dynamicFileSizing": {
|
||||
"enabled": true,
|
||||
"maxDimension": 2000
|
||||
},
|
||||
"markdownSettings": {
|
||||
"skipHtmlMain": true,
|
||||
"escapeHtmlMain": true,
|
||||
|
@ -83,10 +87,7 @@
|
|||
"code",
|
||||
"html",
|
||||
"parsedHtml"
|
||||
],
|
||||
"disallowedTypesMain": [],
|
||||
"disallowedTypesDescriptions": ["image", "html"],
|
||||
"disallowedTypesExample": ["image", "html"]
|
||||
]
|
||||
},
|
||||
"customFileExtensions": {
|
||||
"application/x-troff-man": "man",
|
||||
|
|
|
@ -26,7 +26,6 @@ PUBLISHING:
|
|||
|
||||
"primaryClaimAddress": null, - generally supplied by your lbrynet sdk
|
||||
"uploadDirectory": "/home/lbry/Uploads", - lbrynet sdk will know your uploads are here
|
||||
"lbrynetHome": "/home/lbry",
|
||||
"thumbnailChannel": null, - when publishing non-image content, thumbnails will go here.
|
||||
"thumbnailChannelId": null,
|
||||
"additionalClaimAddresses": [],
|
||||
|
@ -50,48 +49,52 @@ PUBLISHING:
|
|||
"application/octet-stream": 50000000
|
||||
}
|
||||
}
|
||||
|
||||
SERVING:
|
||||
|
||||
"markdownSettings": {
|
||||
"skipHtmlMain": true, - false: render html, in a somewhat unpredictable way~
|
||||
"escapeHtmlMain": true, - true: rather than render html, escape it and print it visibly
|
||||
"skipHtmlDescriptions": true, - as above, for descriptions
|
||||
"escapeHtmlDescriptions": true, - as above, for descriptions
|
||||
"allowedTypesMain": [], - markdown rendered as main content
|
||||
"allowedTypesDescriptions": [], - markdown rendered in description in content details
|
||||
"allowedTypesExample": [ - here are examples of allowed types
|
||||
"see react-markdown docs", `https://github.com/rexxars/react-markdown`
|
||||
"root",
|
||||
"text",
|
||||
"break",
|
||||
"paragraph",
|
||||
"emphasis",
|
||||
"strong",
|
||||
"thematicBreak",
|
||||
"blockquote",
|
||||
"delete",
|
||||
"link",
|
||||
"image", - you may not have a lot of control over how these are rendered
|
||||
"linkReference",
|
||||
"imageReference",
|
||||
"table",
|
||||
"tableHead",
|
||||
"tableBody",
|
||||
"tableRow",
|
||||
"tableCell",
|
||||
"list",
|
||||
"listItem",
|
||||
"heading",
|
||||
"inlineCode",
|
||||
"code",
|
||||
"html", - potentially DANGEROUS, intended for `serveOnlyApproved = true` environments, includes iframes, divs.
|
||||
"parsedHtml"
|
||||
],
|
||||
},
|
||||
"customFileExtensions": { - suggest a file extension for experimental content types you may be publishing
|
||||
"application/example-type": "example"
|
||||
}
|
||||
SERVING:
|
||||
|
||||
"dynamicFileSizing": {
|
||||
"enabled": false, - if you choose to allow your instance to serve transform images
|
||||
"maxDimension": 2000 - the maximum size you allow transform to scale
|
||||
},
|
||||
"markdownSettings": {
|
||||
"skipHtmlMain": true, - false: render html, in a somewhat unpredictable way~
|
||||
"escapeHtmlMain": true, - true: rather than render html, escape it and print it visibly
|
||||
"skipHtmlDescriptions": true, - as above, for descriptions
|
||||
"escapeHtmlDescriptions": true, - as above, for descriptions
|
||||
"allowedTypesMain": [], - markdown rendered as main content
|
||||
"allowedTypesDescriptions": [], - markdown rendered in description in content details
|
||||
"allowedTypesExample": [ - here are examples of allowed types
|
||||
"see react-markdown docs", `https://github.com/rexxars/react-markdown`
|
||||
"root",
|
||||
"text",
|
||||
"break",
|
||||
"paragraph",
|
||||
"emphasis",
|
||||
"strong",
|
||||
"thematicBreak",
|
||||
"blockquote",
|
||||
"delete",
|
||||
"link",
|
||||
"image", - you may not have a lot of control over how these are rendered
|
||||
"linkReference",
|
||||
"imageReference",
|
||||
"table",
|
||||
"tableHead",
|
||||
"tableBody",
|
||||
"tableRow",
|
||||
"tableCell",
|
||||
"list",
|
||||
"listItem",
|
||||
"heading",
|
||||
"inlineCode",
|
||||
"code",
|
||||
"html", - potentially DANGEROUS, intended for `serveOnlyApproved = true` environments, includes iframes, divs.
|
||||
"parsedHtml"
|
||||
],
|
||||
},
|
||||
"customFileExtensions": { - suggest a file extension for experimental content types you may be publishing
|
||||
"application/example-type": "example"
|
||||
}
|
||||
|
||||
STARTUP:
|
||||
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
const logger = require('winston');
|
||||
const transformImage = require('./transformImage');
|
||||
const isValidQueryObject = require('../../../utils/isValidQueryObj');
|
||||
const {
|
||||
serving: { dynamicFileSizing },
|
||||
} = require('@config/siteConfig');
|
||||
const { enabled: dynamicEnabled } = dynamicFileSizing;
|
||||
const serveFile = async ({ filePath, fileType }, res, originalUrl) => {
|
||||
const queryObject = {};
|
||||
// TODO: replace quick/dirty try catch with better practice
|
||||
|
@ -21,7 +26,10 @@ const serveFile = async ({ filePath, fileType }, res, originalUrl) => {
|
|||
|
||||
let mediaType = fileType ? fileType.substr(0, fileType.indexOf('/')) : '';
|
||||
const transform =
|
||||
mediaType === 'image' && queryObject.hasOwnProperty('h') && queryObject.hasOwnProperty('w');
|
||||
mediaType === 'image' &&
|
||||
queryObject.hasOwnProperty('h') &&
|
||||
queryObject.hasOwnProperty('w') &&
|
||||
dynamicEnabled;
|
||||
|
||||
const sendFileOptions = {
|
||||
headers: {
|
||||
|
@ -32,14 +40,26 @@ const serveFile = async ({ filePath, fileType }, res, originalUrl) => {
|
|||
},
|
||||
};
|
||||
logger.debug(`fileOptions for ${filePath}:`, sendFileOptions);
|
||||
if (transform) {
|
||||
logger.debug(`transforming and sending file`);
|
||||
try {
|
||||
if (transform) {
|
||||
if (!isValidQueryObject(queryObject)) {
|
||||
logger.debug(`Unacceptable querystring`, { queryObject });
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: 'Querystring may not have dimensions greater than 2000',
|
||||
});
|
||||
res.end();
|
||||
}
|
||||
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);
|
||||
let xformed = await transformImage(filePath, queryObject);
|
||||
res.status(200).set(sendFileOptions.headers);
|
||||
res.end(xformed, 'binary');
|
||||
} else {
|
||||
res.status(200).sendFile(filePath, sendFileOptions);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.debug(e);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
24
server/utils/isValidQueryObj.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
Can be an arrow function as well Can be an arrow function as well
|
||||
const {
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
serving: { dynamicFileSizing },
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
} = require('@config/siteConfig');
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
const { maxDimension } = dynamicFileSizing;
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
const isValidQueryObj = queryObj => {
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
let {
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
h: cHeight = null,
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
w: cWidth = null,
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
t: transform = null,
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
x: xOrigin = null,
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
y: yOrigin = null,
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
} = queryObj;
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
return (
|
||||
Can be an arrow function as well Can be an arrow function as well
line break above line break above
|
||||
((cHeight <= maxDimension && cHeight > 0) || cHeight === null) &&
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
((cWidth <= maxDimension && cWidth > 0) || cWidth === null) &&
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
(transform === null || transform === 'crop' || transform === 'stretch') &&
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
((xOrigin <= maxDimension && xOrigin >= 0) || xOrigin === null) &&
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
((yOrigin <= maxDimension && yOrigin >= 0) || yOrigin === null)
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
);
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
};
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
|
||||
Can be an arrow function as well Can be an arrow function as well
|
||||
module.exports = isValidQueryObj;
|
||||
Can be an arrow function as well Can be an arrow function as well
|
Can be an arrow function as well