Update readme, add stack+support. Add redis env vars. Add disable registration flag

This commit is contained in:
Daniel Mason 2021-03-28 10:39:08 +13:00
parent dc1a2df968
commit feb2ac37da
Signed by: idanoo
GPG Key ID: 387387CDBC02F132
14 changed files with 367 additions and 100 deletions

View File

@ -3,6 +3,11 @@ MYSQL_USER=
MYSQL_PASS= MYSQL_PASS=
MYSQL_DB= MYSQL_DB=
REDIS_URL=
REDIS_DB=
REDIS_PREFIX="gs:"
REDIS_AUTH=""
JWT_SECRET= JWT_SECRET=
JWT_EXPIRY=86400 JWT_EXPIRY=86400

View File

@ -30,7 +30,6 @@ build-react:
expire_in: 1 day expire_in: 1 day
paths: paths:
- web/build - web/build
- web/public
- .env.example - .env.example
bundle: bundle:
@ -41,8 +40,8 @@ bundle:
before_script: before_script:
- apk add --no-cache zip tar - apk add --no-cache zip tar
script: script:
- zip -r goscrobble.zip web goscrobble migrations init .env.example - zip -r goscrobble.zip web/build goscrobble migrations init .env.example
- tar -czf goscrobble.tar.gz web goscrobble migrations init .env.example - tar -czf goscrobble.tar.gz web/build goscrobble migrations init .env.example
artifacts: artifacts:
expire_in: 6 months expire_in: 6 months
paths: paths:

View File

