diff --git a/internal/goscrobble/server.go b/internal/goscrobble/server.go index bc3dfc4e..4ed24332 100644 --- a/internal/goscrobble/server.go +++ b/internal/goscrobble/server.go @@ -113,6 +113,15 @@ func generateJsonMessage(m string) []byte { return js } +// generateJsonError - Generates a err:str response +func generateJsonError(m string) []byte { + jr := jsonResponse{ + Err: m, + } + js, _ := json.Marshal(&jr) + return js +} + // tokenMiddleware - Validates token to a user func tokenMiddleware(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { @@ -136,7 +145,7 @@ func limitMiddleware(next http.HandlerFunc, limiter *IPRateLimiter) http.Handler return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { limiter := limiter.GetLimiter(r.RemoteAddr) if !limiter.Allow() { - msg := generateJsonMessage("Too many requests") + msg := generateJsonError("Too many requests") w.WriteHeader(http.StatusTooManyRequests) w.Write(msg) return @@ -183,7 +192,7 @@ func handleLogin(w http.ResponseWriter, r *http.Request) { ip := getUserIp(r) data, err := loginUser(&logReq, ip) if err != nil { - throwBadReq(w, err.Error()) + throwOkMessage(w, err.Error()) return } diff --git a/web/src/App.js b/web/src/App.js index 8c4ea494..b61f1b54 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -2,12 +2,13 @@ import './App.css'; import Home from './Components/Pages/Home'; import About from './Components/Pages/About'; import Login from './Components/Pages/Login'; +import Register from './Components/Pages/Register'; import Navigation from './Components/Pages/Navigation'; import { Route, Switch, HashRouter } from 'react-router-dom'; import { connect } from "react-redux"; -import { ToastProvider, useToasts } from 'react-toast-notifications'; +import { ToastProvider } from 'react-toast-notifications'; import '../node_modules/bootstrap/dist/css/bootstrap.min.css'; @@ -28,13 +29,14 @@ const App = () => { let exact = true return ( - +
+
diff --git a/web/src/Components/Navigation.css b/web/src/Components/Navigation.css new file mode 100644 index 00000000..8480283c --- /dev/null +++ b/web/src/Components/Navigation.css @@ -0,0 +1,9 @@ +.navLink { + padding: 0 15px 0 15px; + color: #CCCCCC; +} + +.navLink:hover { + color: #666666; + text-decoration: none; +} \ No newline at end of file diff --git a/web/src/Components/Navigation.js b/web/src/Components/Navigation.js new file mode 100644 index 00000000..c4ab9903 --- /dev/null +++ b/web/src/Components/Navigation.js @@ -0,0 +1,61 @@ +import { React, Component } from 'react'; +import { Navbar, NavbarBrand } from 'reactstrap'; +import { Link } from 'react-router-dom'; +import './Navigation.css'; + +const menuItems = [ + 'Home', + 'About', +]; + +class Navigation extends Component { + constructor(props) { + super(props); + // Yeah I know you might not hit home.. but I can't get the + // path based finder thing working on initial load :sweatsmile: + console.log(this.props.initLocation) + this.state = { isLoggedIn: false, active: "Home" }; + } + + toggleLogin() { + this.setState({ isLoggedIn: !this.state.isLoggedIn }) + } + + _handleClick(menuItem) { + this.setState({ active: menuItem }); + } + + render() { + const activeStyle = { color: '#FFFFFF' }; + + const renderAuthButton = () => { + if (this.state.isLoggedIn) { + return Logout; + } else { + return Login; + } + } + + return ( +
+ + GoScrobble + {menuItems.map(menuItem => + + {menuItem} + + )} + {renderAuthButton()} + +
+ ); + } + } + +export default Navigation; \ No newline at end of file diff --git a/web/src/Components/Pages/Login.js b/web/src/Components/Pages/Login.js index 9e30195d..8d12db4d 100644 --- a/web/src/Components/Pages/Login.js +++ b/web/src/Components/Pages/Login.js @@ -3,7 +3,7 @@ import '../../App.css'; import './Login.css'; import { Button } from 'reactstrap'; -import { ToastProvider, useToasts } from 'react-toast-notifications'; +import { useToasts } from 'react-toast-notifications'; // const FormWithToasts = () => { // const { addToast } = useToasts(); @@ -22,28 +22,33 @@ import { ToastProvider, useToasts } from 'react-toast-notifications'; // }; // const { addToast } = useToasts(); +function withToast(Component) { + return function WrappedComponent(props) { + const toastFuncs = useToasts() + return ; + } +} -class About extends React.Component { +class Login extends React.Component { constructor(props) { super(props); - this.state = {username: '', password: ''}; + this.state = {username: '', password: '', loading: false}; this.handleUsernameChange = this.handleUsernameChange.bind(this); this.handlePasswordChange = this.handlePasswordChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } + handleUsernameChange(event) { this.setState({username: event.target.value}); - // addToast(error.message, { appearance: 'error' }); - } handlePasswordChange(event) { this.setState({password: event.target.value}); } - handleSubmit(event) { event.preventDefault(); + this.setState({loading: true}); const requestOptions = { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -55,14 +60,14 @@ class About extends React.Component { const apiUrl = 'http://127.0.0.1:42069/api/v1/login'; fetch(apiUrl, requestOptions) .then((response) => response.json()) - .then((data) => function() { - - - - - - - }); + .then((function(data) { + if (data.error) { + this.props.addToast(data.error, { appearance: 'error' }); + } else { + this.props.addToast(data.token, { appearance: 'success' }); + } + this.setState({loading: false}); + }).bind(this)) } render() { @@ -97,6 +102,7 @@ class About extends React.Component { color="primary" type="submit" className="loginButton" + disabled={this.state.loading} >Login @@ -105,4 +111,4 @@ class About extends React.Component { } } -export default About; +export default withToast(Login); diff --git a/web/src/Components/Pages/Navigation.js b/web/src/Components/Pages/Navigation.js index c4ab9903..f55c8213 100644 --- a/web/src/Components/Pages/Navigation.js +++ b/web/src/Components/Pages/Navigation.js @@ -32,7 +32,7 @@ class Navigation extends Component { if (this.state.isLoggedIn) { return Logout; } else { - return Login; + return
LoginRegister
; } } diff --git a/web/src/Components/Pages/Register.css b/web/src/Components/Pages/Register.css new file mode 100644 index 00000000..223f2c12 --- /dev/null +++ b/web/src/Components/Pages/Register.css @@ -0,0 +1,15 @@ +.loginBody { + padding: 20px 5px 5px 5px; + font-size: 16pt; + width: 300px; +} + +.loginFields { + width: 100%; +} + +.loginButton { + height: 50px; + width: 100%; + margin-top:-5px; +} \ No newline at end of file diff --git a/web/src/Components/Pages/Register.js b/web/src/Components/Pages/Register.js new file mode 100644 index 00000000..7c9fd0f8 --- /dev/null +++ b/web/src/Components/Pages/Register.js @@ -0,0 +1,143 @@ +import React from 'react'; +import '../../App.css'; +import './Login.css'; +import { Button } from 'reactstrap'; + +import { useToasts } from 'react-toast-notifications'; + +function withToast(Component) { + return function WrappedComponent(props) { + const toastFuncs = useToasts() + return ; + } +} + +class Register extends React.Component { + constructor(props) { + super(props); + this.state = {username: '', email: '', password: '', passwordconfirm: '', loading: false}; + this.handleUsernameChange = this.handleUsernameChange.bind(this); + this.handleEmailChange = this.handleEmailChange.bind(this); + this.handlePasswordChange = this.handlePasswordChange.bind(this); + this.handlePasswordConfirmChange = this.handlePasswordConfirmChange.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + } + + handleUsernameChange(event) { + this.setState({username: event.target.value}); + } + + handleEmailChange(event) { + this.setState({email: event.target.value}); + } + + handlePasswordChange(event) { + this.setState({password: event.target.value}); + } + + handlePasswordConfirmChange(event) { + this.setState({passwordconfirm: event.target.value}); + } + + handleSubmit(event) { + event.preventDefault(); + + if (this.state.password !== this.state.passwordconfirm) { + this.props.addToast('Passwords do not match', { appearance: 'error' }); + return + } + + if (this.state.password.len < 8) { + this.props.addToast('Passwords do not match', { appearance: 'error' }); + return + } + + this.setState({loading: true}); + const requestOptions = { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + username: this.state.username, + email: this.state.email, + password: this.state.password, + }) + }; + + const apiUrl = 'http://127.0.0.1:42069/api/v1/register'; + fetch(apiUrl, requestOptions) + .then((response) => response.json()) + .then((function(data) { + if (data.error) { + this.props.addToast(data.error, { appearance: 'error' }); + } else { + this.props.addToast(data.message, { appearance: 'success' }); + } + this.setState({loading: false}); + }).bind(this)); + } + + render() { + return ( +
+

+ Register +

+
+
+ +
+ +
+ +
+ +

+ +
+
+
+ ); + } +} + +export default withToast(Register);