From 385bee964dc7decf2455f6638d36b7ee0c731eec Mon Sep 17 00:00:00 2001 From: Charlie Humphreys Date: Fri, 6 Jan 2023 21:29:21 -0600 Subject: [PATCH 1/5] adjust frontend error handling, add client/server mismatch middleware --- client/homebrew/pages/editPage/editPage.jsx | 97 +++---------------- client/homebrew/pages/editPage/editPage.less | 70 ------------- client/homebrew/pages/homePage/homePage.jsx | 18 +++- client/homebrew/pages/homePage/homePage.less | 11 +++ client/homebrew/pages/newPage/newPage.jsx | 76 ++------------- client/homebrew/pages/newPage/newPage.less | 70 ------------- .../styles/nav-item-error-container.less | 72 ++++++++++++++ .../homebrew/utils/render-error-nav-item.js | 71 ++++++++++++++ client/homebrew/utils/request-middleware.js | 12 +++ client/template.js | 3 + server/homebrew.api.js | 1 + server/middleware.js | 17 ++++ 12 files changed, 221 insertions(+), 297 deletions(-) create mode 100644 client/homebrew/styles/nav-item-error-container.less create mode 100644 client/homebrew/utils/render-error-nav-item.js create mode 100644 client/homebrew/utils/request-middleware.js create mode 100644 server/middleware.js diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 873f5d3d2..6f1a8d91c 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -1,9 +1,10 @@ /* eslint-disable max-lines */ require('./editPage.less'); +require('../../styles/nav-item-error-container.less'); const React = require('react'); const createClass = require('create-react-class'); const _ = require('lodash'); -const request = require('superagent'); +const request = require('../../utils/request-middleware.js'); const { Meta } = require('vitreum/headtags'); const Nav = require('naturalcrit/nav/nav.jsx'); @@ -45,7 +46,7 @@ const EditPage = createClass({ alertLoginToTransfer : false, saveGoogle : this.props.brew.googleId ? true : false, confirmGoogleTransfer : false, - errors : null, + error : null, htmlErrors : Markdown.validate(this.props.brew.text), url : '', autoSave : true, @@ -60,7 +61,6 @@ const EditPage = createClass({ url : window.location.href }); - this.savedBrew = JSON.parse(JSON.stringify(this.props.brew)); //Deep copy this.setState({ autoSave: JSON.parse(localStorage.getItem('AUTOSAVE_ON')) ?? true }, ()=>{ @@ -157,7 +157,10 @@ const EditPage = createClass({ this.setState((prevState)=>({ confirmGoogleTransfer : !prevState.confirmGoogleTransfer })); - this.clearErrors(); + this.setState({ + error : null, + isSaving : false + }); }, closeAlerts : function(event){ @@ -173,24 +176,16 @@ const EditPage = createClass({ this.setState((prevState)=>({ saveGoogle : !prevState.saveGoogle, isSaving : false, - errors : null + error : null }), ()=>this.save()); }, - clearErrors : function(){ - this.setState({ - errors : null, - isSaving : false - - }); - }, - save : async function(){ if(this.debounceSave && this.debounceSave.cancel) this.debounceSave.cancel(); this.setState((prevState)=>({ isSaving : true, - errors : null, + error : null, htmlErrors : Markdown.validate(prevState.brew.text) })); @@ -205,8 +200,9 @@ const EditPage = createClass({ .send(brew) .catch((err)=>{ console.log('Error Updating Local Brew'); - this.setState({ errors: err }); + this.setState({ error: err.response }); }); + if(!res) return; this.savedBrew = res.body; history.replaceState(null, null, `/edit/${this.savedBrew.editId}`); @@ -266,75 +262,8 @@ const EditPage = createClass({ }, renderSaveButton : function(){ - if(this.state.errors){ - let errMsg = ''; - try { - errMsg += `${this.state.errors.toString()}\n\n`; - errMsg += `\`\`\`\n${this.state.errors.stack}\n`; - errMsg += `${JSON.stringify(this.state.errors.response.error, null, ' ')}\n\`\`\``; - console.log(errMsg); - } catch (e){} - - // if(this.state.errors.status == '401'){ - // return - // Oops! - //
- // You must be signed in to a Google account - // to save this to
Google Drive!
- // - //
- // Sign In - //
- //
- //
- // Not Now - //
- //
- //
; - // } - - if(this.state.errors.response.req.url.match(/^\/api.*Google.*$/m)){ - return - Oops! -
- Looks like your Google credentials have - expired! Visit our log in page to sign out - and sign back in with Google, - then try saving again! - -
- Sign In -
-
-
- Not Now -
-
-
; - } - - if(this.state.errors.response.error.status === 409) { - const message = this.state.errors.response.body?.message; - return - Oops! -
- {message ? message : 'Conflict: please refresh to get latest changes'} -
-
; - } - - return - Oops! -
- Looks like there was a problem saving.
- Report the issue - here - . -
-
; + if(this.state.error){ + return require('../../utils/render-error-nav-item.js')(this, this.state.error); } if(this.state.autoSaveWarning && this.hasChanges()){ diff --git a/client/homebrew/pages/editPage/editPage.less b/client/homebrew/pages/editPage/editPage.less index 9d3f69221..2eb212f3f 100644 --- a/client/homebrew/pages/editPage/editPage.less +++ b/client/homebrew/pages/editPage/editPage.less @@ -26,74 +26,4 @@ padding : 0px; margin : -5px; } - .errorContainer{ - animation-name: glideDown; - animation-duration: 0.4s; - position : absolute; - top : 100%; - left : 50%; - z-index : 500; - width : 140px; - padding : 3px; - color : white; - background-color : #333; - border : 3px solid #444; - border-radius : 5px; - transform : translate(-50% + 3px, 10px); - text-align : center; - font-size : 10px; - font-weight : 800; - text-transform : uppercase; - a{ - color : @teal; - } - &:before { - content: ""; - width: 0px; - height: 0px; - position: absolute; - border-left: 10px solid transparent; - border-right: 10px solid transparent; - border-top: 10px solid transparent; - border-bottom: 10px solid #444; - left: 53px; - top: -23px; - } - &:after { - content: ""; - width: 0px; - height: 0px; - position: absolute; - border-left: 10px solid transparent; - border-right: 10px solid transparent; - border-top: 10px solid transparent; - border-bottom: 10px solid #333; - left: 53px; - top: -19px; - } - .deny { - width : 48%; - margin : 1px; - padding : 5px; - background-color : #333; - display : inline-block; - border-left : 1px solid #666; - .animate(background-color); - &:hover{ - background-color : red; - } - } - .confirm { - width : 48%; - margin : 1px; - padding : 5px; - background-color : #333; - display : inline-block; - color : white; - .animate(background-color); - &:hover{ - background-color : teal; - } - } - } } diff --git a/client/homebrew/pages/homePage/homePage.jsx b/client/homebrew/pages/homePage/homePage.jsx index 7e9d9c3b4..228a406d7 100644 --- a/client/homebrew/pages/homePage/homePage.jsx +++ b/client/homebrew/pages/homePage/homePage.jsx @@ -1,9 +1,10 @@ require('./homePage.less'); +require('../../styles/nav-item-error-container.less'); const React = require('react'); const createClass = require('create-react-class'); const _ = require('lodash'); const cx = require('classnames'); -const request = require('superagent'); +const request = require('../../utils/request-middleware.js'); const { Meta } = require('vitreum/headtags'); const Nav = require('naturalcrit/nav/nav.jsx'); @@ -31,14 +32,18 @@ const HomePage = createClass({ getInitialState : function() { return { brew : this.props.brew, - welcomeText : this.props.brew.text + welcomeText : this.props.brew.text, + error : undefined }; }, handleSave : function(){ request.post('/api') .send(this.state.brew) .end((err, res)=>{ - if(err) return; + if(err) { + this.setState({ error: err.response }); + return; + } const brew = res.body; window.location = `/edit/${brew.editId}`; }); @@ -51,9 +56,16 @@ const HomePage = createClass({ brew : { ...prevState.brew, text: text } })); }, + renderSaveError : function(){ + if(this.state.error) { + return require('../../utils/render-error-nav-item.js')(this, this.state.error); + } + return null; + }, renderNavbar : function(){ return + {this.renderSaveError()} diff --git a/client/homebrew/pages/homePage/homePage.less b/client/homebrew/pages/homePage/homePage.less index 719e89f22..b0320d8d2 100644 --- a/client/homebrew/pages/homePage/homePage.less +++ b/client/homebrew/pages/homePage/homePage.less @@ -40,4 +40,15 @@ right : 350px; } } + + .navItem.save{ + background-color: @orange; + &:hover{ + background-color: @green; + } + &.error{ + position : relative; + background-color : @red; + } + } } diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index cd197f5fb..8f9fc7649 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -1,9 +1,10 @@ /*eslint max-lines: ["warn", {"max": 300, "skipBlankLines": true, "skipComments": true}]*/ require('./newPage.less'); +require('../../styles/nav-item-error-container.less'); const React = require('react'); const createClass = require('create-react-class'); const _ = require('lodash'); -const request = require('superagent'); +const request = require('../../utils/request-middleware.js'); const Markdown = require('naturalcrit/markdown.js'); @@ -39,7 +40,7 @@ const NewPage = createClass({ brew : brew, isSaving : false, saveGoogle : (global.account && global.account.googleId ? true : false), - errors : null, + error : null, htmlErrors : Markdown.validate(brew.text) }; }, @@ -122,14 +123,6 @@ const NewPage = createClass({ })); }, - clearErrors : function(){ - this.setState({ - errors : null, - isSaving : false - - }); - }, - save : async function(){ this.setState({ isSaving : true @@ -152,7 +145,7 @@ const NewPage = createClass({ .send(brew) .catch((err)=>{ console.log(err); - this.setState({ isSaving: false, errors: err }); + this.setState({ isSaving: false, error: err.response }); }); if(!res) return; @@ -164,65 +157,8 @@ const NewPage = createClass({ }, renderSaveButton : function(){ - if(this.state.errors){ - let errMsg = ''; - try { - errMsg += `${this.state.errors.toString()}\n\n`; - errMsg += `\`\`\`\n${this.state.errors.stack}\n`; - errMsg += `${JSON.stringify(this.state.errors.response.error, null, ' ')}\n\`\`\``; - console.log(errMsg); - } catch (e){} - - // if(this.state.errors.status == '401'){ - // return - // Oops! - //
- // You must be signed in to a Google account - // to save this to
Google Drive!
- // - //
- // Sign In - //
- //
- //
- // Not Now - //
- //
- //
; - // } - - if(this.state.errors.response.req.url.match(/^\/api.*Google.*$/m)){ - return - Oops! -
- Looks like your Google credentials have - expired! Visit our log in page to sign out - and sign back in with Google, - then try saving again! - -
- Sign In -
-
-
- Not Now -
-
-
; - } - - return - Oops! -
- Looks like there was a problem saving.
- Report the issue - here - . -
-
; + if(this.state.error){ + return require('../../utils/render-error-nav-item.js')(this, this.state.error); } if(this.state.isSaving){ diff --git a/client/homebrew/pages/newPage/newPage.less b/client/homebrew/pages/newPage/newPage.less index 4f39e626d..eb80cad38 100644 --- a/client/homebrew/pages/newPage/newPage.less +++ b/client/homebrew/pages/newPage/newPage.less @@ -9,74 +9,4 @@ background-color : @red; } } - .errorContainer{ - animation-name: glideDown; - animation-duration: 0.4s; - position : absolute; - top : 100%; - left : 50%; - z-index : 100000; - width : 140px; - padding : 3px; - color : white; - background-color : #333; - border : 3px solid #444; - border-radius : 5px; - transform : translate(-50% + 3px, 10px); - text-align : center; - font-size : 10px; - font-weight : 800; - text-transform : uppercase; - a{ - color : @teal; - } - &:before { - content: ""; - width: 0px; - height: 0px; - position: absolute; - border-left: 10px solid transparent; - border-right: 10px solid transparent; - border-top: 10px solid transparent; - border-bottom: 10px solid #444; - left: 53px; - top: -23px; - } - &:after { - content: ""; - width: 0px; - height: 0px; - position: absolute; - border-left: 10px solid transparent; - border-right: 10px solid transparent; - border-top: 10px solid transparent; - border-bottom: 10px solid #333; - left: 53px; - top: -19px; - } - .deny { - width : 48%; - margin : 1px; - padding : 5px; - background-color : #333; - display : inline-block; - border-left : 1px solid #666; - .animate(background-color); - &:hover{ - background-color : red; - } - } - .confirm { - width : 48%; - margin : 1px; - padding : 5px; - background-color : #333; - display : inline-block; - color : white; - .animate(background-color); - &:hover{ - background-color : teal; - } - } - } } diff --git a/client/homebrew/styles/nav-item-error-container.less b/client/homebrew/styles/nav-item-error-container.less new file mode 100644 index 000000000..2161208c2 --- /dev/null +++ b/client/homebrew/styles/nav-item-error-container.less @@ -0,0 +1,72 @@ +.navItem { + .errorContainer{ + animation-name: glideDown; + animation-duration: 0.4s; + position : absolute; + top : 100%; + left : 50%; + z-index : 500; + width : 140px; + padding : 3px; + color : white; + background-color : #333; + border : 3px solid #444; + border-radius : 5px; + transform : translate(-50% + 3px, 10px); + text-align : center; + font-size : 10px; + font-weight : 800; + text-transform : uppercase; + a{ + color : @teal; + } + &:before { + content: ""; + width: 0px; + height: 0px; + position: absolute; + border-left: 10px solid transparent; + border-right: 10px solid transparent; + border-top: 10px solid transparent; + border-bottom: 10px solid #444; + left: 53px; + top: -23px; + } + &:after { + content: ""; + width: 0px; + height: 0px; + position: absolute; + border-left: 10px solid transparent; + border-right: 10px solid transparent; + border-top: 10px solid transparent; + border-bottom: 10px solid #333; + left: 53px; + top: -19px; + } + .deny { + width : 48%; + margin : 1px; + padding : 5px; + background-color : #333; + display : inline-block; + border-left : 1px solid #666; + .animate(background-color); + &:hover{ + background-color : red; + } + } + .confirm { + width : 48%; + margin : 1px; + padding : 5px; + background-color : #333; + display : inline-block; + color : white; + .animate(background-color); + &:hover{ + background-color : teal; + } + } + } +} \ No newline at end of file diff --git a/client/homebrew/utils/render-error-nav-item.js b/client/homebrew/utils/render-error-nav-item.js new file mode 100644 index 000000000..bf3960a29 --- /dev/null +++ b/client/homebrew/utils/render-error-nav-item.js @@ -0,0 +1,71 @@ +const React = require('react'); +const Nav = require('naturalcrit/nav/nav.jsx'); + +module.exports = function(component, error){ + const clearError = ()=>{ + const state = { + error : null + }; + if(component.state.isSaving) { + state.isSaving = false; + } + component.setState(state); + }; + + const status = error.status; + const message = error.body?.message; + let errMsg = ''; + try { + errMsg += `${error.toString()}\n\n`; + errMsg += `\`\`\`\n${error.stack}\n`; + errMsg += `${JSON.stringify(error.error, null, ' ')}\n\`\`\``; + console.log(errMsg); + } catch (e){} + + if(status === 409) { + return + Oops! +
+ {message ?? 'Conflict: please refresh to get latest changes'} +
+
; + } else if(status === 412) { + return + Oops! +
+ {message ?? 'Your client is out of date. Please save your changes elsewhere and refresh.'} +
+
; + } + + if(error.req.url.match(/^\/api.*Google.*$/m)){ + return + Oops! +
+ Looks like your Google credentials have + expired! Visit our log in page to sign out + and sign back in with Google, + then try saving again! + +
+ Sign In +
+
+
+ Not Now +
+
+
; + } + + return + Oops! +
+ Looks like there was a problem saving.
+ Report the issue + here + . +
+
; +}; \ No newline at end of file diff --git a/client/homebrew/utils/request-middleware.js b/client/homebrew/utils/request-middleware.js new file mode 100644 index 000000000..441218521 --- /dev/null +++ b/client/homebrew/utils/request-middleware.js @@ -0,0 +1,12 @@ +const request = require('superagent'); + +const addHeader = (request)=>request.set('Homebrewery-Version', sessionStorage.getItem('version')); + +const requestMiddleware = { + get : (path)=>addHeader(request.get(path)), + put : (path)=>addHeader(request.put(path)), + post : (path)=>addHeader(request.post(path)), + delete : (path)=>addHeader(request.delete(path)), +}; + +module.exports = requestMiddleware; \ No newline at end of file diff --git a/client/template.js b/client/template.js index cab4790b9..a36a04f12 100644 --- a/client/template.js +++ b/client/template.js @@ -22,6 +22,9 @@ const template = async function(name, title='', props = {}){
${require(`../build/${name}/ssr.js`)(props)}
+ diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 550da0ddd..e108a3946 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -336,6 +336,7 @@ If you believe you should have access to this brew, ask the file owner to invite } }; +router.use('/api', require('./middleware.js').versionMismatch); router.post('/api', asyncHandler(api.newBrew)); router.put('/api/:id', asyncHandler(api.getBrew('edit', true)), asyncHandler(api.updateBrew)); router.put('/api/update/:id', asyncHandler(api.getBrew('edit', true)), asyncHandler(api.updateBrew)); diff --git a/server/middleware.js b/server/middleware.js new file mode 100644 index 000000000..2d40d7263 --- /dev/null +++ b/server/middleware.js @@ -0,0 +1,17 @@ +const middleware = { + versionMismatch : (req, res, next)=>{ + const userVersion = req.get('Homebrewery-Version'); + const version = require('./../package.json').version; + + if(userVersion != version) { + console.warn(`Version mismatch -- expected: ${version}, actual: ${userVersion}`); + return res.status(412).send({ + message : `Client version ${userVersion} is out of date. Please save your changes elsewhere and refresh to pick up client version ${version}.` + }); + } + + next(); + } +}; + +module.exports = middleware; \ No newline at end of file From 7755affa1ef8994b239ccdbdc698323ef936a96e Mon Sep 17 00:00:00 2001 From: Charlie Humphreys Date: Fri, 20 Jan 2023 23:07:24 -0600 Subject: [PATCH 2/5] update based on feedback --- client/homebrew/pages/editPage/editPage.jsx | 3 +-- client/homebrew/pages/homePage/homePage.jsx | 3 +-- client/homebrew/pages/newPage/newPage.jsx | 3 +-- ...or-nav-item.js => render-error-nav-item.jsx} | 1 + .../render-error-nav-item.less} | 0 client/homebrew/utils/request-middleware.js | 2 +- client/template.js | 3 --- server/homebrew.api.js | 2 +- server/middleware.js | 17 ----------------- server/middleware/check-client-version.js | 12 ++++++++++++ 10 files changed, 18 insertions(+), 28 deletions(-) rename client/homebrew/utils/{render-error-nav-item.js => render-error-nav-item.jsx} (98%) rename client/homebrew/{styles/nav-item-error-container.less => utils/render-error-nav-item.less} (100%) delete mode 100644 server/middleware.js create mode 100644 server/middleware/check-client-version.js diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 6f1a8d91c..6d8939d56 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -1,6 +1,5 @@ /* eslint-disable max-lines */ require('./editPage.less'); -require('../../styles/nav-item-error-container.less'); const React = require('react'); const createClass = require('create-react-class'); const _ = require('lodash'); @@ -263,7 +262,7 @@ const EditPage = createClass({ renderSaveButton : function(){ if(this.state.error){ - return require('../../utils/render-error-nav-item.js')(this, this.state.error); + return require('../../utils/render-error-nav-item.jsx')(this, this.state.error); } if(this.state.autoSaveWarning && this.hasChanges()){ diff --git a/client/homebrew/pages/homePage/homePage.jsx b/client/homebrew/pages/homePage/homePage.jsx index 228a406d7..b594a256e 100644 --- a/client/homebrew/pages/homePage/homePage.jsx +++ b/client/homebrew/pages/homePage/homePage.jsx @@ -1,5 +1,4 @@ require('./homePage.less'); -require('../../styles/nav-item-error-container.less'); const React = require('react'); const createClass = require('create-react-class'); const _ = require('lodash'); @@ -58,7 +57,7 @@ const HomePage = createClass({ }, renderSaveError : function(){ if(this.state.error) { - return require('../../utils/render-error-nav-item.js')(this, this.state.error); + return require('../../utils/render-error-nav-item.jsx')(this, this.state.error); } return null; }, diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index 8f9fc7649..1563d0fd8 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -1,6 +1,5 @@ /*eslint max-lines: ["warn", {"max": 300, "skipBlankLines": true, "skipComments": true}]*/ require('./newPage.less'); -require('../../styles/nav-item-error-container.less'); const React = require('react'); const createClass = require('create-react-class'); const _ = require('lodash'); @@ -158,7 +157,7 @@ const NewPage = createClass({ renderSaveButton : function(){ if(this.state.error){ - return require('../../utils/render-error-nav-item.js')(this, this.state.error); + return require('../../utils/render-error-nav-item.jsx')(this, this.state.error); } if(this.state.isSaving){ diff --git a/client/homebrew/utils/render-error-nav-item.js b/client/homebrew/utils/render-error-nav-item.jsx similarity index 98% rename from client/homebrew/utils/render-error-nav-item.js rename to client/homebrew/utils/render-error-nav-item.jsx index bf3960a29..0359c1e43 100644 --- a/client/homebrew/utils/render-error-nav-item.js +++ b/client/homebrew/utils/render-error-nav-item.jsx @@ -1,3 +1,4 @@ +require('./render-error-nav-item.less'); const React = require('react'); const Nav = require('naturalcrit/nav/nav.jsx'); diff --git a/client/homebrew/styles/nav-item-error-container.less b/client/homebrew/utils/render-error-nav-item.less similarity index 100% rename from client/homebrew/styles/nav-item-error-container.less rename to client/homebrew/utils/render-error-nav-item.less diff --git a/client/homebrew/utils/request-middleware.js b/client/homebrew/utils/request-middleware.js index 441218521..f6bc2571b 100644 --- a/client/homebrew/utils/request-middleware.js +++ b/client/homebrew/utils/request-middleware.js @@ -1,6 +1,6 @@ const request = require('superagent'); -const addHeader = (request)=>request.set('Homebrewery-Version', sessionStorage.getItem('version')); +const addHeader = (request)=>request.set('Homebrewery-Version', global.version); const requestMiddleware = { get : (path)=>addHeader(request.get(path)), diff --git a/client/template.js b/client/template.js index a36a04f12..cab4790b9 100644 --- a/client/template.js +++ b/client/template.js @@ -22,9 +22,6 @@ const template = async function(name, title='', props = {}){
${require(`../build/${name}/ssr.js`)(props)}
- diff --git a/server/homebrew.api.js b/server/homebrew.api.js index e108a3946..e6314a635 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -336,7 +336,7 @@ If you believe you should have access to this brew, ask the file owner to invite } }; -router.use('/api', require('./middleware.js').versionMismatch); +router.use('/api', require('./middleware/check-client-version.js')); router.post('/api', asyncHandler(api.newBrew)); router.put('/api/:id', asyncHandler(api.getBrew('edit', true)), asyncHandler(api.updateBrew)); router.put('/api/update/:id', asyncHandler(api.getBrew('edit', true)), asyncHandler(api.updateBrew)); diff --git a/server/middleware.js b/server/middleware.js deleted file mode 100644 index 2d40d7263..000000000 --- a/server/middleware.js +++ /dev/null @@ -1,17 +0,0 @@ -const middleware = { - versionMismatch : (req, res, next)=>{ - const userVersion = req.get('Homebrewery-Version'); - const version = require('./../package.json').version; - - if(userVersion != version) { - console.warn(`Version mismatch -- expected: ${version}, actual: ${userVersion}`); - return res.status(412).send({ - message : `Client version ${userVersion} is out of date. Please save your changes elsewhere and refresh to pick up client version ${version}.` - }); - } - - next(); - } -}; - -module.exports = middleware; \ No newline at end of file diff --git a/server/middleware/check-client-version.js b/server/middleware/check-client-version.js new file mode 100644 index 000000000..e9caf6eff --- /dev/null +++ b/server/middleware/check-client-version.js @@ -0,0 +1,12 @@ +module.exports = (req, res, next)=>{ + const userVersion = req.get('Homebrewery-Version'); + const version = require('../../package.json').version; + + if(userVersion != version) { + return res.status(412).send({ + message : `Client version ${userVersion} is out of date. Please save your changes elsewhere and refresh to pick up client version ${version}.` + }); + } + + next(); +}; From 79db97efdf34ef730d6afa049c0febfeda1d619c Mon Sep 17 00:00:00 2001 From: Charlie Humphreys Date: Sat, 21 Jan 2023 00:25:35 -0600 Subject: [PATCH 3/5] create error navitem and use it in all necessary use cases --- client/homebrew/navbar/error-navitem.jsx | 84 +++++++++++++++++++ .../error-navitem.less} | 7 +- .../basePages/listPage/brewItem/brewItem.jsx | 13 ++- .../pages/basePages/listPage/listPage.jsx | 5 +- client/homebrew/pages/editPage/editPage.jsx | 16 ++-- client/homebrew/pages/editPage/editPage.less | 4 - client/homebrew/pages/homePage/homePage.jsx | 12 ++- client/homebrew/pages/homePage/homePage.less | 4 - client/homebrew/pages/newPage/newPage.jsx | 10 +-- client/homebrew/pages/newPage/newPage.less | 4 - client/homebrew/pages/userPage/userPage.jsx | 15 +++- .../homebrew/utils/render-error-nav-item.jsx | 72 ---------------- 12 files changed, 133 insertions(+), 113 deletions(-) create mode 100644 client/homebrew/navbar/error-navitem.jsx rename client/homebrew/{utils/render-error-nav-item.less => navbar/error-navitem.less} (94%) delete mode 100644 client/homebrew/utils/render-error-nav-item.jsx diff --git a/client/homebrew/navbar/error-navitem.jsx b/client/homebrew/navbar/error-navitem.jsx new file mode 100644 index 000000000..19ebd0213 --- /dev/null +++ b/client/homebrew/navbar/error-navitem.jsx @@ -0,0 +1,84 @@ +require('./error-navitem.less'); +const React = require('react'); +const Nav = require('naturalcrit/nav/nav.jsx'); +const createClass = require('create-react-class'); + +const ErrorNavItem = createClass({ + getDefaultProps : function() { + return { + error : '', + parent : null + }; + }, + render : function() { + const clearError = ()=>{ + const state = { + error : null + }; + if(this.props.parent.state.isSaving) { + state.isSaving = false; + } + this.props.parent.setState(state); + }; + + const error = this.props.error; + const status = error.status; + const message = error.body?.message; + let errMsg = ''; + try { + errMsg += `${error.toString()}\n\n`; + errMsg += `\`\`\`\n${error.stack}\n`; + errMsg += `${JSON.stringify(error.error, null, ' ')}\n\`\`\``; + console.log(errMsg); + } catch (e){} + + if(status === 409) { + return + Oops! +
+ {message ?? 'Conflict: please refresh to get latest changes'} +
+
; + } else if(status === 412) { + return + Oops! +
+ {message ?? 'Your client is out of date. Please save your changes elsewhere and refresh.'} +
+
; + } + + if(error.req.url.match(/^\/api.*Google.*$/m)){ + return + Oops! +
+ Looks like your Google credentials have + expired! Visit our log in page to sign out + and sign back in with Google, + then try saving again! + +
+ Sign In +
+
+
+ Not Now +
+
+
; + } + + return + Oops! +
+ Looks like there was a problem saving.
+ Report the issue + here + . +
+
; + } +}); + +module.exports = ErrorNavItem; \ No newline at end of file diff --git a/client/homebrew/utils/render-error-nav-item.less b/client/homebrew/navbar/error-navitem.less similarity index 94% rename from client/homebrew/utils/render-error-nav-item.less rename to client/homebrew/navbar/error-navitem.less index 2161208c2..8a7cabb19 100644 --- a/client/homebrew/utils/render-error-nav-item.less +++ b/client/homebrew/navbar/error-navitem.less @@ -1,11 +1,16 @@ .navItem { + &.error { + position : relative; + background-color : @red; + } + .errorContainer{ animation-name: glideDown; animation-duration: 0.4s; position : absolute; top : 100%; left : 50%; - z-index : 500; + z-index : 1000; width : 140px; padding : 3px; color : white; diff --git a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx index d17c020af..327fe21be 100644 --- a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx +++ b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx @@ -4,7 +4,7 @@ const createClass = require('create-react-class'); const _ = require('lodash'); const cx = require('classnames'); const moment = require('moment'); -const request = require('superagent'); +const request = require('../../../../utils/request-middleware.js'); const googleDriveIcon = require('../../../../googleDrive.png'); const dedent = require('dedent-tabs').default; @@ -18,7 +18,8 @@ const BrewItem = createClass({ description : '', authors : [], stubbed : true - } + }, + reportError : null }; }, @@ -33,8 +34,12 @@ const BrewItem = createClass({ request.delete(`/api/${this.props.brew.googleId ?? ''}${this.props.brew.editId}`) .send() - .end(function(err, res){ - location.reload(); + .end((err, res)=>{ + if(err && this.props.reportError) { + this.props.reportError(err.response); + } else { + location.reload(); + } }); }, diff --git a/client/homebrew/pages/basePages/listPage/listPage.jsx b/client/homebrew/pages/basePages/listPage/listPage.jsx index 162aba5e4..b80b04899 100644 --- a/client/homebrew/pages/basePages/listPage/listPage.jsx +++ b/client/homebrew/pages/basePages/listPage/listPage.jsx @@ -23,7 +23,8 @@ const ListPage = createClass({ brews : [] } ], - navItems : <> + navItems : <>, + reportError : null }; }, getInitialState : function() { @@ -81,7 +82,7 @@ const ListPage = createClass({ if(!brews || !brews.length) return
No Brews.
; return _.map(brews, (brew, idx)=>{ - return ; + return ; }); }, diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 6d8939d56..481ac280f 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -12,6 +12,7 @@ const Navbar = require('../../navbar/navbar.jsx'); const NewBrew = require('../../navbar/newbrew.navitem.jsx'); const HelpNavItem = require('../../navbar/help.navitem.jsx'); const PrintLink = require('../../navbar/print.navitem.jsx'); +const ErrorNavItem = require('../../navbar/error-navitem.jsx'); const Account = require('../../navbar/account.navitem.jsx'); const RecentNavItem = require('../../navbar/recent.navitem.jsx').both; @@ -261,10 +262,6 @@ const EditPage = createClass({ }, renderSaveButton : function(){ - if(this.state.error){ - return require('../../utils/render-error-nav-item.jsx')(this, this.state.error); - } - if(this.state.autoSaveWarning && this.hasChanges()){ this.setAutosaveWarning(); const elapsedTime = Math.round((new Date() - this.state.unsavedTime) / 1000 / 60); @@ -352,10 +349,13 @@ const EditPage = createClass({ {this.renderGoogleDriveIcon()} - - {this.renderSaveButton()} - {this.renderAutoSaveButton()} - + {this.state.error ? + : + + {this.renderSaveButton()} + {this.renderAutoSaveButton()} + + } diff --git a/client/homebrew/pages/editPage/editPage.less b/client/homebrew/pages/editPage/editPage.less index 2eb212f3f..581e6dfa2 100644 --- a/client/homebrew/pages/editPage/editPage.less +++ b/client/homebrew/pages/editPage/editPage.less @@ -13,10 +13,6 @@ cursor : initial; color : #666; } - &.error{ - position : relative; - background-color : @red; - } } .googleDriveStorage { position : relative; diff --git a/client/homebrew/pages/homePage/homePage.jsx b/client/homebrew/pages/homePage/homePage.jsx index b594a256e..f4a30b34b 100644 --- a/client/homebrew/pages/homePage/homePage.jsx +++ b/client/homebrew/pages/homePage/homePage.jsx @@ -12,6 +12,7 @@ const NewBrewItem = require('../../navbar/newbrew.navitem.jsx'); const HelpNavItem = require('../../navbar/help.navitem.jsx'); const RecentNavItem = require('../../navbar/recent.navitem.jsx').both; const AccountNavItem = require('../../navbar/account.navitem.jsx'); +const ErrorNavItem = require('../../navbar/error-navitem.jsx'); const SplitPane = require('naturalcrit/splitPane/splitPane.jsx'); @@ -55,16 +56,13 @@ const HomePage = createClass({ brew : { ...prevState.brew, text: text } })); }, - renderSaveError : function(){ - if(this.state.error) { - return require('../../utils/render-error-nav-item.jsx')(this, this.state.error); - } - return null; - }, renderNavbar : function(){ return - {this.renderSaveError()} + {this.state.error ? + : + null + } diff --git a/client/homebrew/pages/homePage/homePage.less b/client/homebrew/pages/homePage/homePage.less index b0320d8d2..a7523bd3c 100644 --- a/client/homebrew/pages/homePage/homePage.less +++ b/client/homebrew/pages/homePage/homePage.less @@ -46,9 +46,5 @@ &:hover{ background-color: @green; } - &.error{ - position : relative; - background-color : @red; - } } } diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index 1563d0fd8..a4044a0e9 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -10,6 +10,7 @@ const Markdown = require('naturalcrit/markdown.js'); const Nav = require('naturalcrit/nav/nav.jsx'); const Navbar = require('../../navbar/navbar.jsx'); const AccountNavItem = require('../../navbar/account.navitem.jsx'); +const ErrorNavItem = require('../../navbar/error-navitem.jsx'); const RecentNavItem = require('../../navbar/recent.navitem.jsx').both; const HelpNavItem = require('../../navbar/help.navitem.jsx'); @@ -156,10 +157,6 @@ const NewPage = createClass({ }, renderSaveButton : function(){ - if(this.state.error){ - return require('../../utils/render-error-nav-item.jsx')(this, this.state.error); - } - if(this.state.isSaving){ return save... @@ -189,7 +186,10 @@ const NewPage = createClass({ - {this.renderSaveButton()} + {this.state.error ? + : + this.renderSaveButton() + } {this.renderLocalPrintButton()} diff --git a/client/homebrew/pages/newPage/newPage.less b/client/homebrew/pages/newPage/newPage.less index eb80cad38..f83827ffb 100644 --- a/client/homebrew/pages/newPage/newPage.less +++ b/client/homebrew/pages/newPage/newPage.less @@ -4,9 +4,5 @@ &:hover{ background-color: @green; } - &.error{ - position : relative; - background-color : @red; - } } } diff --git a/client/homebrew/pages/userPage/userPage.jsx b/client/homebrew/pages/userPage/userPage.jsx index 7341c61ad..1e051987b 100644 --- a/client/homebrew/pages/userPage/userPage.jsx +++ b/client/homebrew/pages/userPage/userPage.jsx @@ -12,6 +12,7 @@ const RecentNavItem = require('../../navbar/recent.navitem.jsx').both; const Account = require('../../navbar/account.navitem.jsx'); const NewBrew = require('../../navbar/newbrew.navitem.jsx'); const HelpNavItem = require('../../navbar/help.navitem.jsx'); +const ErrorNavItem = require('../../navbar/error-navitem.jsx'); const UserPage = createClass({ displayName : 'UserPage', @@ -19,7 +20,8 @@ const UserPage = createClass({ return { username : '', brews : [], - query : '' + query : '', + error : null }; }, getInitialState : function() { @@ -50,10 +52,19 @@ const UserPage = createClass({ brewCollection : brewCollection }; }, + errorReported : function(error) { + this.setState({ + error + }); + }, navItems : function() { return + {this.state.error ? + : + null + } @@ -63,7 +74,7 @@ const UserPage = createClass({ }, render : function(){ - return ; + return ; } }); diff --git a/client/homebrew/utils/render-error-nav-item.jsx b/client/homebrew/utils/render-error-nav-item.jsx deleted file mode 100644 index 0359c1e43..000000000 --- a/client/homebrew/utils/render-error-nav-item.jsx +++ /dev/null @@ -1,72 +0,0 @@ -require('./render-error-nav-item.less'); -const React = require('react'); -const Nav = require('naturalcrit/nav/nav.jsx'); - -module.exports = function(component, error){ - const clearError = ()=>{ - const state = { - error : null - }; - if(component.state.isSaving) { - state.isSaving = false; - } - component.setState(state); - }; - - const status = error.status; - const message = error.body?.message; - let errMsg = ''; - try { - errMsg += `${error.toString()}\n\n`; - errMsg += `\`\`\`\n${error.stack}\n`; - errMsg += `${JSON.stringify(error.error, null, ' ')}\n\`\`\``; - console.log(errMsg); - } catch (e){} - - if(status === 409) { - return - Oops! -
- {message ?? 'Conflict: please refresh to get latest changes'} -
-
; - } else if(status === 412) { - return - Oops! -
- {message ?? 'Your client is out of date. Please save your changes elsewhere and refresh.'} -
-
; - } - - if(error.req.url.match(/^\/api.*Google.*$/m)){ - return - Oops! -
- Looks like your Google credentials have - expired! Visit our log in page to sign out - and sign back in with Google, - then try saving again! - -
- Sign In -
-
-
- Not Now -
-
-
; - } - - return - Oops! -
- Looks like there was a problem saving.
- Report the issue - here - . -
-
; -}; \ No newline at end of file From 6ddf0bb8893aa166073fdcb7249119c3603db45f Mon Sep 17 00:00:00 2001 From: Charlie Humphreys Date: Mon, 23 Jan 2023 09:35:19 -0600 Subject: [PATCH 4/5] update metadata editor to include error handling --- client/homebrew/editor/editor.jsx | 4 +++- .../editor/metadataEditor/metadataEditor.jsx | 13 +++++++++---- .../pages/basePages/listPage/brewItem/brewItem.jsx | 4 ++-- client/homebrew/pages/editPage/editPage.jsx | 7 +++++++ 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index 75ab08de4..58e84e4fc 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -32,6 +32,7 @@ const Editor = createClass({ onTextChange : ()=>{}, onStyleChange : ()=>{}, onMetaChange : ()=>{}, + reportError : ()=>{}, renderer : 'legacy' }; @@ -291,7 +292,8 @@ const Editor = createClass({ rerenderParent={this.rerenderParent} /> + onChange={this.props.onMetaChange} + reportError={this.props.reportError}/> ; } }, diff --git a/client/homebrew/editor/metadataEditor/metadataEditor.jsx b/client/homebrew/editor/metadataEditor/metadataEditor.jsx index 03f40e7cf..913e79d20 100644 --- a/client/homebrew/editor/metadataEditor/metadataEditor.jsx +++ b/client/homebrew/editor/metadataEditor/metadataEditor.jsx @@ -4,7 +4,7 @@ const React = require('react'); const createClass = require('create-react-class'); const _ = require('lodash'); const cx = require('classnames'); -const request = require('superagent'); +const request = require('../../utils/request-middleware.js'); const Nav = require('naturalcrit/nav/nav.jsx'); const StringArrayEditor = require('../stringArrayEditor/stringArrayEditor.jsx'); @@ -37,7 +37,8 @@ const MetadataEditor = createClass({ renderer : 'legacy', theme : '5ePHB' }, - onChange : ()=>{} + onChange : ()=>{}, + reportError : ()=>{} }; }, @@ -121,8 +122,12 @@ const MetadataEditor = createClass({ request.delete(`/api/${this.props.metadata.googleId ?? ''}${this.props.metadata.editId}`) .send() - .end(function(err, res){ - window.location.href = '/'; + .end((err, res)=>{ + if(err) { + this.props.reportError(err.response); + } else { + window.location.href = '/'; + } }); }, diff --git a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx index 327fe21be..1c150800e 100644 --- a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx +++ b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx @@ -19,7 +19,7 @@ const BrewItem = createClass({ authors : [], stubbed : true }, - reportError : null + reportError : ()=>{} }; }, @@ -35,7 +35,7 @@ const BrewItem = createClass({ request.delete(`/api/${this.props.brew.googleId ?? ''}${this.props.brew.editId}`) .send() .end((err, res)=>{ - if(err && this.props.reportError) { + if(err) { this.props.reportError(err.response); } else { location.reload(); diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 481ac280f..59a0163bf 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -305,6 +305,12 @@ const EditPage = createClass({ this.warningTimer; }, + errorReported : function(error) { + this.setState({ + error + }); + }, + renderAutoSaveButton : function(){ return Autosave @@ -393,6 +399,7 @@ const EditPage = createClass({ onTextChange={this.handleTextChange} onStyleChange={this.handleStyleChange} onMetaChange={this.handleMetaChange} + reportError={this.errorReported} renderer={this.state.brew.renderer} /> From 8f75ea4728cdbb5a02ce0e1693ca7269394e967f Mon Sep 17 00:00:00 2001 From: Charlie Humphreys Date: Mon, 23 Jan 2023 14:22:18 -0600 Subject: [PATCH 5/5] fix error issue --- client/homebrew/editor/metadataEditor/metadataEditor.jsx | 2 +- client/homebrew/navbar/error-navitem.jsx | 9 +++++---- .../pages/basePages/listPage/brewItem/brewItem.jsx | 2 +- client/homebrew/pages/editPage/editPage.jsx | 2 +- client/homebrew/pages/homePage/homePage.jsx | 2 +- client/homebrew/pages/newPage/newPage.jsx | 2 +- 6 files changed, 10 insertions(+), 9 deletions(-) diff --git a/client/homebrew/editor/metadataEditor/metadataEditor.jsx b/client/homebrew/editor/metadataEditor/metadataEditor.jsx index 913e79d20..e19f6644a 100644 --- a/client/homebrew/editor/metadataEditor/metadataEditor.jsx +++ b/client/homebrew/editor/metadataEditor/metadataEditor.jsx @@ -124,7 +124,7 @@ const MetadataEditor = createClass({ .send() .end((err, res)=>{ if(err) { - this.props.reportError(err.response); + this.props.reportError(err); } else { window.location.href = '/'; } diff --git a/client/homebrew/navbar/error-navitem.jsx b/client/homebrew/navbar/error-navitem.jsx index 19ebd0213..efee04019 100644 --- a/client/homebrew/navbar/error-navitem.jsx +++ b/client/homebrew/navbar/error-navitem.jsx @@ -22,13 +22,14 @@ const ErrorNavItem = createClass({ }; const error = this.props.error; - const status = error.status; - const message = error.body?.message; + const response = error.response; + const status = response.status; + const message = response.body?.message; let errMsg = ''; try { errMsg += `${error.toString()}\n\n`; errMsg += `\`\`\`\n${error.stack}\n`; - errMsg += `${JSON.stringify(error.error, null, ' ')}\n\`\`\``; + errMsg += `${JSON.stringify(response.error, null, ' ')}\n\`\`\``; console.log(errMsg); } catch (e){} @@ -48,7 +49,7 @@ const ErrorNavItem = createClass({ ; } - if(error.req.url.match(/^\/api.*Google.*$/m)){ + if(response.req.url.match(/^\/api.*Google.*$/m)){ return Oops!
diff --git a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx index 1c150800e..aa2f47431 100644 --- a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx +++ b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx @@ -36,7 +36,7 @@ const BrewItem = createClass({ .send() .end((err, res)=>{ if(err) { - this.props.reportError(err.response); + this.props.reportError(err); } else { location.reload(); } diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 59a0163bf..87bc7eefb 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -200,7 +200,7 @@ const EditPage = createClass({ .send(brew) .catch((err)=>{ console.log('Error Updating Local Brew'); - this.setState({ error: err.response }); + this.setState({ error: err }); }); if(!res) return; diff --git a/client/homebrew/pages/homePage/homePage.jsx b/client/homebrew/pages/homePage/homePage.jsx index f4a30b34b..9802517b1 100644 --- a/client/homebrew/pages/homePage/homePage.jsx +++ b/client/homebrew/pages/homePage/homePage.jsx @@ -41,7 +41,7 @@ const HomePage = createClass({ .send(this.state.brew) .end((err, res)=>{ if(err) { - this.setState({ error: err.response }); + this.setState({ error: err }); return; } const brew = res.body; diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index a4044a0e9..3e96ff5c0 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -145,7 +145,7 @@ const NewPage = createClass({ .send(brew) .catch((err)=>{ console.log(err); - this.setState({ isSaving: false, error: err.response }); + this.setState({ isSaving: false, error: err }); }); if(!res) return;