From 0e88bb4c60524b555a2aca42ee1421409009347d Mon Sep 17 00:00:00 2001 From: bill bittner Date: Wed, 31 Jan 2018 19:12:54 -0800 Subject: [PATCH] updated routes to handle serves only and return to client for show requests --- helpers/lbryUri.js | 29 +++- react/components/AssetDisplay/index.js | 4 +- react/components/AssetInfo/index.js | 14 +- react/components/ErrorPage/index.js | 20 +++ react/components/ShowAsset/index.js | 8 +- react/components/ShowAssetDetails/index.js | 19 ++- react/components/ShowPage/index.js | 17 ++- react/utils/lbryUri.js | 20 +-- routes/serve-routes.js | 166 +++++++++------------ 9 files changed, 165 insertions(+), 132 deletions(-) create mode 100644 react/components/ErrorPage/index.js diff --git a/helpers/lbryUri.js b/helpers/lbryUri.js index 15ab0b86..f72a25ef 100644 --- a/helpers/lbryUri.js +++ b/helpers/lbryUri.js @@ -55,14 +55,14 @@ module.exports = { claimId, }; }, - parseName: function (name) { - logger.debug('parsing name:', name); + parseClaim: function (claim) { + logger.debug('parsing name:', claim); const componentsRegex = new RegExp( '([^:$#/.]*)' + // name (stops at the first modifier) '([:$#.]?)([^/]*)' // modifier separator, modifier (stops at the first path separator or end) ); const [proto, claimName, modifierSeperator, modifier] = componentsRegex - .exec(name) + .exec(claim) .map(match => match || null); logger.debug(`${proto}, ${claimName}, ${modifierSeperator}, ${modifier}`); @@ -75,7 +75,6 @@ module.exports = { throw new Error(`Invalid characters in claim name: ${nameBadChars.join(', ')}.`); } // Validate and process modifier - let isServeRequest = false; if (modifierSeperator) { if (!modifier) { throw new Error(`No file extension provided after separator ${modifierSeperator}.`); @@ -83,11 +82,29 @@ module.exports = { if (modifierSeperator !== '.') { throw new Error(`The ${modifierSeperator} modifier is not supported in the claim name`); } - isServeRequest = true; } + // return results return { claimName, - isServeRequest, + }; + }, + parseModifier: function (claim) { + logger.debug('parsing modifier:', claim); + const componentsRegex = new RegExp( + '([^:$#/.]*)' + // name (stops at the first modifier) + '([:$#.]?)([^/]*)' // modifier separator, modifier (stops at the first path separator or end) + ); + const [proto, claimName, modifierSeperator, modifier] = componentsRegex + .exec(claim) + .map(match => match || null); + logger.debug(`${proto}, ${claimName}, ${modifierSeperator}, ${modifier}`); + // Validate and process modifier + let hasFileExtension = false; + if (modifierSeperator) { + hasFileExtension = true; + } + return { + hasFileExtension, }; }, }; diff --git a/react/components/AssetDisplay/index.js b/react/components/AssetDisplay/index.js index 18d72c12..ae630a0b 100644 --- a/react/components/AssetDisplay/index.js +++ b/react/components/AssetDisplay/index.js @@ -1,9 +1,9 @@ import React from 'react'; -const AssetDisplay = ({ claimName, claimId }) => { +const AssetDisplay = ({ name, claimId }) => { return (
-

display {claimName}#{claimId} here

+

display {name}#{claimId} here

); }; diff --git a/react/components/AssetInfo/index.js b/react/components/AssetInfo/index.js index 4c0306aa..006ea701 100644 --- a/react/components/AssetInfo/index.js +++ b/react/components/AssetInfo/index.js @@ -54,7 +54,7 @@ class AssetInfo extends React.Component {
@@ -168,6 +168,6 @@ class AssetInfo extends React.Component { }; // required props -// {channelName, certificateId, description, shortId, name, fileExt, claimId, contentType, thumbnail, host} +// {channelName, certificateId, description, shortClaimId, name, fileExt, claimId, contentType, thumbnail, host} export default AssetInfo; diff --git a/react/components/ErrorPage/index.js b/react/components/ErrorPage/index.js new file mode 100644 index 00000000..f40d348a --- /dev/null +++ b/react/components/ErrorPage/index.js @@ -0,0 +1,20 @@ +import React from 'react'; +import NavBar from 'containers/NavBar'; + +class ErrorPage extends React.Component { + render () { + return ( +
+ +
+

{this.props.error}

+
+
+ ); + } +}; + +// required props +// error + +export default ErrorPage; diff --git a/react/components/ShowAsset/index.js b/react/components/ShowAsset/index.js index d2dd5db7..616ae44e 100644 --- a/react/components/ShowAsset/index.js +++ b/react/components/ShowAsset/index.js @@ -1,6 +1,6 @@ import React from 'react'; -import ShowLite from 'components/ShowAssetLite'; -import ShowDetails from 'components/ShowAssetDetails'; +import ShowAssetLite from 'components/ShowAssetLite'; +import ShowAssetDetails from 'components/ShowAssetDetails'; import request from 'utils/request'; class ShowAsset extends React.Component { @@ -83,14 +83,14 @@ class ShowAsset extends React.Component { render () { if (this.props.isServeRequest) { return ( - ); } return ( - diff --git a/react/components/ShowAssetDetails/index.js b/react/components/ShowAssetDetails/index.js index a6e810bb..640aa409 100644 --- a/react/components/ShowAssetDetails/index.js +++ b/react/components/ShowAssetDetails/index.js @@ -13,7 +13,9 @@ class ShowDetails extends React.Component {
{this.props.error && -

{this.props.error}

+
+

{this.props.error}

+
} {this.props.claimData &&
@@ -23,13 +25,24 @@ class ShowDetails extends React.Component {
- +
diff --git a/react/components/ShowPage/index.js b/react/components/ShowPage/index.js index 6945e729..67e6a68e 100644 --- a/react/components/ShowPage/index.js +++ b/react/components/ShowPage/index.js @@ -1,4 +1,5 @@ import React from 'react'; +import ErrorPage from 'components/ErrorPage'; import ShowAsset from 'components/ShowAsset'; import ShowChannel from 'components/ShowChannel'; import lbryUri from 'utils/lbryUri'; @@ -10,6 +11,7 @@ class ShowPage extends React.Component { constructor (props) { super(props); this.state = { + error : null, identifier : null, claim : null, isServeRequest: null, @@ -46,9 +48,9 @@ class ShowPage extends React.Component { let isChannel, channelName, channelClaimId, claimId, claimName, isServeRequest; try { ({ isChannel, channelName, channelClaimId, claimId } = lbryUri.parseIdentifier(identifier)); - ({ claimName, isServeRequest } = lbryUri.parseName(claim)); + ({ claimName, isServeRequest } = lbryUri.parseClaim(claim)); } catch (error) { - return console.log('error:', error); + return this.setState({error: error.message}); } // set state return this.setState({ @@ -72,7 +74,7 @@ class ShowPage extends React.Component { try { ({ isChannel, channelName, channelClaimId } = lbryUri.parseIdentifier(claim)); } catch (error) { - return console.log('error:', error); + return this.setState({error: error.message}); } if (isChannel) { return this.setState({ @@ -85,9 +87,9 @@ class ShowPage extends React.Component { } let claimName, isServeRequest; try { - ({claimName, isServeRequest} = lbryUri.parseName(claim)); + ({claimName, isServeRequest} = lbryUri.parseClaim(claim)); } catch (error) { - return console.log('error:', error); + return this.setState({error: error.message}); } this.setState({ claim: { @@ -98,6 +100,11 @@ class ShowPage extends React.Component { } render () { console.log('rendering ShowPage'); + if (this.state.error) { + return ( + + ); + } if (this.state.claim) { if (this.state.claim.isChannel) { return ( diff --git a/react/utils/lbryUri.js b/react/utils/lbryUri.js index 653b0fe7..957a1a5f 100644 --- a/react/utils/lbryUri.js +++ b/react/utils/lbryUri.js @@ -16,18 +16,18 @@ module.exports = { // Validate and process name if (!value) { - throw new Error(`Check your url. No channel name provided before "${modifierSeperator}"`); + throw new Error(`Check your URL. No channel name provided before "${modifierSeperator}"`); } const isChannel = value.startsWith(module.exports.CHANNEL_CHAR); const channelName = isChannel ? value : null; let claimId; if (isChannel) { if (!channelName) { - throw new Error('No channel name after @.'); + throw new Error('Check your URL. No channel name after "@".'); } const nameBadChars = (channelName).match(module.exports.REGEXP_INVALID_CHANNEL); if (nameBadChars) { - throw new Error(`Invalid characters in channel name: ${nameBadChars.join(', ')}.`); + throw new Error(`Check your URL. Invalid characters in channel name: "${nameBadChars.join(', ')}".`); } } else { claimId = value; @@ -37,13 +37,13 @@ module.exports = { let channelClaimId; if (modifierSeperator) { if (!modifier) { - throw new Error(`No modifier provided after separator "${modifierSeperator}"`); + throw new Error(`Check your URL. No modifier provided after separator "${modifierSeperator}"`); } if (modifierSeperator === ':') { channelClaimId = modifier; } else { - throw new Error(`The "${modifierSeperator}" modifier is not currently supported`); + throw new Error(`Check your URL. The "${modifierSeperator}" modifier is not currently supported`); } } return { @@ -53,7 +53,7 @@ module.exports = { claimId, }; }, - parseName: function (name) { + parseClaim: function (name) { console.log('parsing name:', name); const componentsRegex = new RegExp( '([^:$#/.]*)' + // name (stops at the first modifier) @@ -66,20 +66,20 @@ module.exports = { // Validate and process name if (!claimName) { - throw new Error('No claim name provided before .'); + throw new Error('Check your URL. No claim name provided before "."'); } const nameBadChars = (claimName).match(module.exports.REGEXP_INVALID_CLAIM); if (nameBadChars) { - throw new Error(`Invalid characters in claim name: ${nameBadChars.join(', ')}.`); + throw new Error(`Check your URL. Invalid characters in claim name: "${nameBadChars.join(', ')}".`); } // Validate and process modifier let isServeRequest = false; if (modifierSeperator) { if (!modifier) { - throw new Error(`No file extension provided after separator ${modifierSeperator}.`); + throw new Error(`Check your URL. No file extension provided after separator "${modifierSeperator}".`); } if (modifierSeperator !== '.') { - throw new Error(`The ${modifierSeperator} modifier is not supported in the claim name`); + throw new Error(`Check your URL. The "${modifierSeperator}" modifier is not supported in the claim name.`); } isServeRequest = true; } diff --git a/routes/serve-routes.js b/routes/serve-routes.js index ab42137e..b95c564a 100644 --- a/routes/serve-routes.js +++ b/routes/serve-routes.js @@ -2,13 +2,10 @@ const logger = require('winston'); const { getClaimId, getLocalFileRecord } = require('../controllers/serveController.js'); const serveHelpers = require('../helpers/serveHelpers.js'); const { handleRequestError } = require('../helpers/errorHandlers.js'); -const { postToStats } = require('../helpers/statsHelpers.js'); -const db = require('../models'); const lbryUri = require('../helpers/lbryUri.js'); const SERVE = 'SERVE'; const SHOW = 'SHOW'; -const SHOWLITE = 'SHOWLITE'; const NO_CHANNEL = 'NO_CHANNEL'; const NO_CLAIM = 'NO_CLAIM'; const NO_FILE = 'NO_FILE'; @@ -39,49 +36,23 @@ function clientWantsAsset ({accept, range}) { return imageIsWanted || videoIsWanted; } -function determineResponseType (isServeRequest, headers) { +function determineResponseType (hasFileExtension, headers) { let responseType; - if (isServeRequest) { - responseType = SERVE; - if (clientAcceptsHtml(headers)) { // this is in case a serve request comes from a browser - responseType = SHOWLITE; + if (hasFileExtension) { + responseType = SERVE; // assume a serve request if file extension is present + if (clientAcceptsHtml(headers)) { // if the request comes from a browser, change it to a show request + responseType = SHOW; } } else { responseType = SHOW; if (clientWantsAsset(headers) && requestIsFromBrowser(headers)) { // this is in case someone embeds a show url - logger.debug('Show request came from browser and wants an image/video; changing response to serve.'); + logger.debug('Show request came from browser but wants an image/video. Changing response to serve...'); responseType = SERVE; } } return responseType; } -// function showAssetToClient (claimId, name, res) { -// return Promise -// .all([db.Claim.resolveClaim(name, claimId), db.Claim.getShortClaimIdFromLongClaimId(claimId, name)]) -// .then(([claimInfo, shortClaimId]) => { -// // logger.debug('claimInfo:', claimInfo); -// // logger.debug('shortClaimId:', shortClaimId); -// return serveHelpers.showFile(claimInfo, shortClaimId, res); -// }) -// .catch(error => { -// throw error; -// }); -// } -// -// function showLiteAssetToClient (claimId, name, res) { -// return Promise -// .all([db.Claim.resolveClaim(name, claimId), db.Claim.getShortClaimIdFromLongClaimId(claimId, name)]) -// .then(([claimInfo, shortClaimId]) => { -// // logger.debug('claimInfo:', claimInfo); -// // logger.debug('shortClaimId:', shortClaimId); -// return serveHelpers.showFileLite(claimInfo, shortClaimId, res); -// }) -// .catch(error => { -// throw error; -// }); -// } - function serveAssetToClient (claimId, name, res) { return getLocalFileRecord(claimId, name) .then(fileInfo => { @@ -115,82 +86,87 @@ function logRequestData (responseType, claimName, channelName, claimId) { module.exports = (app) => { // route to serve a specific asset using the channel or claim id - app.get('/:identifier/:name', ({ headers, ip, originalUrl, params }, res) => { - let isChannel, channelName, channelClaimId, claimId, claimName, isServeRequest; + app.get('/:identifier/:claim', ({ headers, ip, originalUrl, params }, res) => { + // decide if this is a show request + let hasFileExtension; + try { + ({ hasFileExtension } = lbryUri.parseModifier(params.claim)); + } catch (error) { + return res.status(200).json({success: false, message: error.message}); + } + let responseType = determineResponseType(hasFileExtension, headers); + if (responseType !== SERVE) { + return res.status(200).render('index'); + } + // parse the claim + let claimName; + try { + ({ claimName } = lbryUri.parseClaim(params.claim)); + } catch (error) { + return res.status(200).json({success: false, message: error.message}); + } + // parse the identifier + let isChannel, channelName, channelClaimId, claimId; try { ({ isChannel, channelName, channelClaimId, claimId } = lbryUri.parseIdentifier(params.identifier)); - ({ claimName, isServeRequest } = lbryUri.parseName(params.name)); } catch (error) { return handleRequestError(originalUrl, ip, error, res); } if (!isChannel) { [claimId, claimName] = flipClaimNameAndIdForBackwardsCompatibility(claimId, claimName); } - let responseType = determineResponseType(isServeRequest, headers); // log the request data for debugging logRequestData(responseType, claimName, channelName, claimId); - // if a serve request, serve, otherwise send the react app - if (responseType === SERVE) { - // get the claim Id and then serve/show the asset - getClaimId(channelName, channelClaimId, claimName, claimId) - .then(fullClaimId => { - if (fullClaimId === NO_CLAIM) { - return res.status(200).json({success: false, message: 'no claim id could be found'}); - } else if (fullClaimId === NO_CHANNEL) { - return res.status(200).json({success: false, message: 'no channel id could be found'}); - } - serveAssetToClient(fullClaimId, claimName, res); - // postToStats(responseType, originalUrl, ip, claimName, fullClaimId, 'success'); - }) - .catch(error => { - handleRequestError(originalUrl, ip, error, res); - // postToStats(responseType, originalUrl, ip, claimName, fullClaimId, 'fail'); - }); - } else { - res.status(200).render('index'); - } + // get the claim Id and then serve the asset + getClaimId(channelName, channelClaimId, claimName, claimId) + .then(fullClaimId => { + if (fullClaimId === NO_CLAIM) { + return res.status(200).json({success: false, message: 'no claim id could be found'}); + } else if (fullClaimId === NO_CHANNEL) { + return res.status(200).json({success: false, message: 'no channel id could be found'}); + } + serveAssetToClient(fullClaimId, claimName, res); + // postToStats(responseType, originalUrl, ip, claimName, fullClaimId, 'success'); + }) + .catch(error => { + handleRequestError(originalUrl, ip, error, res); + // postToStats(responseType, originalUrl, ip, claimName, fullClaimId, 'fail'); + }); }); // route to serve the winning asset at a claim or a channel page - app.get('/:identifier', ({ headers, ip, originalUrl, params, query }, res) => { - let isChannel, channelName, channelClaimId; + app.get('/:claim', ({ headers, ip, originalUrl, params, query }, res) => { + // decide if this is a show request + let hasFileExtension; try { - ({ isChannel, channelName, channelClaimId } = lbryUri.parseIdentifier(params.identifier)); - // log the result - logger.debug(`isChannel: ${isChannel}, channelName: ${channelName}, channelClaimId: ${channelClaimId}`); + ({ hasFileExtension } = lbryUri.parseModifier(params.claim)); } catch (error) { - return handleRequestError(originalUrl, ip, error, res); + return res.status(200).json({success: false, message: error.message}); } - if (isChannel) { - // handle showing the channel page + let responseType = determineResponseType(hasFileExtension, headers); + if (responseType !== SERVE) { return res.status(200).render('index'); - } else { - let claimName, isServeRequest; - try { - ({claimName, isServeRequest} = lbryUri.parseName(params.identifier)); - } catch (error) { - return handleRequestError(originalUrl, ip, error, res); - } - let responseType = determineResponseType(isServeRequest, headers); - // log the request data for debugging - logRequestData(responseType, claimName, null, null); - // if a serve request, serve, otherwise send the react app - if (responseType === SERVE) { - // get the claim Id and then serve/show the asset - getClaimId(null, null, claimName, null) - .then(fullClaimId => { - if (fullClaimId === NO_CLAIM) { - return res.status(200).render('index'); - } - serveAssetToClient(fullClaimId, claimName, res); - // postToStats(responseType, originalUrl, ip, claimName, fullClaimId, 'success'); - }) - .catch(error => { - handleRequestError(originalUrl, ip, error, res); - // postToStats(responseType, originalUrl, ip, claimName, fullClaimId, 'fail'); - }); - } else { - res.status(200).render('index'); - } } + // parse the claim + let claimName; + try { + ({claimName} = lbryUri.parseClaim(params.claim)); + } catch (error) { + return res.status(200).json({success: false, message: error.message}); + } + // log the request data for debugging + logRequestData(responseType, claimName, null, null); + // get the claim Id and then serve the asset + getClaimId(null, null, claimName, null) + .then(fullClaimId => { + if (fullClaimId === NO_CLAIM) { + return res.status(200).render('index'); + } + serveAssetToClient(fullClaimId, claimName, res); + // postToStats(responseType, originalUrl, ip, claimName, fullClaimId, 'success'); + }) + .catch(error => { + handleRequestError(originalUrl, ip, error, res); + // postToStats(responseType, originalUrl, ip, claimName, fullClaimId, 'fail'); + }); }); };