From 2f8aa2e502f5ed76742ef615dbd6b222a0d8b643 Mon Sep 17 00:00:00 2001 From: Daniel Mason Date: Tue, 30 Mar 2021 21:36:28 +1300 Subject: [PATCH] 0.0.5 - Only allow ItemType:Audio from Jellyfin - Fix NavBar for Mobile (Ugly hack but.. TO REWORK) - Fixed registration page issues - Add functionality to pull recent scrobbles to Dashboard - Add MX record lookup validation for emails - Add username validation for a-Z 0-9 _ and . - Dashboard shows basic table of last 500 scrobbles. --- .gitlab-ci.yml | 2 +- docs/changelog.md | 9 ++ docs/removing_bad_data.md | 13 ++ internal/goscrobble/jellyfin.go | 17 +++ internal/goscrobble/server.go | 14 +- internal/goscrobble/user.go | 11 +- internal/goscrobble/utils.go | 27 +++- web/package-lock.json | 40 ++++++ web/package.json | 2 + web/src/Actions/api.js | 8 ++ web/src/Actions/message.js | 10 -- web/src/Actions/types.js | 3 - web/src/App.css | 2 +- web/src/App.js | 34 ++--- web/src/Components/HomeBanner.css | 1 + web/src/Components/HomeBanner.js | 8 +- web/src/Components/Navigation.css | 16 ++- web/src/Components/Navigation.js | 205 +++++++++++++++++++--------- web/src/Components/ScrobbleTable.js | 43 ++++++ web/src/Pages/About.css | 2 +- web/src/Pages/About.js | 10 +- web/src/Pages/Dashboard.js | 34 ++++- web/src/Pages/Home.css | 0 web/src/Pages/Home.js | 15 +- web/src/Pages/Register.css | 6 +- web/src/Pages/Register.js | 22 +-- web/src/Reducers/index.js | 2 - web/src/Reducers/message.js | 18 --- web/src/Services/api.service.js | 5 + web/src/Services/auth-header.js | 6 +- web/src/Services/auth.service.js | 11 +- 31 files changed, 425 insertions(+), 171 deletions(-) create mode 100644 docs/removing_bad_data.md delete mode 100644 web/src/Actions/message.js create mode 100644 web/src/Components/ScrobbleTable.js create mode 100644 web/src/Pages/Home.css delete mode 100644 web/src/Reducers/message.js diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f9a0f52d..fb587f89 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,7 +3,7 @@ stages: - bundle variables: - VERSION: 0.0.4 + VERSION: 0.0.5 build-go: image: golang:1.16.2 diff --git a/docs/changelog.md b/docs/changelog.md index 7a5cee20..835b0988 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,3 +1,12 @@ +# 0.0.5 +- Only allow ItemType:Audio from Jellyfin +- Fix NavBar for Mobile (Ugly hack but.. TO REWORK) +- Fixed registration page issues +- Add functionality to pull recent scrobbles to Dashboard +- Add MX record lookup validation for emails +- Add username validation for a-Z 0-9 _ and . +- Dashboard shows basic table of last 500 scrobbles. + # 0.0.4 - Display stats on homepage diff --git a/docs/removing_bad_data.md b/docs/removing_bad_data.md new file mode 100644 index 00000000..b8a86409 --- /dev/null +++ b/docs/removing_bad_data.md @@ -0,0 +1,13 @@ +## Removing bad data + +This is by no means recommended.. But during testing I somehow scrobbled movies. + + SET FOREIGN_KEY_CHECKS=0; + DELETE FROM artists WHERE `name` = "%!s()"; + DELETE FROM albums WHERE `name` = "%!s()"; + DELETE album_artist FROM album_artist LEFT JOIN artists ON artists.uuid = album_artist.artist WHERE artists.uuid is null; + DELETE album_artist FROM album_artist LEFT JOIN albums ON albums.uuid = album_artist.album WHERE albums.uuid is null; + DELETE track_artist FROM track_artist LEFT JOIN artists ON artists.uuid = track_artist.artist WHERE artists.uuid is null; + DELETE tracks FROM tracks LEFT JOIN track_artist ON track_artist.track = tracks.uuid WHERE track_artist.track IS NULL; + DELETE scrobbles FROM scrobbles LEFT JOIN tracks ON tracks.uuid = scrobbles.track WHERE tracks.uuid is null; + SET FOREIGN_KEY_CHECKS=1; diff --git a/internal/goscrobble/jellyfin.go b/internal/goscrobble/jellyfin.go index c480037a..0c916c1e 100644 --- a/internal/goscrobble/jellyfin.go +++ b/internal/goscrobble/jellyfin.go @@ -10,6 +10,23 @@ import ( // ParseJellyfinInput - Transform API data into a common struct func ParseJellyfinInput(userUUID string, data map[string]interface{}, ip net.IP, tx *sql.Tx) error { + if data["ItemType"] != "Audio" { + return errors.New("Media type not audio") + } + + // Safety Checks + if data["Artist"] == nil { + return errors.New("Missing artist data") + } + + if data["Album"] == nil { + return errors.New("Missing album data") + } + + if data["Name"] == nil { + return errors.New("Missing track data") + } + // Insert artist if not exist artist, err := insertArtist(fmt.Sprintf("%s", data["Artist"]), fmt.Sprintf("%s", data["Provider_musicbrainzartist"]), tx) if err != nil { diff --git a/internal/goscrobble/server.go b/internal/goscrobble/server.go index 60d7ab4f..c08efb99 100644 --- a/internal/goscrobble/server.go +++ b/internal/goscrobble/server.go @@ -165,14 +165,18 @@ func jwtMiddleware(next func(http.ResponseWriter, *http.Request, string, string) return } - var v string + var reqUuid string for k, v := range mux.Vars(r) { if k == "id" { - log.Printf("key=%v, value=%v", k, v) + reqUuid = v } } - next(w, r, claims.Subject, v) + if reqUuid == "" { + throwBadReq(w, "Invalid Request") + } + + next(w, r, claims.Subject, reqUuid) } } @@ -206,7 +210,7 @@ func handleRegister(w http.ResponseWriter, r *http.Request) { ip := getUserIp(r) err = createUser(®Req, ip) if err != nil { - throwOkMessage(w, err.Error()) + throwOkError(w, err.Error()) return } @@ -265,7 +269,7 @@ func handleIngress(w http.ResponseWriter, r *http.Request, userUuid string) { ip := getUserIp(r) err := ParseJellyfinInput(userUuid, bodyJson, ip, tx) if err != nil { - log.Printf("Error inserting track: %+v", err) + // log.Printf("Error inserting track: %+v", err) tx.Rollback() throwBadReq(w, err.Error()) return diff --git a/internal/goscrobble/user.go b/internal/goscrobble/user.go index c06a28cf..2b3fff66 100644 --- a/internal/goscrobble/user.go +++ b/internal/goscrobble/user.go @@ -59,15 +59,12 @@ func createUser(req *RegisterRequest, ip net.IP) error { return errors.New("A username is required") } - // Check max length for Username - if len(req.Username) > 64 { - return errors.New("Username cannot be longer than 64 characters") - } - - // Check username doesn't contain @ - if strings.Contains(req.Username, "@") { + // Check username is valid + if !isUsernameValid(req.Username) { + log.Println("user is invalid") return errors.New("Username contains invalid characters") } + log.Println("user is valid") // If set an email.. validate it! if req.Email != "" { diff --git a/internal/goscrobble/utils.go b/internal/goscrobble/utils.go index c07554d0..14b6651d 100644 --- a/internal/goscrobble/utils.go +++ b/internal/goscrobble/utils.go @@ -9,9 +9,11 @@ import ( "net" "net/http" "regexp" + "strings" ) var emailRegex = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$") +var usernameRegex = regexp.MustCompile("^[a-zA-Z0-9_\\.]+$") // decodeJson - Returns a map[string]interface{} func decodeJson(body io.ReadCloser) (map[string]interface{}, error) { @@ -24,10 +26,31 @@ func decodeJson(body io.ReadCloser) (map[string]interface{}, error) { // isEmailValid - checks if the email provided passes the required structure and length. func isEmailValid(e string) bool { - if len(e) < 3 && len(e) > 254 { + if len(e) < 5 && len(e) > 254 { return false } - return emailRegex.MatchString(e) + + if !emailRegex.MatchString(e) { + return false + } + + // Do MX lookup + parts := strings.Split(e, "@") + mx, err := net.LookupMX(parts[1]) + if err != nil || len(mx) == 0 { + return false + } + + return true +} + +// isUsernameValid - Checks if username is alphanumeric+underscores+dots +func isUsernameValid(e string) bool { + if len(e) > 64 { + return false + } + + return usernameRegex.MatchString(e) } // contains - Check if string is in list diff --git a/web/package-lock.json b/web/package-lock.json index a531c8b1..a86fda08 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -19,11 +19,13 @@ "react": "^17.0.2", "react-bootstrap": "^1.5.2", "react-cookie": "^4.0.3", + "react-data-grid": "*", "react-dom": "^17.0.2", "react-redux": "^7.2.3", "react-router-dom": "^5.2.0", "react-scripts": "4.0.3", "react-spinners": "^0.10.6", + "react-table": "^7.6.3", "react-toast": "^1.0.1", "react-toast-notifications": "^2.4.3", "react-toastify": "^7.0.3", @@ -16326,6 +16328,18 @@ "react": ">= 16.3.0" } }, + "node_modules/react-data-grid": { + "version": "7.0.0-canary.38", + "resolved": "https://registry.npmjs.org/react-data-grid/-/react-data-grid-7.0.0-canary.38.tgz", + "integrity": "sha512-JjMyChuh9KxOtYmpxrOuPBI6EYIbNLn/+pjwoQYeD7d5vkWMURWWhyLX1NJkT5bt5LF2qxOSQiFf3G6YndxlAg==", + "dependencies": { + "clsx": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.14 || ^17.0", + "react-dom": "^16.14 || ^17.0" + } + }, "node_modules/react-dev-utils": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz", @@ -16719,6 +16733,18 @@ "react-dom": "^16.0.0 || ^17.0.0" } }, + "node_modules/react-table": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.6.3.tgz", + "integrity": "sha512-hfPF13zDLxPMpLKzIKCE8RZud9T/XrRTsaCIf8zXpWZIZ2juCl7qrGpo3AQw9eAetXV5DP7s2GDm+hht7qq5Dw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.3 || ^17.0.0-0" + } + }, "node_modules/react-toast": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/react-toast/-/react-toast-1.0.1.tgz", @@ -34981,6 +35007,14 @@ "universal-cookie": "^4.0.0" } }, + "react-data-grid": { + "version": "7.0.0-canary.38", + "resolved": "https://registry.npmjs.org/react-data-grid/-/react-data-grid-7.0.0-canary.38.tgz", + "integrity": "sha512-JjMyChuh9KxOtYmpxrOuPBI6EYIbNLn/+pjwoQYeD7d5vkWMURWWhyLX1NJkT5bt5LF2qxOSQiFf3G6YndxlAg==", + "requires": { + "clsx": "^1.1.1" + } + }, "react-dev-utils": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz", @@ -35297,6 +35331,12 @@ "@emotion/core": "^10.0.35" } }, + "react-table": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.6.3.tgz", + "integrity": "sha512-hfPF13zDLxPMpLKzIKCE8RZud9T/XrRTsaCIf8zXpWZIZ2juCl7qrGpo3AQw9eAetXV5DP7s2GDm+hht7qq5Dw==", + "requires": {} + }, "react-toast": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/react-toast/-/react-toast-1.0.1.tgz", diff --git a/web/package.json b/web/package.json index 4ee6a3ed..745e2036 100644 --- a/web/package.json +++ b/web/package.json @@ -14,11 +14,13 @@ "react": "^17.0.2", "react-bootstrap": "^1.5.2", "react-cookie": "^4.0.3", + "react-data-grid": "*", "react-dom": "^17.0.2", "react-redux": "^7.2.3", "react-router-dom": "^5.2.0", "react-scripts": "4.0.3", "react-spinners": "^0.10.6", + "react-table": "^7.6.3", "react-toast": "^1.0.1", "react-toast-notifications": "^2.4.3", "react-toastify": "^7.0.3", diff --git a/web/src/Actions/api.js b/web/src/Actions/api.js index b2deb75d..57e591d2 100644 --- a/web/src/Actions/api.js +++ b/web/src/Actions/api.js @@ -8,3 +8,11 @@ export const getStats = () => { ); }; +export const getRecentScrobbles = (id) => { + return ApiService.getRecentScrobbles(id).then( + (data) => { + return data.data; + } + ); +}; + diff --git a/web/src/Actions/message.js b/web/src/Actions/message.js deleted file mode 100644 index c14afb2a..00000000 --- a/web/src/Actions/message.js +++ /dev/null @@ -1,10 +0,0 @@ -import { SET_MESSAGE, CLEAR_MESSAGE } from "./types"; - -export const setMessage = (message) => ({ - type: SET_MESSAGE, - payload: message, -}); - -export const clearMessage = () => ({ - type: CLEAR_MESSAGE, -}); diff --git a/web/src/Actions/types.js b/web/src/Actions/types.js index 4a7dd4cf..98fda989 100644 --- a/web/src/Actions/types.js +++ b/web/src/Actions/types.js @@ -3,6 +3,3 @@ export const REGISTER_FAIL = "REGISTER_FAIL"; export const LOGIN_SUCCESS = "LOGIN_SUCCESS"; export const LOGIN_FAIL = "LOGIN_FAIL"; export const LOGOUT = "LOGOUT"; - -export const SET_MESSAGE = "SET_MESSAGE"; -export const CLEAR_MESSAGE = "CLEAR_MESSAGE"; diff --git a/web/src/App.css b/web/src/App.css index c3ee31fb..44106f36 100644 --- a/web/src/App.css +++ b/web/src/App.css @@ -31,7 +31,7 @@ .pageWrapper { background-color: #282c34; - padding-top: 100px; + padding: 100px 15px 0 15px; min-height: 100vh; display: flex; flex-direction: column; diff --git a/web/src/App.js b/web/src/App.js index 23237477..657fc525 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -10,7 +10,7 @@ import Settings from './Pages/Settings'; import Register from './Pages/Register'; import Navigation from './Components/Navigation'; -import { logout } from './Actions/auth'; +// import { logout } from './Actions/auth'; import { Route, Switch, withRouter } from 'react-router-dom'; import { connect } from 'react-redux'; import { Component } from 'react'; @@ -26,34 +26,33 @@ function mapStateToProps(state) { class App extends Component { constructor(props) { super(props); - this.logOut = this.logOut.bind(this); + // this.logOut = this.logOut.bind(this); this.state = { // showAdminBoard: false, - currentUser: undefined, + // currentUser: undefined, // Don't even ask.. apparently you can't pass // exact="true".. it has to be a bool :| true: true, }; } + - componentDidMount() { - const user = this.props.user; + // componentDidMount() { + // const user = this.props.user; - if (user) { - this.setState({ - currentUser: user, - // showAdminBoard: user.roles.includes("ROLE_ADMIN"), - }); - } - } + // if (user) { + // this.setState({ + // currentUser: user, + // }); + // } + // } - logOut() { - this.props.dispatch(logout()); - } + // logOut() { + // this.props.dispatch(logout()); + // } render() { - // const { currentUser, showAdminBoard } = this.state; return (
@@ -63,9 +62,10 @@ class App extends Component { + + - diff --git a/web/src/Components/HomeBanner.css b/web/src/Components/HomeBanner.css index cbed0d78..277251ef 100644 --- a/web/src/Components/HomeBanner.css +++ b/web/src/Components/HomeBanner.css @@ -1,6 +1,7 @@ .homeBanner { margin-top: 30px; width: 100%; + max-width: 1100px; } .homeBannerItem { diff --git a/web/src/Components/HomeBanner.js b/web/src/Components/HomeBanner.js index 00fa4886..c4a4cdf4 100644 --- a/web/src/Components/HomeBanner.js +++ b/web/src/Components/HomeBanner.js @@ -39,22 +39,22 @@ class HomeBanner extends React.Component {
{this.state.isLoading - ? + ? : {this.state.scrobbleCount}}
Scrobbles
{this.state.isLoading - ? + ? : {this.state.userCount}}
Users
{this.state.isLoading - ? + ? : {this.state.trackCount}}
Tracks
{this.state.isLoading - ? + ? : {this.state.artistCount}}
Artists
diff --git a/web/src/Components/Navigation.css b/web/src/Components/Navigation.css index c784b163..6e09c4dc 100644 --- a/web/src/Components/Navigation.css +++ b/web/src/Components/Navigation.css @@ -3,19 +3,33 @@ color: #CCCCCC; } +.navLinkMobile { + color: #CCCCCC; +} + .navLink:hover { color: #666666; text-decoration: none; } +.navLinkMobile:hover { + color: #666666; + text-decoration: none; +} + .navLinkLogin { margin-left: 15px; padding-left: 15px; border-left: 1px solid #282c34; } +.navLinkLoginMobile { + margin: 0 auto 0 auto; + padding: 10px; + text-align: center; +} + .nav-logo { height: 50px; margin: -15px 5px -15px -5px; - } \ No newline at end of file diff --git a/web/src/Components/Navigation.js b/web/src/Components/Navigation.js index 8a3e6dce..56c65e3b 100644 --- a/web/src/Components/Navigation.js +++ b/web/src/Components/Navigation.js @@ -1,5 +1,5 @@ import { React, Component } from 'react'; -import { Navbar, NavbarBrand } from 'reactstrap'; +import { Navbar, NavbarBrand, Collapse, Nav, NavbarToggler, NavItem } from 'reactstrap'; import { Link } from 'react-router-dom'; import logo from '../logo.png'; import './Navigation.css'; @@ -21,12 +21,19 @@ const loggedInMenuItems = [ 'Dashboard', 'About', ] + +const isMobile = () => { + return (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) +}; + class Navigation extends Component { constructor(props) { super(props); + this.toggleNavbar = this.toggleNavbar.bind(this); + // Yeah I know you might not hit home.. but I can't get the // path based finder thing working on initial load :sweatsmile: - this.state = { active: "Home" }; + this.state = { active: "Home", collapsed: true}; } componentDidMount() { @@ -51,84 +58,150 @@ class Navigation extends Component { eventBus.remove(LOGOUT); } - _handleClick(menuItem) { - this.setState({ active: menuItem }); + this.setState({ active: menuItem, collapsed: !this.state.collapsed }); } + toggleNavbar() { + this.setState({ collapsed: !this.state.collapsed }); + } + // This is a real mess. TO CLEAN UP. render() { const activeStyle = { color: '#FFFFFF' }; - const renderAuthButtons = () => { - if (this.state.isLoggedIn) { - return
+ const renderMobileNav = () => { + return + logo GoScrobble + + + {this.state.isLoggedIn ? +
; - } else { - return
- Login - Register -
; - } + key={menuItem} + className="navLinkMobile" + style={this.state.active === menuItem ? activeStyle : {}} + onClick={this._handleClick.bind(this, menuItem)} + to={menuItem} + >{menuItem} + + )} + Profile + Logout + + : + } + + } - const renderMenuButtons = () => { - if (this.state.isLoggedIn) { - return
- {loggedInMenuItems.map(menuItem => - - {menuItem} - - )} -
; - } else { - return
- {menuItems.map(menuItem => - - {menuItem} - - )} -
; - } + const renderDesktopNav = () => { + return + logo GoScrobble + {this.state.isLoggedIn ? +
+ {loggedInMenuItems.map(menuItem => + + {menuItem} + + )} +
+ :
+ {menuItems.map(menuItem => + + {menuItem} + + )} +
+ } + {this.state.isLoggedIn ? +
+ Profile + Logout +
+ : +
+ Login + Register +
+ + } +
} return (
- - logo GoScrobble - {renderMenuButtons()} - {renderAuthButtons()} - + { + isMobile() + ? renderMobileNav() + : renderDesktopNav() + }
); } diff --git a/web/src/Components/ScrobbleTable.js b/web/src/Components/ScrobbleTable.js new file mode 100644 index 00000000..c15e6e93 --- /dev/null +++ b/web/src/Components/ScrobbleTable.js @@ -0,0 +1,43 @@ +import React from "react"; + +class ScrobbleTable extends React.Component { + constructor(props) { + super(props); + + this.state = { + data: this.props.data, + }; + } + + render() { + return ( +
+ + + + + + + + + + + { + this.state.data && this.state.data.items && + this.state.data.items.map(function (element) { + return + + + + + ; + }) + } + +
TimestampTrackArtistAlbum
{element.time}{element.track}{element.artist}{element.album}
+
+ ); + } +} + +export default ScrobbleTable; \ No newline at end of file diff --git a/web/src/Pages/About.css b/web/src/Pages/About.css index 5f995ce1..7c0a2a6e 100644 --- a/web/src/Pages/About.css +++ b/web/src/Pages/About.css @@ -1,4 +1,4 @@ .aboutBody { - padding: 20px 5px 5px 5px; + padding: 20px 0px 0px 0px; font-size: 16pt; } \ No newline at end of file diff --git a/web/src/Pages/About.js b/web/src/Pages/About.js index e789ccfc..9e800c3a 100644 --- a/web/src/Pages/About.js +++ b/web/src/Pages/About.js @@ -8,8 +8,16 @@ function About() { About GoScrobble.com

- Go-Scrobble is an open source music scorbbling service written in Go and React. + Go-Scrobble is an open source music scorbbling service written in Go and React.
+ Used to track your listening history and build a profile to discover new music.

+ gitlab.com/idanoo/go-scrobble +
); } diff --git a/web/src/Pages/Dashboard.js b/web/src/Pages/Dashboard.js index b56fa434..5491ed4f 100644 --- a/web/src/Pages/Dashboard.js +++ b/web/src/Pages/Dashboard.js @@ -2,15 +2,40 @@ import React from 'react'; import '../App.css'; import './Dashboard.css'; import { connect } from 'react-redux'; +import { getRecentScrobbles } from '../Actions/api'; +import ScaleLoader from 'react-spinners/ScaleLoader'; +import ScrobbleTable from "../Components/ScrobbleTable"; class Dashboard extends React.Component { + constructor(props) { + super(props); + this.state = { + isLoading: true, + scrobbleData: [], + uuid: null, + }; + } + componentDidMount() { - const { history } = this.props; + const { history, uuid } = this.props; const isLoggedIn = this.props.isLoggedIn; if (!isLoggedIn) { history.push("/login") } + + getRecentScrobbles(uuid) + .then((data) => { + this.setState({ + isLoading: false, + data: data + }); + }) + .catch(() => { + this.setState({ + isLoading: false + }); + }); } render() { @@ -19,6 +44,10 @@ class Dashboard extends React.Component {

Dashboard!

+ {this.state.isLoading + ? + : + } ); } @@ -26,8 +55,11 @@ class Dashboard extends React.Component { function mapStateToProps(state) { const { isLoggedIn } = state.auth; + const { uuid } = state.auth.user; + return { isLoggedIn, + uuid, }; } diff --git a/web/src/Pages/Home.css b/web/src/Pages/Home.css new file mode 100644 index 00000000..e69de29b diff --git a/web/src/Pages/Home.js b/web/src/Pages/Home.js index 1e1f3573..8bf4b070 100644 --- a/web/src/Pages/Home.js +++ b/web/src/Pages/Home.js @@ -1,24 +1,15 @@ import logo from '../logo.png'; import '../App.css'; +import './Home.css'; import HomeBanner from '../Components/HomeBanner'; import React from 'react'; class Home extends React.Component { render() { return ( -
+
logo -

- goscrobble.com -

- - gitlab.com/idanoo/go-scrobble - +

Go-Scrobble is an open source music scrobbling service written in Go and React.

); diff --git a/web/src/Pages/Register.css b/web/src/Pages/Register.css index 223f2c12..b4aedf6c 100644 --- a/web/src/Pages/Register.css +++ b/web/src/Pages/Register.css @@ -1,14 +1,14 @@ -.loginBody { +.registerBody { padding: 20px 5px 5px 5px; font-size: 16pt; width: 300px; } -.loginFields { +.registerFields { width: 100%; } -.loginButton { +.registerButton { height: 50px; width: 100%; margin-top:-5px; diff --git a/web/src/Pages/Register.js b/web/src/Pages/Register.js index 026a1311..2c1ed49c 100644 --- a/web/src/Pages/Register.js +++ b/web/src/Pages/Register.js @@ -1,10 +1,10 @@ import React from 'react'; import '../App.css'; -import './Login.css'; -import { Button, Form } from 'reactstrap'; +import './Register.css'; +import { Button } from 'reactstrap'; import ScaleLoader from "react-spinners/ScaleLoader"; import { register } from '../Actions/auth'; -import { Formik, Field } from 'formik'; +import { Formik, Field, Form } from 'formik'; import { connect } from 'react-redux'; class Register extends React.Component { @@ -22,6 +22,7 @@ class Register extends React.Component { } handleRegister(values) { + console.log(values) this.setState({loading: true}); const { dispatch, history } = this.props; @@ -53,10 +54,11 @@ class Register extends React.Component {

Register

-
+
this.handleRegister(values)}> + onSubmit={async values => this.handleRegister(values)} + >

@@ -73,7 +75,7 @@ class Register extends React.Component {
@@ -83,7 +85,7 @@ class Register extends React.Component { name="password" type="password" required={trueBool} - className="loginFields" + className="registerFields" />
@@ -93,14 +95,14 @@ class Register extends React.Component { name="passwordconfirm" type="password" required={trueBool} - className="loginFields" + className="registerFields" />

diff --git a/web/src/Reducers/index.js b/web/src/Reducers/index.js index 8bb57879..3df08848 100644 --- a/web/src/Reducers/index.js +++ b/web/src/Reducers/index.js @@ -1,8 +1,6 @@ import { combineReducers } from "redux"; import auth from "./auth"; -import message from "./message"; export default combineReducers({ auth, - message, }); diff --git a/web/src/Reducers/message.js b/web/src/Reducers/message.js deleted file mode 100644 index edc6ed5d..00000000 --- a/web/src/Reducers/message.js +++ /dev/null @@ -1,18 +0,0 @@ -import { SET_MESSAGE, CLEAR_MESSAGE } from "../Actions/types"; - -const initialState = {}; - -export default function message(state = initialState, action) { - const { type, payload } = action; - - switch (type) { - case SET_MESSAGE: - return { message: payload }; - - case CLEAR_MESSAGE: - return { message: "" }; - - default: - return state; - } -} diff --git a/web/src/Services/api.service.js b/web/src/Services/api.service.js index 65dd6931..0a202c5a 100644 --- a/web/src/Services/api.service.js +++ b/web/src/Services/api.service.js @@ -1,9 +1,14 @@ import axios from "axios"; +import authHeader from '../Services/auth-header'; class ApiService { async getStats() { return axios.get(process.env.REACT_APP_API_URL + "stats"); } + + async getRecentScrobbles(id) { + return axios.get(process.env.REACT_APP_API_URL + "user/" + id + "/scrobbles", { headers: authHeader() }); + } } export default new ApiService(); \ No newline at end of file diff --git a/web/src/Services/auth-header.js b/web/src/Services/auth-header.js index e2103afc..75454e14 100644 --- a/web/src/Services/auth-header.js +++ b/web/src/Services/auth-header.js @@ -1,8 +1,8 @@ export default function authHeader() { - const auth = localStorage.getItem('user'); + const user = JSON.parse(localStorage.getItem('user')); - if (auth && auth.jwt) { - return { Authorization: 'Bearer ' + auth.jwt }; + if (user && user.jwt) { + return { Authorization: 'Bearer ' + user.jwt }; } else { return {}; } diff --git a/web/src/Services/auth.service.js b/web/src/Services/auth.service.js index c0b1f3f7..045764c5 100644 --- a/web/src/Services/auth.service.js +++ b/web/src/Services/auth.service.js @@ -2,7 +2,7 @@ import axios from "axios"; import jwt from 'jwt-decode' // import dependency class AuthService { - login(username, password) { + async login(username, password) { return axios .post(process.env.REACT_APP_API_URL + "login", { username, password }) .then((response) => { @@ -24,11 +24,16 @@ class AuthService { localStorage.removeItem("user"); } - register(username, email, password) { - return axios.post(process.env.REACT_APP_API_URL + "register", { + async register(username, email, password) { + return axios + .post(process.env.REACT_APP_API_URL + "register", { username, email, password, + }) + .then((response) => { + console.log(response) + return response.data; }); } }