mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-04 21:12:41 +00:00
adjust frontend error handling, add client/server mismatch middleware
This commit is contained in:
@@ -1,9 +1,10 @@
|
|||||||
/* eslint-disable max-lines */
|
/* eslint-disable max-lines */
|
||||||
require('./editPage.less');
|
require('./editPage.less');
|
||||||
|
require('../../styles/nav-item-error-container.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const request = require('superagent');
|
const request = require('../../utils/request-middleware.js');
|
||||||
const { Meta } = require('vitreum/headtags');
|
const { Meta } = require('vitreum/headtags');
|
||||||
|
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('naturalcrit/nav/nav.jsx');
|
||||||
@@ -45,7 +46,7 @@ const EditPage = createClass({
|
|||||||
alertLoginToTransfer : false,
|
alertLoginToTransfer : false,
|
||||||
saveGoogle : this.props.brew.googleId ? true : false,
|
saveGoogle : this.props.brew.googleId ? true : false,
|
||||||
confirmGoogleTransfer : false,
|
confirmGoogleTransfer : false,
|
||||||
errors : null,
|
error : null,
|
||||||
htmlErrors : Markdown.validate(this.props.brew.text),
|
htmlErrors : Markdown.validate(this.props.brew.text),
|
||||||
url : '',
|
url : '',
|
||||||
autoSave : true,
|
autoSave : true,
|
||||||
@@ -60,7 +61,6 @@ const EditPage = createClass({
|
|||||||
url : window.location.href
|
url : window.location.href
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
this.savedBrew = JSON.parse(JSON.stringify(this.props.brew)); //Deep copy
|
this.savedBrew = JSON.parse(JSON.stringify(this.props.brew)); //Deep copy
|
||||||
|
|
||||||
this.setState({ autoSave: JSON.parse(localStorage.getItem('AUTOSAVE_ON')) ?? true }, ()=>{
|
this.setState({ autoSave: JSON.parse(localStorage.getItem('AUTOSAVE_ON')) ?? true }, ()=>{
|
||||||
@@ -157,7 +157,10 @@ const EditPage = createClass({
|
|||||||
this.setState((prevState)=>({
|
this.setState((prevState)=>({
|
||||||
confirmGoogleTransfer : !prevState.confirmGoogleTransfer
|
confirmGoogleTransfer : !prevState.confirmGoogleTransfer
|
||||||
}));
|
}));
|
||||||
this.clearErrors();
|
this.setState({
|
||||||
|
error : null,
|
||||||
|
isSaving : false
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
closeAlerts : function(event){
|
closeAlerts : function(event){
|
||||||
@@ -173,24 +176,16 @@ const EditPage = createClass({
|
|||||||
this.setState((prevState)=>({
|
this.setState((prevState)=>({
|
||||||
saveGoogle : !prevState.saveGoogle,
|
saveGoogle : !prevState.saveGoogle,
|
||||||
isSaving : false,
|
isSaving : false,
|
||||||
errors : null
|
error : null
|
||||||
}), ()=>this.save());
|
}), ()=>this.save());
|
||||||
},
|
},
|
||||||
|
|
||||||
clearErrors : function(){
|
|
||||||
this.setState({
|
|
||||||
errors : null,
|
|
||||||
isSaving : false
|
|
||||||
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
save : async function(){
|
save : async function(){
|
||||||
if(this.debounceSave && this.debounceSave.cancel) this.debounceSave.cancel();
|
if(this.debounceSave && this.debounceSave.cancel) this.debounceSave.cancel();
|
||||||
|
|
||||||
this.setState((prevState)=>({
|
this.setState((prevState)=>({
|
||||||
isSaving : true,
|
isSaving : true,
|
||||||
errors : null,
|
error : null,
|
||||||
htmlErrors : Markdown.validate(prevState.brew.text)
|
htmlErrors : Markdown.validate(prevState.brew.text)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -205,8 +200,9 @@ const EditPage = createClass({
|
|||||||
.send(brew)
|
.send(brew)
|
||||||
.catch((err)=>{
|
.catch((err)=>{
|
||||||
console.log('Error Updating Local Brew');
|
console.log('Error Updating Local Brew');
|
||||||
this.setState({ errors: err });
|
this.setState({ error: err.response });
|
||||||
});
|
});
|
||||||
|
if(!res) return;
|
||||||
|
|
||||||
this.savedBrew = res.body;
|
this.savedBrew = res.body;
|
||||||
history.replaceState(null, null, `/edit/${this.savedBrew.editId}`);
|
history.replaceState(null, null, `/edit/${this.savedBrew.editId}`);
|
||||||
@@ -266,75 +262,8 @@ const EditPage = createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
renderSaveButton : function(){
|
renderSaveButton : function(){
|
||||||
if(this.state.errors){
|
if(this.state.error){
|
||||||
let errMsg = '';
|
return require('../../utils/render-error-nav-item.js')(this, this.state.error);
|
||||||
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 <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
|
||||||
// Oops!
|
|
||||||
// <div className='errorContainer' onClick={this.clearErrors}>
|
|
||||||
// You must be signed in to a Google account
|
|
||||||
// to save this to<br />Google Drive!<br />
|
|
||||||
// <a target='_blank' rel='noopener noreferrer'
|
|
||||||
// href={`https://www.naturalcrit.com/login?redirect=${this.state.url}`}>
|
|
||||||
// <div className='confirm'>
|
|
||||||
// Sign In
|
|
||||||
// </div>
|
|
||||||
// </a>
|
|
||||||
// <div className='deny'>
|
|
||||||
// Not Now
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
// </Nav.item>;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if(this.state.errors.response.req.url.match(/^\/api.*Google.*$/m)){
|
|
||||||
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
|
||||||
Oops!
|
|
||||||
<div className='errorContainer' onClick={this.clearErrors}>
|
|
||||||
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!
|
|
||||||
<a target='_blank' rel='noopener noreferrer'
|
|
||||||
href={`https://www.naturalcrit.com/login?redirect=${this.state.url}`}>
|
|
||||||
<div className='confirm'>
|
|
||||||
Sign In
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<div className='deny'>
|
|
||||||
Not Now
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Nav.item>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.state.errors.response.error.status === 409) {
|
|
||||||
const message = this.state.errors.response.body?.message;
|
|
||||||
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
|
||||||
Oops!
|
|
||||||
<div className='errorContainer'>
|
|
||||||
{message ? message : 'Conflict: please refresh to get latest changes'}
|
|
||||||
</div>
|
|
||||||
</Nav.item>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
|
||||||
Oops!
|
|
||||||
<div className='errorContainer'>
|
|
||||||
Looks like there was a problem saving. <br />
|
|
||||||
Report the issue <a target='_blank' rel='noopener noreferrer'
|
|
||||||
href={`https://github.com/naturalcrit/homebrewery/issues/new?template=save_issue.yml&error-code=${encodeURIComponent(errMsg)}`}>
|
|
||||||
here
|
|
||||||
</a>.
|
|
||||||
</div>
|
|
||||||
</Nav.item>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.state.autoSaveWarning && this.hasChanges()){
|
if(this.state.autoSaveWarning && this.hasChanges()){
|
||||||
|
|||||||
@@ -26,74 +26,4 @@
|
|||||||
padding : 0px;
|
padding : 0px;
|
||||||
margin : -5px;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
require('./homePage.less');
|
require('./homePage.less');
|
||||||
|
require('../../styles/nav-item-error-container.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const cx = require('classnames');
|
const cx = require('classnames');
|
||||||
const request = require('superagent');
|
const request = require('../../utils/request-middleware.js');
|
||||||
const { Meta } = require('vitreum/headtags');
|
const { Meta } = require('vitreum/headtags');
|
||||||
|
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('naturalcrit/nav/nav.jsx');
|
||||||
@@ -31,14 +32,18 @@ const HomePage = createClass({
|
|||||||
getInitialState : function() {
|
getInitialState : function() {
|
||||||
return {
|
return {
|
||||||
brew : this.props.brew,
|
brew : this.props.brew,
|
||||||
welcomeText : this.props.brew.text
|
welcomeText : this.props.brew.text,
|
||||||
|
error : undefined
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
handleSave : function(){
|
handleSave : function(){
|
||||||
request.post('/api')
|
request.post('/api')
|
||||||
.send(this.state.brew)
|
.send(this.state.brew)
|
||||||
.end((err, res)=>{
|
.end((err, res)=>{
|
||||||
if(err) return;
|
if(err) {
|
||||||
|
this.setState({ error: err.response });
|
||||||
|
return;
|
||||||
|
}
|
||||||
const brew = res.body;
|
const brew = res.body;
|
||||||
window.location = `/edit/${brew.editId}`;
|
window.location = `/edit/${brew.editId}`;
|
||||||
});
|
});
|
||||||
@@ -51,9 +56,16 @@ const HomePage = createClass({
|
|||||||
brew : { ...prevState.brew, text: text }
|
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(){
|
renderNavbar : function(){
|
||||||
return <Navbar ver={this.props.ver}>
|
return <Navbar ver={this.props.ver}>
|
||||||
<Nav.section>
|
<Nav.section>
|
||||||
|
{this.renderSaveError()}
|
||||||
<NewBrewItem />
|
<NewBrewItem />
|
||||||
<HelpNavItem />
|
<HelpNavItem />
|
||||||
<RecentNavItem />
|
<RecentNavItem />
|
||||||
|
|||||||
@@ -40,4 +40,15 @@
|
|||||||
right : 350px;
|
right : 350px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navItem.save{
|
||||||
|
background-color: @orange;
|
||||||
|
&:hover{
|
||||||
|
background-color: @green;
|
||||||
|
}
|
||||||
|
&.error{
|
||||||
|
position : relative;
|
||||||
|
background-color : @red;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
/*eslint max-lines: ["warn", {"max": 300, "skipBlankLines": true, "skipComments": true}]*/
|
/*eslint max-lines: ["warn", {"max": 300, "skipBlankLines": true, "skipComments": true}]*/
|
||||||
require('./newPage.less');
|
require('./newPage.less');
|
||||||
|
require('../../styles/nav-item-error-container.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const request = require('superagent');
|
const request = require('../../utils/request-middleware.js');
|
||||||
|
|
||||||
const Markdown = require('naturalcrit/markdown.js');
|
const Markdown = require('naturalcrit/markdown.js');
|
||||||
|
|
||||||
@@ -39,7 +40,7 @@ const NewPage = createClass({
|
|||||||
brew : brew,
|
brew : brew,
|
||||||
isSaving : false,
|
isSaving : false,
|
||||||
saveGoogle : (global.account && global.account.googleId ? true : false),
|
saveGoogle : (global.account && global.account.googleId ? true : false),
|
||||||
errors : null,
|
error : null,
|
||||||
htmlErrors : Markdown.validate(brew.text)
|
htmlErrors : Markdown.validate(brew.text)
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -122,14 +123,6 @@ const NewPage = createClass({
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
clearErrors : function(){
|
|
||||||
this.setState({
|
|
||||||
errors : null,
|
|
||||||
isSaving : false
|
|
||||||
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
save : async function(){
|
save : async function(){
|
||||||
this.setState({
|
this.setState({
|
||||||
isSaving : true
|
isSaving : true
|
||||||
@@ -152,7 +145,7 @@ const NewPage = createClass({
|
|||||||
.send(brew)
|
.send(brew)
|
||||||
.catch((err)=>{
|
.catch((err)=>{
|
||||||
console.log(err);
|
console.log(err);
|
||||||
this.setState({ isSaving: false, errors: err });
|
this.setState({ isSaving: false, error: err.response });
|
||||||
});
|
});
|
||||||
if(!res) return;
|
if(!res) return;
|
||||||
|
|
||||||
@@ -164,65 +157,8 @@ const NewPage = createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
renderSaveButton : function(){
|
renderSaveButton : function(){
|
||||||
if(this.state.errors){
|
if(this.state.error){
|
||||||
let errMsg = '';
|
return require('../../utils/render-error-nav-item.js')(this, this.state.error);
|
||||||
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 <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
|
||||||
// Oops!
|
|
||||||
// <div className='errorContainer' onClick={this.clearErrors}>
|
|
||||||
// You must be signed in to a Google account
|
|
||||||
// to save this to<br />Google Drive!<br />
|
|
||||||
// <a target='_blank' rel='noopener noreferrer'
|
|
||||||
// href={`https://www.naturalcrit.com/login?redirect=${this.state.url}`}>
|
|
||||||
// <div className='confirm'>
|
|
||||||
// Sign In
|
|
||||||
// </div>
|
|
||||||
// </a>
|
|
||||||
// <div className='deny'>
|
|
||||||
// Not Now
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
// </Nav.item>;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if(this.state.errors.response.req.url.match(/^\/api.*Google.*$/m)){
|
|
||||||
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
|
||||||
Oops!
|
|
||||||
<div className='errorContainer' onClick={this.clearErrors}>
|
|
||||||
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!
|
|
||||||
<a target='_blank' rel='noopener noreferrer'
|
|
||||||
href={`https://www.naturalcrit.com/login?redirect=${this.state.url}`}>
|
|
||||||
<div className='confirm'>
|
|
||||||
Sign In
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<div className='deny'>
|
|
||||||
Not Now
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Nav.item>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
|
||||||
Oops!
|
|
||||||
<div className='errorContainer'>
|
|
||||||
Looks like there was a problem saving. <br />
|
|
||||||
Report the issue <a target='_blank' rel='noopener noreferrer'
|
|
||||||
href={`https://github.com/naturalcrit/homebrewery/issues/new?body=${encodeURIComponent(errMsg)}`}>
|
|
||||||
here
|
|
||||||
</a>.
|
|
||||||
</div>
|
|
||||||
</Nav.item>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.state.isSaving){
|
if(this.state.isSaving){
|
||||||
|
|||||||
@@ -9,74 +9,4 @@
|
|||||||
background-color : @red;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
72
client/homebrew/styles/nav-item-error-container.less
Normal file
72
client/homebrew/styles/nav-item-error-container.less
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
client/homebrew/utils/render-error-nav-item.js
Normal file
71
client/homebrew/utils/render-error-nav-item.js
Normal file
@@ -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 <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
||||||
|
Oops!
|
||||||
|
<div className='errorContainer' onClick={clearError}>
|
||||||
|
{message ?? 'Conflict: please refresh to get latest changes'}
|
||||||
|
</div>
|
||||||
|
</Nav.item>;
|
||||||
|
} else if(status === 412) {
|
||||||
|
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
||||||
|
Oops!
|
||||||
|
<div className='errorContainer' onClick={clearError}>
|
||||||
|
{message ?? 'Your client is out of date. Please save your changes elsewhere and refresh.'}
|
||||||
|
</div>
|
||||||
|
</Nav.item>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(error.req.url.match(/^\/api.*Google.*$/m)){
|
||||||
|
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
||||||
|
Oops!
|
||||||
|
<div className='errorContainer' onClick={clearError}>
|
||||||
|
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!
|
||||||
|
<a target='_blank' rel='noopener noreferrer'
|
||||||
|
href={`https://www.naturalcrit.com/login?redirect=${window.location.href}`}>
|
||||||
|
<div className='confirm'>
|
||||||
|
Sign In
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<div className='deny'>
|
||||||
|
Not Now
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Nav.item>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
||||||
|
Oops!
|
||||||
|
<div className='errorContainer'>
|
||||||
|
Looks like there was a problem saving. <br />
|
||||||
|
Report the issue <a target='_blank' rel='noopener noreferrer' href={`https://github.com/naturalcrit/homebrewery/issues/new?template=save_issue.yml&error-code=${encodeURIComponent(errMsg)}`}>
|
||||||
|
here
|
||||||
|
</a>.
|
||||||
|
</div>
|
||||||
|
</Nav.item>;
|
||||||
|
};
|
||||||
12
client/homebrew/utils/request-middleware.js
Normal file
12
client/homebrew/utils/request-middleware.js
Normal file
@@ -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;
|
||||||
@@ -22,6 +22,9 @@ const template = async function(name, title='', props = {}){
|
|||||||
<body>
|
<body>
|
||||||
<main id="reactRoot">${require(`../build/${name}/ssr.js`)(props)}</main>
|
<main id="reactRoot">${require(`../build/${name}/ssr.js`)(props)}</main>
|
||||||
<script src=${`/${name}/bundle.js`}></script>
|
<script src=${`/${name}/bundle.js`}></script>
|
||||||
|
<script>
|
||||||
|
sessionStorage.setItem('version', '${props.version}');
|
||||||
|
</script>
|
||||||
<script>start_app(${JSON.stringify(props)})</script>
|
<script>start_app(${JSON.stringify(props)})</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -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.post('/api', asyncHandler(api.newBrew));
|
||||||
router.put('/api/:id', asyncHandler(api.getBrew('edit', true)), asyncHandler(api.updateBrew));
|
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));
|
router.put('/api/update/:id', asyncHandler(api.getBrew('edit', true)), asyncHandler(api.updateBrew));
|
||||||
|
|||||||
17
server/middleware.js
Normal file
17
server/middleware.js
Normal file
@@ -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;
|
||||||
Reference in New Issue
Block a user