From c2a9d7e4c00d16ff2b0569a4249c373783ecf605 Mon Sep 17 00:00:00 2001 From: jessop Date: Tue, 5 Feb 2019 01:32:35 -0500 Subject: [PATCH 1/3] makes final tweaks to markdown --- client/src/components/FileViewer/index.jsx | 1 + client/src/containers/AssetInfo/view.jsx | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/client/src/components/FileViewer/index.jsx b/client/src/components/FileViewer/index.jsx index 8cd0172e..0d3d4866 100644 --- a/client/src/components/FileViewer/index.jsx +++ b/client/src/components/FileViewer/index.jsx @@ -1,5 +1,6 @@ import React from 'react'; import ReactMarkdown from 'react-markdown'; +// TODO: get markdown settings from siteConfig class FileViewer extends React.Component { diff --git a/client/src/containers/AssetInfo/view.jsx b/client/src/containers/AssetInfo/view.jsx index 70d63298..c1e6154d 100644 --- a/client/src/containers/AssetInfo/view.jsx +++ b/client/src/containers/AssetInfo/view.jsx @@ -22,6 +22,7 @@ class AssetInfo extends React.Component { const canonicalUrl = createCanonicalLink({ asset: { ...claimData, shortId: asset.shortId }}); const assetCanonicalUrl = `${host}${canonicalUrl}`; // Todo Issue #882 centralize all this media type detection + // Todo get markdown settings from siteConfig const embedable = contentType.split('/')[0] === 'image' || contentType === 'video/mp4'; let channelCanonicalUrl; @@ -37,7 +38,7 @@ class AssetInfo extends React.Component { { description && ( } - content={
} + content={
} /> )} {editable && ( -- 2.45.3 From 815d0437ac1abf76ec4b920d9394b11eef463d5f Mon Sep 17 00:00:00 2001 From: jessop Date: Tue, 5 Feb 2019 21:34:45 -0500 Subject: [PATCH 2/3] refactors publishValidate --- cli/defaults/siteConfig.json | 13 +++++- utils/validateFileForPublish.js | 75 +++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 utils/validateFileForPublish.js diff --git a/cli/defaults/siteConfig.json b/cli/defaults/siteConfig.json index 15243fce..dd9f7066 100644 --- a/cli/defaults/siteConfig.json +++ b/cli/defaults/siteConfig.json @@ -23,7 +23,7 @@ "publishing": { "primaryClaimAddress": null, "uploadDirectory": "/home/lbry/Uploads", - "lbrynetHome": "/home/lbry", + "lbrynetHome": "/CURRENTLYUNUSED", "thumbnailChannel": null, "thumbnailChannelId": null, "additionalClaimAddresses": [], @@ -36,6 +36,17 @@ "publishingChannelWhitelist": [], "channelClaimBidAmount": "0.1", "fileClaimBidAmount": "0.01", + "fileSizeLimits": { + "image": 50000000, + "video": 50000000, + "audio": 50000000, + "text": 50000000, + "model": 50000000, + "application": 50000000, + "customByContentType": { + "application/octet-stream": 50000000 + } + }, "maxSizeImage": 10000000, "maxSizeGif": 50000000, "maxSizeVideo": 50000000 diff --git a/utils/validateFileForPublish.js b/utils/validateFileForPublish.js new file mode 100644 index 00000000..b2e8f87d --- /dev/null +++ b/utils/validateFileForPublish.js @@ -0,0 +1,75 @@ +import { publishing } from '@config/siteConfig.json'; + +const { + fileSizeLimits: { + image: maxSizeImage = 10000000, + video: maxSizeVideo = 50000000, + audio: maxSizeAudio = 50000000, + text: maxSizeText = 50000000, + model: maxSizeModel = 50000000, + application: maxSizeApplication = 50000000, + customByContentType, + }, +} = publishing; + +const SIZE_MB = 1000000; + +export const validateFileForPublish = file => { + let contentType = file.type; + let mediaType = contentType ? contentType.substr(0, contentType.indexOf('/')) : ''; + + if (!file) { + throw new Error('no file provided'); + } + + if (/'/.test(file.name)) { + throw new Error('apostrophes are not allowed in the file name'); + } + + if (Object.keys(customByContentType).includes(contentType)) { + if (file.size > customByContentType[contentType]) { + throw new Error( + `Sorry, type ${contentType} is limited to ${customByContentType[contentType] / SIZE_MB} MB.` + ); + } + } else { + switch (mediaType) { + case 'image': + if (file.size > maxSizeImage) { + throw new Error(`Sorry, type ${mediaType} is limited to ${maxSizeImage / SIZE_MB} MB.`); + } + break; + case 'audio': + if (file.size > maxSizeAudio) { + throw new Error(`Sorry, type ${mediaType} is limited to ${maxSizeAudio / SIZE_MB} MB.`); + } + break; + case 'video': + if (file.size > maxSizeVideo) { + throw new Error(`Sorry, type ${mediaType} is limited to ${maxSizeVideo / SIZE_MB} MB.`); + } + break; + case 'text': + if (file.size > maxSizeText) { + throw new Error(`Sorry, type ${mediaType} is limited to ${maxSizeText / SIZE_MB} MB.`); + } + break; + case 'model': + if (file.size > maxSizeModel) { + throw new Error(`Sorry, type ${mediaType} is limited to ${maxSizeModel / SIZE_MB} MB.`); + } + break; + case 'application': + if (file.size > maxSizeApplication) { + throw new Error( + `Sorry, type ${mediaType} is limited to ${maxSizeApplication / SIZE_MB} MB.` + ); + } + break; + default: + throw new Error(`Missing or unrecognized file type`); + } + return false; + } + return file; +}; -- 2.45.3 From e364dc4024179e1374f3b8f6d24bda9b1b6ab589 Mon Sep 17 00:00:00 2001 From: jessop Date: Wed, 6 Feb 2019 12:56:28 -0500 Subject: [PATCH 3/3] publishes any file type, handles 99% for download --- cli/defaults/siteConfig.json | 14 +++- .../components/DropzonePreviewImage/index.jsx | 14 ++-- client/src/containers/Dropzone/view.jsx | 10 +-- client/src/utils/createAssetMetaTags.js | 2 - server/chainquery/models/ClaimModel.js | 10 ++- .../publish/parsePublishApiRequestFiles.js | 3 +- .../claim/publish/validateFileForPublish.js | 38 +++++++++++ utils/validateFileForPublish.js | 65 ++++--------------- 8 files changed, 87 insertions(+), 69 deletions(-) create mode 100644 server/controllers/api/claim/publish/validateFileForPublish.js diff --git a/cli/defaults/siteConfig.json b/cli/defaults/siteConfig.json index dd9f7066..67e4a79f 100644 --- a/cli/defaults/siteConfig.json +++ b/cli/defaults/siteConfig.json @@ -60,7 +60,19 @@ "publicDisallowedTypesMain": [] }, "customFileExtensions": { - "application/example-type": "example" + "application/x-troff-man": ".man", + "application/x-troff-me": ".me", + "application/x-mif": ".mif", + "application/x-troff-ms": ".ms", + "application/x-troff": ".roff", + "application/x-python-code": ".pyc", + "text/x-python": ".py", + "application/x-pn-realaudio": ".ram", + "application/x-sgml": ".sgm", + "model/stl": ".stl", + "image/pict": ".pct", + "text/xul": ".xul", + "text/x-go": "go" } }, "startup": { diff --git a/client/src/components/DropzonePreviewImage/index.jsx b/client/src/components/DropzonePreviewImage/index.jsx index d62abaeb..5aa6f730 100644 --- a/client/src/components/DropzonePreviewImage/index.jsx +++ b/client/src/components/DropzonePreviewImage/index.jsx @@ -5,8 +5,9 @@ class PublishPreview extends React.Component { constructor (props) { super(props); this.state = { - imgSource : '', - defaultThumbnail: '/assets/img/video_thumb_default.png', + imgSource : '', + defaultVideoThumbnail: '/assets/img/video_thumb_default.png', + defaultThumbnail : '/assets/img/Speech_Logo_Main@OG-02.jpg', }; } componentDidMount () { @@ -37,12 +38,13 @@ class PublishPreview extends React.Component { }; } setPreviewImageSource (file) { - if (file.type !== 'video/mp4') { + if (this.props.thumbnail) { + this.setPreviewImageSourceFromFile(this.props.thumbnail); + } else if (file.type.substr(0, file.type.indexOf('/')) === 'image'){ this.setPreviewImageSourceFromFile(file); + } else if (file.type === 'video'){ + this.setState({imgSource: this.state.defaultVideoThumbnail}); } else { - if (this.props.thumbnail) { - this.setPreviewImageSourceFromFile(this.props.thumbnail); - } this.setState({imgSource: this.state.defaultThumbnail}); } } diff --git a/client/src/containers/Dropzone/view.jsx b/client/src/containers/Dropzone/view.jsx index a4a6d00e..cba685f9 100644 --- a/client/src/containers/Dropzone/view.jsx +++ b/client/src/containers/Dropzone/view.jsx @@ -1,13 +1,13 @@ import React from 'react'; -import { validateFile } from '../../utils/file'; import Memeify from '@components/Memeify'; import DropzonePreviewImage from '@components/DropzonePreviewImage'; import DropzoneDropItDisplay from '@components/DropzoneDropItDisplay'; import DropzoneInstructionsDisplay from '@components/DropzoneInstructionsDisplay'; +import validateFileForPublish from '@globalutils/validateFileForPublish'; -import { library } from '@fortawesome/fontawesome-svg-core' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { library } from '@fortawesome/fontawesome-svg-core'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faEdit } from '@fortawesome/free-solid-svg-icons'; const isFacebook = (() => { @@ -29,7 +29,7 @@ class Dropzone extends React.Component { memeify : false, }; - if(props.file) { + if (props.file) { // No side effects allowed with `getDerivedStateFromProps`, so // we must use `componentDidUpdate` and `constructor` routines. // Note: `FileReader` has an `onloadend` side-effect @@ -133,7 +133,7 @@ class Dropzone extends React.Component { chooseFile (file) { if (file) { try { - validateFile(file); // validate the file's name, type, and size + validateFileForPublish(file); // validate the file's name, type, and size } catch (error) { return this.props.setFileError(error.message); } diff --git a/client/src/utils/createAssetMetaTags.js b/client/src/utils/createAssetMetaTags.js index 70bead44..e2a0b5ef 100644 --- a/client/src/utils/createAssetMetaTags.js +++ b/client/src/utils/createAssetMetaTags.js @@ -47,8 +47,6 @@ const createAssetMetaTags = asset => { const ogThumbnailContentType = determineContentTypeFromExtension(claimData.thumbnail); const ogThumbnail = claimData.thumbnail || defaultThumbnail; - console.log('asset.claimData', asset.claimData); - // {property: 'og:title'] = ogTitle}, const metaTags = { 'og:title': ogTitle, diff --git a/server/chainquery/models/ClaimModel.js b/server/chainquery/models/ClaimModel.js index 022656f9..4fcc1af0 100644 --- a/server/chainquery/models/ClaimModel.js +++ b/server/chainquery/models/ClaimModel.js @@ -1,9 +1,17 @@ const logger = require('winston'); const mime = require('mime-types'); +const { + serving: { customFileExtensions }, +} = require('@config/siteConfig'); const getterMethods = { generated_extension() { - return mime.extension(this.content_type) ? mime.extension(this.content_type) : 'jpg'; + logger.info('trying to generate extension', this.content_type); + if (customFileExtensions.hasOwnProperty(this.content_type)) { + return customFileExtensions[this.content_type]; + } else { + return mime.extension(this.content_type) ? mime.extension(this.content_type) : 'jpg'; + } }, }; diff --git a/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js b/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js index 3fdcdfc3..6979740e 100644 --- a/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js +++ b/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js @@ -1,5 +1,6 @@ const path = require('path'); const validateFileTypeAndSize = require('./validateFileTypeAndSize.js'); +const validateFileForPublish = require('./validateFileForPublish.js'); const parsePublishApiRequestFiles = ({ file, thumbnail }, isUpdate) => { // make sure a file was provided @@ -40,7 +41,7 @@ const parsePublishApiRequestFiles = ({ file, thumbnail }, isUpdate) => { } // validate the file - if (file) validateFileTypeAndSize(file); + if (file) validateFileForPublish(file); // return results const obj = { fileName: file.name, diff --git a/server/controllers/api/claim/publish/validateFileForPublish.js b/server/controllers/api/claim/publish/validateFileForPublish.js new file mode 100644 index 00000000..1047c048 --- /dev/null +++ b/server/controllers/api/claim/publish/validateFileForPublish.js @@ -0,0 +1,38 @@ +const logger = require('winston'); + +const { publishing } = require('@config/siteConfig.json'); + +const { fileSizeLimits } = publishing; + +const SIZE_MB = 1000000; + +const validateFileForPublish = file => { + let contentType = file.type; + let mediaType = contentType ? contentType.substr(0, contentType.indexOf('/')) : ''; + let mediaTypeLimit = fileSizeLimits[mediaType] || false; + let customLimits = fileSizeLimits['customByContentType']; + + if (!file) { + throw new Error('no file provided'); + } + + if (/'/.test(file.name)) { + throw new Error('apostrophes are not allowed in the file name'); + } + + if (Object.keys(customLimits).includes(contentType)) { + if (file.size > customLimits[contentType]) { + throw new Error( + `Sorry, type ${contentType} is limited to ${customLimits[contentType] / SIZE_MB} MB.` + ); + } + } + if (mediaTypeLimit) { + if (file.size > mediaTypeLimit) { + throw new Error(`Sorry, type ${mediaType} is limited to ${mediaTypeLimit / SIZE_MB} MB.`); + } + } + return file; +}; + +module.exports = validateFileForPublish; diff --git a/utils/validateFileForPublish.js b/utils/validateFileForPublish.js index b2e8f87d..ef129305 100644 --- a/utils/validateFileForPublish.js +++ b/utils/validateFileForPublish.js @@ -1,22 +1,14 @@ import { publishing } from '@config/siteConfig.json'; -const { - fileSizeLimits: { - image: maxSizeImage = 10000000, - video: maxSizeVideo = 50000000, - audio: maxSizeAudio = 50000000, - text: maxSizeText = 50000000, - model: maxSizeModel = 50000000, - application: maxSizeApplication = 50000000, - customByContentType, - }, -} = publishing; +const { fileSizeLimits } = publishing; const SIZE_MB = 1000000; -export const validateFileForPublish = file => { +export default function validateFileForPublish(file) { let contentType = file.type; let mediaType = contentType ? contentType.substr(0, contentType.indexOf('/')) : ''; + let mediaTypeLimit = fileSizeLimits[mediaType] || false; + let customLimits = fileSizeLimits['customByContentType']; if (!file) { throw new Error('no file provided'); @@ -26,50 +18,17 @@ export const validateFileForPublish = file => { throw new Error('apostrophes are not allowed in the file name'); } - if (Object.keys(customByContentType).includes(contentType)) { - if (file.size > customByContentType[contentType]) { + if (Object.keys(customLimits).includes(contentType)) { + if (file.size > customLimits[contentType]) { throw new Error( - `Sorry, type ${contentType} is limited to ${customByContentType[contentType] / SIZE_MB} MB.` + `Sorry, type ${contentType} is limited to ${customLimits[contentType] / SIZE_MB} MB.` ); } - } else { - switch (mediaType) { - case 'image': - if (file.size > maxSizeImage) { - throw new Error(`Sorry, type ${mediaType} is limited to ${maxSizeImage / SIZE_MB} MB.`); - } - break; - case 'audio': - if (file.size > maxSizeAudio) { - throw new Error(`Sorry, type ${mediaType} is limited to ${maxSizeAudio / SIZE_MB} MB.`); - } - break; - case 'video': - if (file.size > maxSizeVideo) { - throw new Error(`Sorry, type ${mediaType} is limited to ${maxSizeVideo / SIZE_MB} MB.`); - } - break; - case 'text': - if (file.size > maxSizeText) { - throw new Error(`Sorry, type ${mediaType} is limited to ${maxSizeText / SIZE_MB} MB.`); - } - break; - case 'model': - if (file.size > maxSizeModel) { - throw new Error(`Sorry, type ${mediaType} is limited to ${maxSizeModel / SIZE_MB} MB.`); - } - break; - case 'application': - if (file.size > maxSizeApplication) { - throw new Error( - `Sorry, type ${mediaType} is limited to ${maxSizeApplication / SIZE_MB} MB.` - ); - } - break; - default: - throw new Error(`Missing or unrecognized file type`); + } + if (mediaTypeLimit) { + if (file.size > mediaTypeLimit) { + throw new Error(`Sorry, type ${mediaType} is limited to ${mediaTypeLimit / SIZE_MB} MB.`); } - return false; } return file; -}; +} -- 2.45.3