@ -1,14 +1,14 @@
# go-scrobble # go-scrobble
Golang based music scrobbler. MySQL 8.0+ Golang based music scrobbler.
Currently building on Node V15.X & Go V1.16.X Stack: Go 1.16+, Node 15+, React 17+, MySQL 8.0+, Redis
There are prebuilt binaries/packages available.
With a prebuilt binary - you will still need the migrations folder + web/build folder on prod.
Copy .env.example to .env and set variables. You can use https://www.grc.com/passwords.htm to generate a JWT_SECRET. Copy .env.example to .env and set variables. You can use https://www.grc.com/passwords.htm to generate a JWT_SECRET.
## Setup MySQL ## Setup MySQL
create user 'goscrobble'@'%' identified by 'supersecurepass'; create user 'goscrobble'@'%' identified by 'supersecurepass';
create database goscrobble; create database goscrobble;
@ -32,3 +32,7 @@ We need to build NPM package, and then ship web/build with the binary.
cd web npm install --production && REACT_APP_API_URL=https://goscrobble.com npm run build cd web npm install --production && REACT_APP_API_URL=https://goscrobble.com npm run build
go build -o goscrobble cmd/go-scrobble/*.go go build -o goscrobble cmd/go-scrobble/*.go
./goscrobble ./goscrobble
## Support Development!
Feel free to support hosting and my coffee addiction https://liberapay.com/idanoo

21
docs/config.md Normal file
View File

@ -0,0 +1,21 @@
## FRONTEND VARS
REACT_APP_REGISTRATION_DISABLED=true // Disables registration
REACT_APP_API_URL=https://goscrobble.com // Sets API URL
## BACKEND VARS
MYSQL_HOST= // MySQL Server
MYSQL_USER= // MySQL User
MYSQL_PASS= // MySQL Password
MYSQL_DB= // MySQL Database
REDIS_URL= // Redis host
REDIS_DB=4 // Redis DB
REDIS_PREFIX="gs:" // Redis key prefix
REDIS_AUTH="" // Redis password
JWT_SECRET= // 32+ Char JWT secret
JWT_EXPIRY=86400 // JWT expiry
REVERSE_PROXIES=127.0.0.1 // Comma separated list of servers to ignore for IP logs
PORT=42069 // Server port

140
web/package-lock.json generated
View File

@ -13,8 +13,10 @@
"@testing-library/react": "^11.2.5", "@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^12.8.3", "@testing-library/user-event": "^12.8.3",
"bootstrap": "^4.6.0", "bootstrap": "^4.6.0",
"formik": "^2.2.6",
"react": "^17.0.2", "react": "^17.0.2",
"react-bootstrap": "^1.5.2", "react-bootstrap": "^1.5.2",
"react-cookie": "^4.0.3",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-redux": "^7.2.3", "react-redux": "^7.2.3",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
@ -22,6 +24,7 @@
"react-toast": "^1.0.1", "react-toast": "^1.0.1",
"react-toast-notifications": "^2.4.3", "react-toast-notifications": "^2.4.3",
"reactstrap": "^8.9.0", "reactstrap": "^8.9.0",
"redux-persist": "^6.0.0",
"web-vitals": "^1.1.1" "web-vitals": "^1.1.1"
}, },
"devDependencies": { "devDependencies": {
@ -3095,6 +3098,11 @@
"resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.11.tgz", "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.11.tgz",
"integrity": "sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw==" "integrity": "sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw=="
}, },
"node_modules/@types/cookie": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz",
"integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow=="
},
"node_modules/@types/eslint": { "node_modules/@types/eslint": {
"version": "7.2.7", "version": "7.2.7",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.7.tgz", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.7.tgz",
@ -8961,6 +8969,42 @@
"node": ">= 0.12" "node": ">= 0.12"
} }
}, },
"node_modules/formik": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/formik/-/formik-2.2.6.tgz",
"integrity": "sha512-Kxk2zQRafy56zhLmrzcbryUpMBvT0tal5IvcifK5+4YNGelKsnrODFJ0sZQRMQboblWNym4lAW3bt+tf2vApSA==",
"funding": [
{
"type": "individual",
"url": "https://opencollective.com/formik"
}
],
"dependencies": {
"deepmerge": "^2.1.1",
"hoist-non-react-statics": "^3.3.0",
"lodash": "^4.17.14",
"lodash-es": "^4.17.14",
"react-fast-compare": "^2.0.1",
"tiny-warning": "^1.0.2",
"tslib": "^1.10.0"
},
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/formik/node_modules/deepmerge": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/formik/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/forwarded": { "node_modules/forwarded": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
@ -16241,6 +16285,19 @@
"regenerator-runtime": "^0.13.4" "regenerator-runtime": "^0.13.4"
} }
}, },
"node_modules/react-cookie": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-4.0.3.tgz",
"integrity": "sha512-cmi6IpdVgTSvjqssqIEvo779Gfqc4uPGHRrKMEdHcqkmGtPmxolGfsyKj95bhdLEKqMdbX8MLBCwezlnhkHK0g==",
"dependencies": {
"@types/hoist-non-react-statics": "^3.0.1",
"hoist-non-react-statics": "^3.0.0",
"universal-cookie": "^4.0.0"
},
"peerDependencies": {
"react": ">= 16.3.0"
}
},
"node_modules/react-dev-utils": { "node_modules/react-dev-utils": {
"version": "11.0.4", "version": "11.0.4",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz",
@ -16357,6 +16414,11 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
"integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==" "integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew=="
}, },
"node_modules/react-fast-compare": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
},
"node_modules/react-is": { "node_modules/react-is": {
"version": "17.0.2", "version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
@ -16862,6 +16924,14 @@
"symbol-observable": "^1.2.0" "symbol-observable": "^1.2.0"
} }
}, },
"node_modules/redux-persist": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz",
"integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==",
"peerDependencies": {
"redux": ">4.0.0"
}
},
"node_modules/redux-thunk": { "node_modules/redux-thunk": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
@ -19892,6 +19962,15 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/universal-cookie": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-4.0.4.tgz",
"integrity": "sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw==",
"dependencies": {
"@types/cookie": "^0.3.3",
"cookie": "^0.4.0"
}
},
"node_modules/universalify": { "node_modules/universalify": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
@ -24453,6 +24532,11 @@
"resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.11.tgz", "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.11.tgz",
"integrity": "sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw==" "integrity": "sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw=="
}, },
"@types/cookie": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz",
"integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow=="
},
"@types/eslint": { "@types/eslint": {
"version": "7.2.7", "version": "7.2.7",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.7.tgz", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.7.tgz",
@ -29137,6 +29221,32 @@
"mime-types": "^2.1.12" "mime-types": "^2.1.12"
} }
}, },
"formik": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/formik/-/formik-2.2.6.tgz",
"integrity": "sha512-Kxk2zQRafy56zhLmrzcbryUpMBvT0tal5IvcifK5+4YNGelKsnrODFJ0sZQRMQboblWNym4lAW3bt+tf2vApSA==",
"requires": {
"deepmerge": "^2.1.1",
"hoist-non-react-statics": "^3.3.0",
"lodash": "^4.17.14",
"lodash-es": "^4.17.14",
"react-fast-compare": "^2.0.1",
"tiny-warning": "^1.0.2",
"tslib": "^1.10.0"
},
"dependencies": {
"deepmerge": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA=="
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
}
},
"forwarded": { "forwarded": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
@ -34782,6 +34892,16 @@
} }
} }
}, },
"react-cookie": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-4.0.3.tgz",
"integrity": "sha512-cmi6IpdVgTSvjqssqIEvo779Gfqc4uPGHRrKMEdHcqkmGtPmxolGfsyKj95bhdLEKqMdbX8MLBCwezlnhkHK0g==",
"requires": {
"@types/hoist-non-react-statics": "^3.0.1",
"hoist-non-react-statics": "^3.0.0",
"universal-cookie": "^4.0.0"
}
},
"react-dev-utils": { "react-dev-utils": {
"version": "11.0.4", "version": "11.0.4",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz",
@ -34872,6 +34992,11 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
"integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==" "integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew=="
}, },
"react-fast-compare": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
},
"react-is": { "react-is": {
"version": "17.0.2", "version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
@ -35276,6 +35401,12 @@
"symbol-observable": "^1.2.0" "symbol-observable": "^1.2.0"
} }
}, },
"redux-persist": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz",
"integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==",
"requires": {}
},
"redux-thunk": { "redux-thunk": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
@ -37681,6 +37812,15 @@
"crypto-random-string": "^1.0.0" "crypto-random-string": "^1.0.0"
} }
}, },
"universal-cookie": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-4.0.4.tgz",
"integrity": "sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw==",
"requires": {
"@types/cookie": "^0.3.3",
"cookie": "^0.4.0"
}
},
"universalify": { "universalify": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",

View File

@ -8,8 +8,10 @@
"@testing-library/react": "^11.2.5", "@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^12.8.3", "@testing-library/user-event": "^12.8.3",
"bootstrap": "^4.6.0", "bootstrap": "^4.6.0",
"formik": "^2.2.6",
"react": "^17.0.2", "react": "^17.0.2",
"react-bootstrap": "^1.5.2", "react-bootstrap": "^1.5.2",
"react-cookie": "^4.0.3",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-redux": "^7.2.3", "react-redux": "^7.2.3",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
@ -17,6 +19,7 @@
"react-toast": "^1.0.1", "react-toast": "^1.0.1",
"react-toast-notifications": "^2.4.3", "react-toast-notifications": "^2.4.3",
"reactstrap": "^8.9.0", "reactstrap": "^8.9.0",
"redux-persist": "^6.0.0",
"web-vitals": "^1.1.1" "web-vitals": "^1.1.1"
}, },
"scripts": { "scripts": {

View File

@ -2,14 +2,12 @@ import './App.css';
import Home from './Components/Pages/Home'; import Home from './Components/Pages/Home';
import About from './Components/Pages/About'; import About from './Components/Pages/About';
import Login from './Components/Pages/Login'; import Login from './Components/Pages/Login';
import Settings from './Components/Pages/Settings';
import Register from './Components/Pages/Register'; import Register from './Components/Pages/Register';
import Navigation from './Components/Navigation'; import Navigation from './Components/Navigation';
import { Route, Switch, HashRouter } from 'react-router-dom'; import { Route, Switch } from 'react-router-dom';
import { connect } from "react-redux"; import { connect } from "react-redux";
import { ToastProvider } from 'react-toast-notifications';
import '../node_modules/bootstrap/dist/css/bootstrap.min.css'; import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
function mapStateToProps(state) { function mapStateToProps(state) {
@ -28,17 +26,16 @@ function mapDispatchToProps(dispatch) {
const App = () => { const App = () => {
let exact = true let exact = true
return ( return (
<HashRouter> <div>
<ToastProvider autoDismiss="true" autoDismissTimeout="5000" placement="bottom-right"> <Navigation />
<Navigation /> <Switch>
<Switch> <Route exact={exact} path="/" component={Home} />
<Route exact={exact} path="/" component={Home} /> <Route path="/about" component={About} />
<Route path="/about" component={About} /> <Route path="/settings" component={Settings} />
<Route path="/login" component={Login} /> <Route path="/login" component={Login} />
<Route path="/register" component={Register} /> <Route path="/register" component={Register} />
</Switch> </Switch>
</ToastProvider> </div>
</HashRouter>
); );
} }

View File

@ -0,0 +1 @@
// https://stackoverflow.com/questions/38397653/redux-what-is-the-correct-place-to-save-cookie-after-login-request

View File

@ -0,0 +1,45 @@
import React, { Component, PropTypes } from 'react';
import { Provider } from 'react-redux';
import { persistStore } from 'redux-persist';
class AppProvider extends Component {
static propTypes = {
store: PropTypes.object.isRequired,
children: PropTypes.node
}
constructor(props) {
super(props);
this.state = { rehydrated: false };
}
componentWillMount() {
const opts = {
whitelist: ['user'] // <-- Your auth/user reducer storing the cookie
};
persistStore(this.props.store, opts, () => {
this.setState({ rehydrated: true });
});
}
render() {
if (!this.state.rehydrated) {
return null;
}
return (
<Provider store={this.props.store}>
{this.props.children}
</Provider>
);
}
}
AppProvider.propTypes = {
store: PropTypes.object.isRequired,
children: PropTypes.node
}
export default AppProvider;

View File

@ -2,7 +2,7 @@ import React from 'react';
import '../../App.css'; import '../../App.css';
import './Login.css'; import './Login.css';
import { Button } from 'reactstrap'; import { Button } from 'reactstrap';
import { Formik, Form, Field } from 'formik';
import { useToasts } from 'react-toast-notifications'; import { useToasts } from 'react-toast-notifications';
function withToast(Component) { function withToast(Component) {
@ -29,16 +29,15 @@ class Login extends React.Component {
this.setState({password: event.target.value}); this.setState({password: event.target.value});
} }
handleSubmit(event) { handleSubmit(values) {
event.preventDefault();
this.setState({loading: true}); this.setState({loading: true});
const requestOptions = { const requestOptions = {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
timeout: 5000, timeout: 5000,
body: JSON.stringify({ body: JSON.stringify({
username: this.state.username, username: values.username,
password: this.state.password, password: values.password,
}) })
}; };
const apiUrl = process.env.REACT_APP_API_URL + '/api/v1/login'; const apiUrl = process.env.REACT_APP_API_URL + '/api/v1/login';
@ -47,7 +46,7 @@ class Login extends React.Component {
.then((function(data) { .then((function(data) {
if (data.error) { if (data.error) {
this.props.addToast(data.error, { appearance: 'error' }); this.props.addToast(data.error, { appearance: 'error' });
} else { } else if (data.token) {
this.props.addToast(data.token, { appearance: 'success' }); this.props.addToast(data.token, { appearance: 'success' });
} }
this.setState({loading: false}); this.setState({loading: false});
@ -59,30 +58,35 @@ class Login extends React.Component {
} }
render() { render() {
let trueBool = true;
return ( return (
<div className="pageWrapper"> <div className="pageWrapper">
<h1> <h1>
Login Login
</h1> </h1>
<div className="loginBody"> <div className="loginBody">
<form onSubmit={this.handleSubmit}> <Formik
initialValues={{ username: '', password: '' }}
onSubmit={async values => this.handleSubmit(values)}
>
<Form>
<label> <label>
Email / Username<br/> Email / Username<br/>
<input <Field
name="username"
type="text" type="text"
required={trueBool}
className="loginFields" className="loginFields"
value={this.state.username}
onChange={this.handleUsernameChange}
/> />
</label> </label>
<br/> <br/>
<label> <label>
Password<br/> Password<br/>
<input <Field
name="password"
type="password" type="password"
required={trueBool}
className="loginFields" className="loginFields"
value={this.state.password}
onChange={this.handlePasswordChange}
/> />
</label> </label>
<br/><br/> <br/><br/>
@ -92,7 +96,8 @@ class Login extends React.Component {
className="loginButton" className="loginButton"
disabled={this.state.loading} disabled={this.state.loading}
>Login</Button> >Login</Button>
</form> </Form>
</Formik>
</div> </div>
</div> </div>
); );

View File

@ -2,7 +2,6 @@ import React from 'react';
import '../../App.css'; import '../../App.css';
import './Login.css'; import './Login.css';
import { Button } from 'reactstrap'; import { Button } from 'reactstrap';
import { useToasts } from 'react-toast-notifications'; import { useToasts } from 'react-toast-notifications';
function withToast(Component) { function withToast(Component) {
@ -84,64 +83,73 @@ class Register extends React.Component {
} }
render() { render() {
let trueBool = true;
return ( return (
<div className="pageWrapper"> <div className="pageWrapper">
<h1> {
Register // TODO: Move to DB:config REGISTRATION_DISABLED=1|0
</h1> process.env.REACT_APP_REGISTRATION_DISABLED === "true" ?
<div className="loginBody"> <p>Registration is temporarily disabled. Please try again soon!</p>
<form onSubmit={this.handleSubmit}> :
<label> <div>
Username*<br/> <h1>
<input Register
type="text" </h1>
required="true" <div className="loginBody">
className="loginFields" <form onSubmit={this.handleSubmit}>
value={this.state.username} <label>
onChange={this.handleUsernameChange} Username*<br/>
/> <input
</label> type="text"
<br/> required={trueBool}
<label> className="loginFields"
Email<br/> value={this.state.username}
<input onChange={this.handleUsernameChange}
type="email" />
className="loginFields" </label>
value={this.state.email} <br/>
onChange={this.handleEmailChange} <label>
/> Email<br/>
</label> <input
<br/> type="email"
<label> className="loginFields"
Password<br/> value={this.state.email}
<input onChange={this.handleEmailChange}
type="password" />
required="true" </label>
className="loginFields" <br/>
value={this.state.password} <label>
onChange={this.handlePasswordChange} Password<br/>
/> <input
</label> type="password"
<br/> required={trueBool}
<label> className="loginFields"
Password<br/> value={this.state.password}
<input onChange={this.handlePasswordChange}
type="password" />
required="true" </label>
className="loginFields" <br/>
value={this.state.passwordconfirm} <label>
onChange={this.handlePasswordConfirmChange} Password<br/>
/> <input
</label> type="password"
<br/><br/> required={trueBool}
<Button className="loginFields"
color="primary" value={this.state.passwordconfirm}
type="submit" onChange={this.handlePasswordConfirmChange}
className="loginButton" />
disabled={this.state.loading} </label>
>Login</Button> <br/><br/>
</form> <Button
</div> color="primary"
type="submit"
className="loginButton"
disabled={this.state.loading}
>Login</Button>
</form>
</div>
</div>
}
</div> </div>
); );
} }

View File

View File

@ -0,0 +1,36 @@
import React from 'react';
import '../../App.css';
import './Settings.css';
import { useToasts } from 'react-toast-notifications';
function withToast(Component) {
return function WrappedComponent(props) {
const toastFuncs = useToasts()
return <Component {...props} {...toastFuncs} />;
}
}
class Settings extends React.Component {
constructor(props) {
super(props);
this.state = {username: '', password: '', loading: false};
}
render() {
return (
<div className="pageWrapper">
<h1>
Settings
</h1>
<div className="loginBody">
<p>
All the settings
</p>
</div>
</div>
);
}
}
export default withToast(Settings);

View File

@ -2,7 +2,8 @@ import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import './index.css'; import './index.css';
import App from './App'; import App from './App';
import { BrowserRouter } from 'react-router-dom' import { HashRouter } from 'react-router-dom'
import { ToastProvider } from 'react-toast-notifications';
import { Provider } from 'react-redux' import { Provider } from 'react-redux'
import { createStore } from 'redux' import { createStore } from 'redux'
@ -14,10 +15,12 @@ const goScorbbleStore = (state = false, logIn) => {
const store = createStore(goScorbbleStore); const store = createStore(goScorbbleStore);
ReactDOM.render( ReactDOM.render(
<Provider store={store}> <HashRouter>
<BrowserRouter> <ToastProvider autoDismiss="true" autoDismissTimeout="5000" placement="bottom-right">
<App /> <Provider store={store}>
</BrowserRouter> <App />
</Provider>, </Provider>
</ToastProvider>
</HashRouter>,
document.getElementById('root') document.getElementById('root')
); );