This commit is contained in:
Daniel Mason 2021-08-13 22:38:03 +12:00
parent 9074149925
commit 525d5c92b5
Signed by: idanoo
GPG Key ID: 387387CDBC02F132
14 changed files with 157 additions and 27 deletions

View File

@ -3,7 +3,7 @@ stages:
- bundle
variables:
VERSION: 0.0.32
VERSION: 0.0.33
build-go:
image: golang:1.16.7

View File

@ -1,3 +1,7 @@
# 0.0.33
- Add mod permission
- Add track/edit page
# 0.0.32
- Add related records into track API
- Build out track page to show links to related records

View File

@ -34,6 +34,7 @@ func generateJWTToken(user User, existingRefresh string) (string, error) {
atClaims["username"] = user.Username
atClaims["email"] = user.Email
atClaims["admin"] = user.Admin
atClaims["mod"] = user.Mod
atClaims["iat"] = time.Now().Unix()
atClaims["exp"] = time.Now().Add(JwtExpiry).Unix()
atClaims["refresh_token"] = refreshToken
@ -80,7 +81,7 @@ func verifyJWTToken(token string) (CustomClaims, error) {
return claims, err
}
func getClaims(token *jwt.Token) CustomClaims {
claims, _ := token.Claims.(CustomClaims)
return claims
}
// func getClaims(token *jwt.Token) CustomClaims {
// claims, _ := token.Claims.(CustomClaims)
// return claims
// }

View File

@ -777,7 +777,7 @@ func getServerInfo(w http.ResponseWriter, r *http.Request) {
}
info := ServerInfo{
Version: "0.0.32",
Version: "0.0.33",
RegistrationEnabled: cachedRegistrationEnabled,
}

View File

@ -320,7 +320,7 @@ func getTopUsersForTrackUUID(trackUUID string, limit int, page int) (TopUserTrac
// Yeah this isn't great. But for now.. it works! Cache later
// TODO: This is counting total scrobbles, not unique users
total, err := getDbCount(
"SELECT COUNT(*) FROM `scrobbles` WHERE `track` = UUID_TO_BIN(?, true) GROUP BY `user`", trackUUID)
"SELECT COUNT(*) FROM `scrobbles` WHERE `track` = UUID_TO_BIN(?, true) GROUP BY `track`, `user`", trackUUID)
if err != nil {
log.Printf("Failed to fetch scrobble count: %+v", err)

View File

@ -28,6 +28,7 @@ type User struct {
Verified bool `json:"verified"`
Active bool `json:"active"`
Admin bool `json:"admin"`
Mod bool `json:"mod"`
Timezone string `json:"timezone"`
Token string `json:"token"`
}
@ -98,16 +99,16 @@ func loginUser(logReq *RequestRequest, ip net.IP) ([]byte, error) {
}
if strings.Contains(logReq.Username, "@") {
err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), `username`, `email`, `password`, `admin` FROM `users` WHERE `email` = ? AND `active` = 1",
logReq.Username).Scan(&user.UUID, &user.Username, &user.Email, &user.Password, &user.Admin)
err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), `username`, `email`, `password`, `admin`, `mod` FROM `users` WHERE `email` = ? AND `active` = 1",
logReq.Username).Scan(&user.UUID, &user.Username, &user.Email, &user.Password, &user.Admin, &user.Mod)
if err != nil {
if err == sql.ErrNoRows {
return resp, errors.New("Invalid Username or Password")
}
}
} else {
err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), `username`, `email`, `password`, `admin` FROM `users` WHERE `username` = ? AND `active` = 1",
logReq.Username).Scan(&user.UUID, &user.Username, &user.Email, &user.Password, &user.Admin)
err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), `username`, `email`, `password`, `admin`, `mod` FROM `users` WHERE `username` = ? AND `active` = 1",
logReq.Username).Scan(&user.UUID, &user.Username, &user.Email, &user.Password, &user.Admin, &user.Mod)
if err == sql.ErrNoRows {
return resp, errors.New("Invalid Username or Password")
}
@ -196,8 +197,8 @@ func userAlreadyExists(req *RequestRequest) bool {
func getUserByUUID(uuid string) (User, error) {
var user User
err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `username`, `email`, `password`, `verified`, `admin`, `timezone`, `token` FROM `users` WHERE `uuid` = UUID_TO_BIN(?, true) AND `active` = 1",
uuid).Scan(&user.UUID, &user.CreatedAt, &user.CreatedIp, &user.ModifiedAt, &user.ModifiedIP, &user.Username, &user.Email, &user.Password, &user.Verified, &user.Admin, &user.Timezone, &user.Token)
err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `username`, `email`, `password`, `verified`, `admin`, `mod`, `timezone`, `token` FROM `users` WHERE `uuid` = UUID_TO_BIN(?, true) AND `active` = 1",
uuid).Scan(&user.UUID, &user.CreatedAt, &user.CreatedIp, &user.ModifiedAt, &user.ModifiedIP, &user.Username, &user.Email, &user.Password, &user.Verified, &user.Admin, &user.Mod, &user.Timezone, &user.Token)
if err == sql.ErrNoRows {
return user, errors.New("Invalid JWT Token")
@ -208,8 +209,8 @@ func getUserByUUID(uuid string) (User, error) {
func getUserByUsername(username string) (User, error) {
var user User
err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `username`, `email`, `password`, `verified`, `admin`, `timezone`, `token` FROM `users` WHERE `username` = ? AND `active` = 1",
username).Scan(&user.UUID, &user.CreatedAt, &user.CreatedIp, &user.ModifiedAt, &user.ModifiedIP, &user.Username, &user.Email, &user.Password, &user.Verified, &user.Admin, &user.Timezone, &user.Token)
err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `username`, `email`, `password`, `verified`, `admin`, `mod`, `timezone`, `token` FROM `users` WHERE `username` = ? AND `active` = 1",
username).Scan(&user.UUID, &user.CreatedAt, &user.CreatedIp, &user.ModifiedAt, &user.ModifiedIP, &user.Username, &user.Email, &user.Password, &user.Verified, &user.Admin, &user.Mod, &user.Timezone, &user.Token)
if err == sql.ErrNoRows {
return user, errors.New("Invalid Username")
@ -220,8 +221,8 @@ func getUserByUsername(username string) (User, error) {
func getUserByEmail(email string) (User, error) {
var user User
err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `username`, `email`, `password`, `verified`, `admin`, `timezone`, `token` FROM `users` WHERE `email` = ? AND `active` = 1",
email).Scan(&user.UUID, &user.CreatedAt, &user.CreatedIp, &user.ModifiedAt, &user.ModifiedIP, &user.Username, &user.Email, &user.Password, &user.Verified, &user.Admin, &user.Timezone, &user.Token)
err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `username`, `email`, `password`, `verified`, `admin`, `mod`, `timezone`, `token` FROM `users` WHERE `email` = ? AND `active` = 1",
email).Scan(&user.UUID, &user.CreatedAt, &user.CreatedIp, &user.ModifiedAt, &user.ModifiedIP, &user.Username, &user.Email, &user.Password, &user.Verified, &user.Admin, &user.Mod, &user.Timezone, &user.Token)
if err == sql.ErrNoRows {
return user, errors.New("Invalid Email")
@ -232,9 +233,9 @@ func getUserByEmail(email string) (User, error) {
func getUserByResetToken(token string) (User, error) {
var user User
err := db.QueryRow("SELECT BIN_TO_UUID(`users`.`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `username`, `email`, `password`, `verified`, `admin`, `timezone`, `token` FROM `users` "+
err := db.QueryRow("SELECT BIN_TO_UUID(`users`.`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `username`, `email`, `password`, `verified`, `admin`, `mod`, `timezone`, `token` FROM `users` "+
"JOIN `resettoken` ON `resettoken`.`user` = `users`.`uuid` WHERE `resettoken`.`token` = ? AND `active` = 1",
token).Scan(&user.UUID, &user.CreatedAt, &user.CreatedIp, &user.ModifiedAt, &user.ModifiedIP, &user.Username, &user.Email, &user.Password, &user.Verified, &user.Admin, &user.Timezone, &user.Token)
token).Scan(&user.UUID, &user.CreatedAt, &user.CreatedIp, &user.ModifiedAt, &user.ModifiedIP, &user.Username, &user.Email, &user.Password, &user.Verified, &user.Admin, &user.Mod, &user.Timezone, &user.Token)
if err == sql.ErrNoRows {
return user, errors.New("Invalid Token")
@ -316,7 +317,7 @@ func (user *User) getNavidromeTokens() (OauthToken, error) {
func getAllSpotifyUsers() ([]User, error) {
users := make([]User, 0)
rows, err := db.Query("SELECT BIN_TO_UUID(`users`.`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `users`.`username`, `email`, `password`, `verified`, `admin`, `timezone` FROM `users` " +
rows, err := db.Query("SELECT BIN_TO_UUID(`users`.`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `users`.`username`, `email`, `password`, `verified`, `admin`, `mod`, `timezone` FROM `users` " +
"JOIN `oauth_tokens` ON `oauth_tokens`.`user` = `users`.`uuid` AND `oauth_tokens`.`service` = 'spotify' WHERE `users`.`active` = 1")
if err != nil {
@ -328,7 +329,7 @@ func getAllSpotifyUsers() ([]User, error) {
for rows.Next() {
var user User
err := rows.Scan(&user.UUID, &user.CreatedAt, &user.CreatedIp, &user.ModifiedAt, &user.ModifiedIP, &user.Username, &user.Email, &user.Password, &user.Verified, &user.Admin, &user.Timezone)
err := rows.Scan(&user.UUID, &user.CreatedAt, &user.CreatedIp, &user.ModifiedAt, &user.ModifiedIP, &user.Username, &user.Email, &user.Password, &user.Verified, &user.Admin, &user.Mod, &user.Timezone)
if err != nil {
log.Printf("Failed to fetch spotify user: %+v", err)
return users, errors.New("Failed to fetch users")
@ -348,7 +349,7 @@ func getAllSpotifyUsers() ([]User, error) {
func getAllNavidromeUsers() ([]User, error) {
users := make([]User, 0)
rows, err := db.Query("SELECT BIN_TO_UUID(`users`.`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `users`.`username`, `email`, `password`, `verified`, `admin`, `timezone` FROM `users` " +
rows, err := db.Query("SELECT BIN_TO_UUID(`users`.`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `users`.`username`, `email`, `password`, `verified`, `admin`, `mod`, `timezone` FROM `users` " +
"JOIN `oauth_tokens` ON `oauth_tokens`.`user` = `users`.`uuid` AND `oauth_tokens`.`service` = 'navidrome' WHERE `users`.`active` = 1")
if err != nil {
@ -360,7 +361,7 @@ func getAllNavidromeUsers() ([]User, error) {
for rows.Next() {
var user User
err := rows.Scan(&user.UUID, &user.CreatedAt, &user.CreatedIp, &user.ModifiedAt, &user.ModifiedIP, &user.Username, &user.Email, &user.Password, &user.Verified, &user.Admin, &user.Timezone)
err := rows.Scan(&user.UUID, &user.CreatedAt, &user.CreatedIp, &user.ModifiedAt, &user.ModifiedIP, &user.Username, &user.Email, &user.Password, &user.Verified, &user.Admin, &user.Mod, &user.Timezone)
if err != nil {
log.Printf("Failed to fetch navidrome user: %+v", err)
return users, errors.New("Failed to fetch users")

View File

@ -0,0 +1 @@
ALTER TABLE `users` DROP COLUMN `mod`;

View File

@ -0,0 +1 @@
ALTER TABLE `users` ADD COLUMN `mod` TINYINT(1) NOT NULL DEFAULT 0 AFTER `admin`;

View File

@ -59,6 +59,7 @@ export const PostLogin = (formValues) => {
exp: expandedUser.exp,
username: expandedUser.username,
admin: expandedUser.admin,
mod: expandedUser.mod,
refresh_token: expandedUser.refresh_token,
refresh_exp: expandedUser.refresh_exp,
}
@ -92,6 +93,7 @@ export const PostRefreshToken = (refreshToken) => {
exp: expandedUser.exp,
username: expandedUser.username,
admin: expandedUser.admin,
mod: expandedUser.mod,
refresh_token: expandedUser.refresh_token,
refresh_exp: expandedUser.refresh_exp,
}

View File

@ -5,6 +5,7 @@ import Profile from './Pages/Profile';
import Artist from './Pages/Artist';
import Album from './Pages/Album';
import Track from './Pages/Track';
import TrackEdit from './Pages/TrackEdit';
import User from './Pages/User';
import Admin from './Pages/Admin';
import Login from './Pages/Login';
@ -36,6 +37,7 @@ const App = () => {
<Route path="/u/:uuid" component={Profile} />
<Route path="/artist/:uuid" component={Artist} />
<Route path="/album/:uuid" component={Album} />
<Route path="/track/:uuid/edit" component={TrackEdit} />
<Route path="/track/:uuid" component={Track} />
<Route path="/admin" component={Admin} />

View File

@ -12,6 +12,7 @@ import { getConfigs, postConfigs } from '../Api/index'
const Admin = () => {
const history = useHistory();
const { user } = useContext(AuthContext);
const [loading, setLoading] = useState(true);
const [configs, setConfigs] = useState({})
const [toggle, setToggle] = useState(false);
@ -47,8 +48,6 @@ const Admin = () => {
)
}
return (
<div className="pageWrapper">
<h1>

View File

@ -1,12 +1,15 @@
import React, { useState, useEffect } from 'react';
import React, { useContext, useState, useEffect } from 'react';
import '../App.css';
import './Track.css';
import TopUserTable from '../Components/TopUserTable';
import ScaleLoader from 'react-spinners/ScaleLoader';
import { getTrack } from '../Api/index'
import { Link } from 'react-router-dom';
import AuthContext from '../Contexts/AuthContext';
const Track = (route) => {
const { user } = useContext(AuthContext);
const [loading, setLoading] = useState(true);
const [track, setTrack] = useState({});
@ -76,7 +79,10 @@ const Track = (route) => {
return (
<div className="pageWrapper">
<h1 style={{margin: 0}}>
{track.name}
{track.name} {user && <Link
key="editbuttonomg"
to={"/track/" + trackUUID + "/edit"}
>edit</Link>}
</h1>
<div className="pageBody">
<div style={{display: `flex`, flexWrap: `wrap`, textAlign: `center`}}>

View File

113
web/src/Pages/TrackEdit.js Normal file
View File

@ -0,0 +1,113 @@
import React, { useContext, useState, useEffect } from 'react';
import '../App.css';
import './TrackEdit.css';
import { useHistory } from 'react-router-dom';
import ScaleLoader from 'react-spinners/ScaleLoader';
import { getTrack } from '../Api/index'
import { Link } from 'react-router-dom';
import AuthContext from '../Contexts/AuthContext';
const TrackEdit = (route) => {
const history = useHistory();
const { user } = useContext(AuthContext);
const [loading, setLoading] = useState(true);
const [track, setTrack] = useState({});
let trackUUID = false;
if (route && route.match && route.match.params && route.match.params.uuid) {
trackUUID = route.match.params.uuid;
} else {
trackUUID = false;
}
useEffect(() => {
if (!trackUUID) {
return false;
}
getTrack(trackUUID)
.then(data => {
setTrack(data);
setLoading(false);
})
}, [trackUUID])
if (!user) {
history.push("/login")
}
if (user && !user.mod) {
history.push("/Dashboard")
}
if (loading) {
return (
<div className="pageWrapper">
<ScaleLoader color="#6AD7E5" />
</div>
)
}
if (!trackUUID || !track) {
return (
<div className="pageWrapper">
Unable to fetch user
</div>
)
}
console.log(track)
let length = "0";
if (track.length && track.length !== '') {
length = new Date(track.length * 1000).toISOString().substr(11, 8)
}
let artists = [];
for (let artist of track.artists) {
const row = (
<Link
key={artist.uuid}
to={"/artist/" + artist.uuid}
>{artist.name} </Link>
);
artists.push(row);
}
let albums = [];
for (let album of track.albums) {
const row = (
<Link
key={album.uuid}
to={"/album/" + album.uuid}
>{album.name} </Link>
);
albums.push(row);
}
return (
<div className="pageWrapper">
<h1 style={{margin: 0}}>
{track.name} {<Link
key="editbuttonomg"
to={"/track/" + trackUUID}
>unedit</Link>}
</h1>
<div className="pageBody" style={{width: `900px`, textAlign: `center`}}>
<img src={process.env.REACT_APP_API_URL + "/img/" + track.img + "_full.jpg"} alt={track.name} style={{maxWidth: `300px`, maxHeight: `300px`}}/>
<br/>
<label>Primary Artist ({track.artists[0].name}):</label><br/>
<input type="text" value={track.artists[0].uuid} style={{width: `420px`}} disabled="true"/><br/>
<label>Primary Album ({track.albums[0].name})</label><br/>
<input type="text" value={track.albums[0].uuid} style={{width: `420px`}} disabled="true"/><br/>
<br/>
<label>MBID</label><br/>
<input type="text" value={track.mbid} style={{width: `420px`}} /><br/>
<label>Spotify ID</label><br/>
<input type="text" value={track.spotify_id} style={{width: `420px`}} /><br/>
</div>
</div>
);
}
export default TrackEdit;