mirror of
https://github.com/idanoo/GoScrobble.git
synced 2024-11-22 08:25:14 +00:00
0.0.8
- Added Admin/Site config page in frontend for admin users - Added API POST/GET /config endpoint
This commit is contained in:
parent
8be4a190a6
commit
af02bd99cc
@ -3,12 +3,13 @@ stages:
|
|||||||
- bundle
|
- bundle
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
VERSION: 0.0.7
|
VERSION: 0.0.8
|
||||||
|
|
||||||
build-go:
|
build-go:
|
||||||
image: golang:1.16.2
|
image: golang:1.16.2
|
||||||
stage: build
|
stage: build
|
||||||
only: master
|
only:
|
||||||
|
- master
|
||||||
script:
|
script:
|
||||||
- go build -o goscrobble cmd/go-scrobble/*.go
|
- go build -o goscrobble cmd/go-scrobble/*.go
|
||||||
artifacts:
|
artifacts:
|
||||||
@ -22,7 +23,8 @@ build-go:
|
|||||||
build-react:
|
build-react:
|
||||||
image: node:15.12.0
|
image: node:15.12.0
|
||||||
stage: build
|
stage: build
|
||||||
only: master
|
only:
|
||||||
|
- master
|
||||||
script:
|
script:
|
||||||
- cd web
|
- cd web
|
||||||
- npm install
|
- npm install
|
||||||
@ -35,7 +37,8 @@ build-react:
|
|||||||
bundle:
|
bundle:
|
||||||
image: bash:latest
|
image: bash:latest
|
||||||
stage: bundle
|
stage: bundle
|
||||||
only: master
|
only:
|
||||||
|
- master
|
||||||
variables:
|
variables:
|
||||||
GIT_STRATEGY: none
|
GIT_STRATEGY: none
|
||||||
before_script:
|
before_script:
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
|
# 0.0.8
|
||||||
|
- Added Admin/Site config page in frontend for admin users
|
||||||
|
- Added API POST/GET /config endpoint
|
||||||
|
|
||||||
# 0.0.7
|
# 0.0.7
|
||||||
- Switch redux -> Context
|
- Switch redux -> Context
|
||||||
- Remove excess packages
|
- Remove excess packages
|
||||||
|
|
||||||
|
|
||||||
# 0.0.6
|
# 0.0.6
|
||||||
- Fix hitting dashboard when logged out
|
- Fix hitting dashboard when logged out
|
||||||
- Clean up app.js
|
- Clean up app.js
|
||||||
|
57
internal/goscrobble/config.go
Normal file
57
internal/goscrobble/config.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package goscrobble
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Setting map[string]string `json:"configs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAllConfigs() (Config, error) {
|
||||||
|
config := Config{}
|
||||||
|
configs := make(map[string]string)
|
||||||
|
|
||||||
|
rows, err := db.Query("SELECT `key`, `value` FROM `config`")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to fetch config: %+v", err)
|
||||||
|
return config, errors.New("Failed to fetch configs")
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var key string
|
||||||
|
var value string
|
||||||
|
err := rows.Scan(&key, &value)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to fetch config: %+v", err)
|
||||||
|
return config, errors.New("Failed to fetch configs")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append
|
||||||
|
configs[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign the data to the parent
|
||||||
|
config.Setting = configs
|
||||||
|
|
||||||
|
err = rows.Err()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to fetch config: %+v", err)
|
||||||
|
return config, errors.New("Failed to fetch configs")
|
||||||
|
}
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateConfigValue(key string, value string) error {
|
||||||
|
_, err := db.Exec("UPDATE `config` SET `value` = ? WHERE `key` = ?", value, key)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to update config: %+v", err)
|
||||||
|
return errors.New("Failed to update config value.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -15,6 +15,7 @@ var JwtExpiry time.Duration
|
|||||||
type CustomClaims struct {
|
type CustomClaims struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
|
Admin bool `json:"admin"`
|
||||||
jwt.StandardClaims
|
jwt.StandardClaims
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,6 +24,7 @@ func generateJWTToken(user User) (string, error) {
|
|||||||
atClaims["sub"] = user.UUID
|
atClaims["sub"] = user.UUID
|
||||||
atClaims["username"] = user.Username
|
atClaims["username"] = user.Username
|
||||||
atClaims["email"] = user.Email
|
atClaims["email"] = user.Email
|
||||||
|
atClaims["admin"] = user.Admin
|
||||||
atClaims["iat"] = time.Now().Unix()
|
atClaims["iat"] = time.Now().Unix()
|
||||||
atClaims["exp"] = time.Now().Add(JwtExpiry).Unix()
|
atClaims["exp"] = time.Now().Add(JwtExpiry).Unix()
|
||||||
at := jwt.NewWithClaims(jwt.SigningMethodHS512, atClaims)
|
at := jwt.NewWithClaims(jwt.SigningMethodHS512, atClaims)
|
||||||
|
@ -51,6 +51,10 @@ func HandleRequests(port string) {
|
|||||||
// JWT Auth
|
// JWT Auth
|
||||||
v1.HandleFunc("/user/{id}/scrobbles", jwtMiddleware(fetchScrobbleResponse)).Methods("GET")
|
v1.HandleFunc("/user/{id}/scrobbles", jwtMiddleware(fetchScrobbleResponse)).Methods("GET")
|
||||||
|
|
||||||
|
// Config auth
|
||||||
|
v1.HandleFunc("/config", adminMiddleware(fetchConfig)).Methods("GET")
|
||||||
|
v1.HandleFunc("/config", adminMiddleware(postConfig)).Methods("POST")
|
||||||
|
|
||||||
// No Auth
|
// No Auth
|
||||||
v1.HandleFunc("/register", limitMiddleware(handleRegister, heavyLimiter)).Methods("POST")
|
v1.HandleFunc("/register", limitMiddleware(handleRegister, heavyLimiter)).Methods("POST")
|
||||||
v1.HandleFunc("/login", limitMiddleware(handleLogin, standardLimiter)).Methods("POST")
|
v1.HandleFunc("/login", limitMiddleware(handleLogin, standardLimiter)).Methods("POST")
|
||||||
@ -117,6 +121,16 @@ func throwOkMessage(w http.ResponseWriter, m string) {
|
|||||||
w.Write(js)
|
w.Write(js)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// throwOkMessage - Throws a happy 200
|
||||||
|
func throwInvalidJson(w http.ResponseWriter) {
|
||||||
|
jr := jsonResponse{
|
||||||
|
Err: "Invalid JSON",
|
||||||
|
}
|
||||||
|
js, _ := json.Marshal(&jr)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Write(js)
|
||||||
|
}
|
||||||
|
|
||||||
// generateJsonMessage - Generates a message:str response
|
// generateJsonMessage - Generates a message:str response
|
||||||
func generateJsonMessage(m string) []byte {
|
func generateJsonMessage(m string) []byte {
|
||||||
jr := jsonResponse{
|
jr := jsonResponse{
|
||||||
@ -142,9 +156,10 @@ func tokenMiddleware(next func(http.ResponseWriter, *http.Request, string)) http
|
|||||||
authToken := strings.Replace(fullToken, "Bearer ", "", 1)
|
authToken := strings.Replace(fullToken, "Bearer ", "", 1)
|
||||||
if authToken == "" {
|
if authToken == "" {
|
||||||
throwUnauthorized(w, "A token is required")
|
throwUnauthorized(w, "A token is required")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userUuid, err := getUserForToken(authToken)
|
userUuid, err := getUserUuidForToken(authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
throwUnauthorized(w, err.Error())
|
throwUnauthorized(w, err.Error())
|
||||||
return
|
return
|
||||||
@ -180,6 +195,32 @@ func jwtMiddleware(next func(http.ResponseWriter, *http.Request, string, string)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// adminMiddleware - Validates user is admin
|
||||||
|
func adminMiddleware(next func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fullToken := r.Header.Get("Authorization")
|
||||||
|
authToken := strings.Replace(fullToken, "Bearer ", "", 1)
|
||||||
|
claims, err := verifyJWTToken(authToken)
|
||||||
|
if err != nil {
|
||||||
|
throwUnauthorized(w, "Invalid JWT Token")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := getUser(claims.Subject)
|
||||||
|
if err != nil {
|
||||||
|
throwUnauthorized(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !user.Admin {
|
||||||
|
throwUnauthorized(w, "User is not admin")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
next(w, r, claims.Subject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// limitMiddleware - Rate limits important stuff
|
// limitMiddleware - Rate limits important stuff
|
||||||
func limitMiddleware(next http.HandlerFunc, limiter *IPRateLimiter) http.HandlerFunc {
|
func limitMiddleware(next http.HandlerFunc, limiter *IPRateLimiter) http.HandlerFunc {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -255,8 +296,7 @@ func handleStats(w http.ResponseWriter, r *http.Request) {
|
|||||||
func handleIngress(w http.ResponseWriter, r *http.Request, userUuid string) {
|
func handleIngress(w http.ResponseWriter, r *http.Request, userUuid string) {
|
||||||
bodyJson, err := decodeJson(r.Body)
|
bodyJson, err := decodeJson(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If we can't decode. Lets tell them nicely.
|
throwInvalidJson(w)
|
||||||
http.Error(w, "{\"error\":\"Invalid JSON\"}", http.StatusBadRequest)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,6 +342,38 @@ func fetchScrobbleResponse(w http.ResponseWriter, r *http.Request, jwtUser strin
|
|||||||
w.Write(json)
|
w.Write(json)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fetchScrobbles - Return an array of scrobbles
|
||||||
|
func fetchConfig(w http.ResponseWriter, r *http.Request, jwtUser string) {
|
||||||
|
config, err := getAllConfigs()
|
||||||
|
if err != nil {
|
||||||
|
throwOkError(w, "Failed to fetch scrobbles")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
json, _ := json.Marshal(&config)
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write(json)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetchScrobbles - Return an array of scrobbles
|
||||||
|
func postConfig(w http.ResponseWriter, r *http.Request, jwtUser string) {
|
||||||
|
bodyJson, err := decodeJson(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
throwInvalidJson(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range bodyJson {
|
||||||
|
err = updateConfigValue(k, fmt.Sprintf("%s", v))
|
||||||
|
if err != nil {
|
||||||
|
throwOkError(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throwOkMessage(w, "Config updated successfully")
|
||||||
|
}
|
||||||
|
|
||||||
// FRONTEND HANDLING
|
// FRONTEND HANDLING
|
||||||
|
|
||||||
// ServerHTTP - Frontend server
|
// ServerHTTP - Frontend server
|
||||||
|
@ -15,7 +15,7 @@ func generateToken(n int) string {
|
|||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUserForToken(token string) (string, error) {
|
func getUserUuidForToken(token string) (string, error) {
|
||||||
var uuid string
|
var uuid string
|
||||||
cachedKey := getRedisVal("user_token:" + token)
|
cachedKey := getRedisVal("user_token:" + token)
|
||||||
if cachedKey == "" {
|
if cachedKey == "" {
|
||||||
|
@ -98,16 +98,16 @@ func loginUser(logReq *LoginRequest, ip net.IP) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if strings.Contains(logReq.Username, "@") {
|
if strings.Contains(logReq.Username, "@") {
|
||||||
err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), `username`, `email`, `password` FROM `users` WHERE `email` = ? AND `active` = 1",
|
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)
|
logReq.Username).Scan(&user.UUID, &user.Username, &user.Email, &user.Password, &user.Admin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return resp, errors.New("Invalid Username or Password")
|
return resp, errors.New("Invalid Username or Password")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), `username`, `email`, `password` FROM `users` WHERE `username` = ? AND `active` = 1",
|
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)
|
logReq.Username).Scan(&user.UUID, &user.Username, &user.Email, &user.Password, &user.Admin)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return resp, errors.New("Invalid Username or Password")
|
return resp, errors.New("Invalid Username or Password")
|
||||||
}
|
}
|
||||||
@ -193,3 +193,15 @@ func userAlreadyExists(req *RegisterRequest) bool {
|
|||||||
|
|
||||||
return count > 0
|
return count > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getUser(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` 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)
|
||||||
|
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return user, errors.New("Invalid JWT Token")
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
1
migrations/6_configs.down.sql
Normal file
1
migrations/6_configs.down.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
TRUNCATE `config`;
|
7
migrations/6_configs.up.sql
Normal file
7
migrations/6_configs.up.sql
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
INSERT INTO
|
||||||
|
`config`(`key`, `value`)
|
||||||
|
VALUES
|
||||||
|
('SPOTIFY_API_ID', ''),
|
||||||
|
('SPOTIFY_API_SECRET', ''),
|
||||||
|
('LASTFM_API_KEY', ''),
|
||||||
|
('REGISTRATION_ENABLED', '1');
|
474
web/package-lock.json
generated
474
web/package-lock.json
generated
@ -8,6 +8,7 @@
|
|||||||
"name": "goscrobble",
|
"name": "goscrobble",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@material-ui/core": "^4.11.3",
|
||||||
"@reduxjs/toolkit": "^1.5.0",
|
"@reduxjs/toolkit": "^1.5.0",
|
||||||
"@testing-library/jest-dom": "^5.11.9",
|
"@testing-library/jest-dom": "^5.11.9",
|
||||||
"@testing-library/react": "^11.2.5",
|
"@testing-library/react": "^11.2.5",
|
||||||
@ -15,6 +16,7 @@
|
|||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"bootstrap": "^4.6.0",
|
"bootstrap": "^4.6.0",
|
||||||
"formik": "^2.2.6",
|
"formik": "^2.2.6",
|
||||||
|
"formik-material-ui": "^3.0.1",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-bootstrap": "^1.5.2",
|
"react-bootstrap": "^1.5.2",
|
||||||
@ -2370,6 +2372,155 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@material-ui/core": {
|
||||||
|
"version": "4.11.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.11.3.tgz",
|
||||||
|
"integrity": "sha512-Adt40rGW6Uds+cAyk3pVgcErpzU/qxc7KBR94jFHBYretU4AtWZltYcNsbeMn9tXL86jjVL1kuGcIHsgLgFGRw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.4.4",
|
||||||
|
"@material-ui/styles": "^4.11.3",
|
||||||
|
"@material-ui/system": "^4.11.3",
|
||||||
|
"@material-ui/types": "^5.1.0",
|
||||||
|
"@material-ui/utils": "^4.11.2",
|
||||||
|
"@types/react-transition-group": "^4.2.0",
|
||||||
|
"clsx": "^1.0.4",
|
||||||
|
"hoist-non-react-statics": "^3.3.2",
|
||||||
|
"popper.js": "1.16.1-lts",
|
||||||
|
"prop-types": "^15.7.2",
|
||||||
|
"react-is": "^16.8.0 || ^17.0.0",
|
||||||
|
"react-transition-group": "^4.4.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/material-ui"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^16.8.6 || ^17.0.0",
|
||||||
|
"react": "^16.8.0 || ^17.0.0",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material-ui/core/node_modules/popper.js": {
|
||||||
|
"version": "1.16.1-lts",
|
||||||
|
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz",
|
||||||
|
"integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA=="
|
||||||
|
},
|
||||||
|
"node_modules/@material-ui/styles": {
|
||||||
|
"version": "4.11.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.3.tgz",
|
||||||
|
"integrity": "sha512-HzVzCG+PpgUGMUYEJ2rTEmQYeonGh41BYfILNFb/1ueqma+p1meSdu4RX6NjxYBMhf7k+jgfHFTTz+L1SXL/Zg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.4.4",
|
||||||
|
"@emotion/hash": "^0.8.0",
|
||||||
|
"@material-ui/types": "^5.1.0",
|
||||||
|
"@material-ui/utils": "^4.11.2",
|
||||||
|
"clsx": "^1.0.4",
|
||||||
|
"csstype": "^2.5.2",
|
||||||
|
"hoist-non-react-statics": "^3.3.2",
|
||||||
|
"jss": "^10.5.1",
|
||||||
|
"jss-plugin-camel-case": "^10.5.1",
|
||||||
|
"jss-plugin-default-unit": "^10.5.1",
|
||||||
|
"jss-plugin-global": "^10.5.1",
|
||||||
|
"jss-plugin-nested": "^10.5.1",
|
||||||
|
"jss-plugin-props-sort": "^10.5.1",
|
||||||
|
"jss-plugin-rule-value-function": "^10.5.1",
|
||||||
|
"jss-plugin-vendor-prefixer": "^10.5.1",
|
||||||
|
"prop-types": "^15.7.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/material-ui"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^16.8.6 || ^17.0.0",
|
||||||
|
"react": "^16.8.0 || ^17.0.0",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material-ui/styles/node_modules/csstype": {
|
||||||
|
"version": "2.6.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.16.tgz",
|
||||||
|
"integrity": "sha512-61FBWoDHp/gRtsoDkq/B1nWrCUG/ok1E3tUrcNbZjsE9Cxd9yzUirjS3+nAATB8U4cTtaQmAHbNndoFz5L6C9Q=="
|
||||||
|
},
|
||||||
|
"node_modules/@material-ui/system": {
|
||||||
|
"version": "4.11.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.11.3.tgz",
|
||||||
|
"integrity": "sha512-SY7otguNGol41Mu2Sg6KbBP1ZRFIbFLHGK81y4KYbsV2yIcaEPOmsCK6zwWlp+2yTV3J/VwT6oSBARtGIVdXPw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.4.4",
|
||||||
|
"@material-ui/utils": "^4.11.2",
|
||||||
|
"csstype": "^2.5.2",
|
||||||
|
"prop-types": "^15.7.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/material-ui"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^16.8.6 || ^17.0.0",
|
||||||
|
"react": "^16.8.0 || ^17.0.0",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material-ui/system/node_modules/csstype": {
|
||||||
|
"version": "2.6.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.16.tgz",
|
||||||
|
"integrity": "sha512-61FBWoDHp/gRtsoDkq/B1nWrCUG/ok1E3tUrcNbZjsE9Cxd9yzUirjS3+nAATB8U4cTtaQmAHbNndoFz5L6C9Q=="
|
||||||
|
},
|
||||||
|
"node_modules/@material-ui/types": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material-ui/utils": {
|
||||||
|
"version": "4.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.2.tgz",
|
||||||
|
"integrity": "sha512-Uul8w38u+PICe2Fg2pDKCaIG7kOyhowZ9vjiC1FsVwPABTW8vPPKfF6OvxRq3IiBaI1faOJmgdvMG7rMJARBhA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.4.4",
|
||||||
|
"prop-types": "^15.7.2",
|
||||||
|
"react-is": "^16.8.0 || ^17.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.4",
|
"version": "2.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz",
|
||||||
@ -6186,6 +6337,15 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/css-vendor": {
|
||||||
|
"version": "2.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz",
|
||||||
|
"integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.8.3",
|
||||||
|
"is-in-browser": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/css-what": {
|
"node_modules/css-what": {
|
||||||
"version": "3.4.2",
|
"version": "3.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz",
|
||||||
@ -9009,6 +9169,17 @@
|
|||||||
"react": ">=16.8.0"
|
"react": ">=16.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/formik-material-ui": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/formik-material-ui/-/formik-material-ui-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-N8oxZIdhY70npRv86IfF6Zaaps9RL3a37XRdq02WDroB3XZC1mXs6lA/zQ09ZYFWYJp/UjI80SKVpVa/xJOJJA==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@material-ui/core": ">=4.0.0",
|
||||||
|
"formik": ">=2.0.0",
|
||||||
|
"react": ">=16.8.0",
|
||||||
|
"tiny-warning": ">=1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/formik/node_modules/deepmerge": {
|
"node_modules/formik/node_modules/deepmerge": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
|
||||||
@ -9954,6 +10125,11 @@
|
|||||||
"node": ">=8.12.0"
|
"node": ">=8.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/hyphenate-style-name": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ=="
|
||||||
|
},
|
||||||
"node_modules/iconv-lite": {
|
"node_modules/iconv-lite": {
|
||||||
"version": "0.4.24",
|
"version": "0.4.24",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
@ -10115,6 +10291,14 @@
|
|||||||
"node": ">=0.8.19"
|
"node": ">=0.8.19"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/indefinite-observable": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/indefinite-observable/-/indefinite-observable-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-G8vgmork+6H9S8lUAg1gtXEj2JxIQTo0g2PbFiYOdjkziSI0F7UYBiVwhZRuixhBCNGczAls34+5HJPyZysvxQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"symbol-observable": "1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/indent-string": {
|
"node_modules/indent-string": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
||||||
@ -10432,6 +10616,11 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-in-browser": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz",
|
||||||
|
"integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU="
|
||||||
|
},
|
||||||
"node_modules/is-module": {
|
"node_modules/is-module": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
|
||||||
@ -12845,6 +13034,89 @@
|
|||||||
"verror": "1.10.0"
|
"verror": "1.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jss": {
|
||||||
|
"version": "10.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss/-/jss-10.6.0.tgz",
|
||||||
|
"integrity": "sha512-n7SHdCozmxnzYGXBHe0NsO0eUf9TvsHVq2MXvi4JmTn3x5raynodDVE/9VQmBdWFyyj9HpHZ2B4xNZ7MMy7lkw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"csstype": "^3.0.2",
|
||||||
|
"indefinite-observable": "^2.0.1",
|
||||||
|
"is-in-browser": "^1.1.3",
|
||||||
|
"tiny-warning": "^1.0.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/jss"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jss-plugin-camel-case": {
|
||||||
|
"version": "10.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.6.0.tgz",
|
||||||
|
"integrity": "sha512-JdLpA3aI/npwj3nDMKk308pvnhoSzkW3PXlbgHAzfx0yHWnPPVUjPhXFtLJzgKZge8lsfkUxvYSQ3X2OYIFU6A==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"hyphenate-style-name": "^1.0.3",
|
||||||
|
"jss": "10.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jss-plugin-default-unit": {
|
||||||
|
"version": "10.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.6.0.tgz",
|
||||||
|
"integrity": "sha512-7y4cAScMHAxvslBK2JRK37ES9UT0YfTIXWgzUWD5euvR+JR3q+o8sQKzBw7GmkQRfZijrRJKNTiSt1PBsLI9/w==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"jss": "10.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jss-plugin-global": {
|
||||||
|
"version": "10.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.6.0.tgz",
|
||||||
|
"integrity": "sha512-I3w7ji/UXPi3VuWrTCbHG9rVCgB4yoBQLehGDTmsnDfXQb3r1l3WIdcO8JFp9m0YMmyy2CU7UOV6oPI7/Tmu+w==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"jss": "10.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jss-plugin-nested": {
|
||||||
|
"version": "10.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.6.0.tgz",
|
||||||
|
"integrity": "sha512-fOFQWgd98H89E6aJSNkEh2fAXquC9aZcAVjSw4q4RoQ9gU++emg18encR4AT4OOIFl4lQwt5nEyBBRn9V1Rk8g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"jss": "10.6.0",
|
||||||
|
"tiny-warning": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jss-plugin-props-sort": {
|
||||||
|
"version": "10.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.6.0.tgz",
|
||||||
|
"integrity": "sha512-oMCe7hgho2FllNc60d9VAfdtMrZPo9n1Iu6RNa+3p9n0Bkvnv/XX5San8fTPujrTBScPqv9mOE0nWVvIaohNuw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"jss": "10.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jss-plugin-rule-value-function": {
|
||||||
|
"version": "10.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.6.0.tgz",
|
||||||
|
"integrity": "sha512-TKFqhRTDHN1QrPTMYRlIQUOC2FFQb271+AbnetURKlGvRl/eWLswcgHQajwuxI464uZk91sPiTtdGi7r7XaWfA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"jss": "10.6.0",
|
||||||
|
"tiny-warning": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jss-plugin-vendor-prefixer": {
|
||||||
|
"version": "10.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.6.0.tgz",
|
||||||
|
"integrity": "sha512-doJ7MouBXT1lypLLctCwb4nJ6lDYqrTfVS3LtXgox42Xz0gXusXIIDboeh6UwnSmox90QpVnub7au8ybrb0krQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"css-vendor": "^2.0.8",
|
||||||
|
"jss": "10.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jsx-ast-utils": {
|
"node_modules/jsx-ast-utils": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz",
|
||||||
@ -24050,6 +24322,96 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@material-ui/core": {
|
||||||
|
"version": "4.11.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.11.3.tgz",
|
||||||
|
"integrity": "sha512-Adt40rGW6Uds+cAyk3pVgcErpzU/qxc7KBR94jFHBYretU4AtWZltYcNsbeMn9tXL86jjVL1kuGcIHsgLgFGRw==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.4.4",
|
||||||
|
"@material-ui/styles": "^4.11.3",
|
||||||
|
"@material-ui/system": "^4.11.3",
|
||||||
|
"@material-ui/types": "^5.1.0",
|
||||||
|
"@material-ui/utils": "^4.11.2",
|
||||||
|
"@types/react-transition-group": "^4.2.0",
|
||||||
|
"clsx": "^1.0.4",
|
||||||
|
"hoist-non-react-statics": "^3.3.2",
|
||||||
|
"popper.js": "1.16.1-lts",
|
||||||
|
"prop-types": "^15.7.2",
|
||||||
|
"react-is": "^16.8.0 || ^17.0.0",
|
||||||
|
"react-transition-group": "^4.4.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"popper.js": {
|
||||||
|
"version": "1.16.1-lts",
|
||||||
|
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz",
|
||||||
|
"integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material-ui/styles": {
|
||||||
|
"version": "4.11.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.3.tgz",
|
||||||
|
"integrity": "sha512-HzVzCG+PpgUGMUYEJ2rTEmQYeonGh41BYfILNFb/1ueqma+p1meSdu4RX6NjxYBMhf7k+jgfHFTTz+L1SXL/Zg==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.4.4",
|
||||||
|
"@emotion/hash": "^0.8.0",
|
||||||
|
"@material-ui/types": "^5.1.0",
|
||||||
|
"@material-ui/utils": "^4.11.2",
|
||||||
|
"clsx": "^1.0.4",
|
||||||
|
"csstype": "^2.5.2",
|
||||||
|
"hoist-non-react-statics": "^3.3.2",
|
||||||
|
"jss": "^10.5.1",
|
||||||
|
"jss-plugin-camel-case": "^10.5.1",
|
||||||
|
"jss-plugin-default-unit": "^10.5.1",
|
||||||
|
"jss-plugin-global": "^10.5.1",
|
||||||
|
"jss-plugin-nested": "^10.5.1",
|
||||||
|
"jss-plugin-props-sort": "^10.5.1",
|
||||||
|
"jss-plugin-rule-value-function": "^10.5.1",
|
||||||
|
"jss-plugin-vendor-prefixer": "^10.5.1",
|
||||||
|
"prop-types": "^15.7.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"csstype": {
|
||||||
|
"version": "2.6.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.16.tgz",
|
||||||
|
"integrity": "sha512-61FBWoDHp/gRtsoDkq/B1nWrCUG/ok1E3tUrcNbZjsE9Cxd9yzUirjS3+nAATB8U4cTtaQmAHbNndoFz5L6C9Q=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material-ui/system": {
|
||||||
|
"version": "4.11.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.11.3.tgz",
|
||||||
|
"integrity": "sha512-SY7otguNGol41Mu2Sg6KbBP1ZRFIbFLHGK81y4KYbsV2yIcaEPOmsCK6zwWlp+2yTV3J/VwT6oSBARtGIVdXPw==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.4.4",
|
||||||
|
"@material-ui/utils": "^4.11.2",
|
||||||
|
"csstype": "^2.5.2",
|
||||||
|
"prop-types": "^15.7.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"csstype": {
|
||||||
|
"version": "2.6.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.16.tgz",
|
||||||
|
"integrity": "sha512-61FBWoDHp/gRtsoDkq/B1nWrCUG/ok1E3tUrcNbZjsE9Cxd9yzUirjS3+nAATB8U4cTtaQmAHbNndoFz5L6C9Q=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material-ui/types": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
|
"@material-ui/utils": {
|
||||||
|
"version": "4.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.2.tgz",
|
||||||
|
"integrity": "sha512-Uul8w38u+PICe2Fg2pDKCaIG7kOyhowZ9vjiC1FsVwPABTW8vPPKfF6OvxRq3IiBaI1faOJmgdvMG7rMJARBhA==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.4.4",
|
||||||
|
"prop-types": "^15.7.2",
|
||||||
|
"react-is": "^16.8.0 || ^17.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@nodelib/fs.scandir": {
|
"@nodelib/fs.scandir": {
|
||||||
"version": "2.1.4",
|
"version": "2.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz",
|
||||||
@ -27073,6 +27435,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"css-vendor": {
|
||||||
|
"version": "2.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz",
|
||||||
|
"integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.8.3",
|
||||||
|
"is-in-browser": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"css-what": {
|
"css-what": {
|
||||||
"version": "3.4.2",
|
"version": "3.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz",
|
||||||
@ -29281,6 +29652,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"formik-material-ui": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/formik-material-ui/-/formik-material-ui-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-N8oxZIdhY70npRv86IfF6Zaaps9RL3a37XRdq02WDroB3XZC1mXs6lA/zQ09ZYFWYJp/UjI80SKVpVa/xJOJJA==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"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",
|
||||||
@ -30045,6 +30422,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
|
||||||
"integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="
|
"integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="
|
||||||
},
|
},
|
||||||
|
"hyphenate-style-name": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ=="
|
||||||
|
},
|
||||||
"iconv-lite": {
|
"iconv-lite": {
|
||||||
"version": "0.4.24",
|
"version": "0.4.24",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
@ -30152,6 +30534,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
|
||||||
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
|
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
|
||||||
},
|
},
|
||||||
|
"indefinite-observable": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/indefinite-observable/-/indefinite-observable-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-G8vgmork+6H9S8lUAg1gtXEj2JxIQTo0g2PbFiYOdjkziSI0F7UYBiVwhZRuixhBCNGczAls34+5HJPyZysvxQ==",
|
||||||
|
"requires": {
|
||||||
|
"symbol-observable": "1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"indent-string": {
|
"indent-string": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
||||||
@ -30379,6 +30769,11 @@
|
|||||||
"is-extglob": "^2.1.1"
|
"is-extglob": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"is-in-browser": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz",
|
||||||
|
"integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU="
|
||||||
|
},
|
||||||
"is-module": {
|
"is-module": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
|
||||||
@ -32151,6 +32546,85 @@
|
|||||||
"verror": "1.10.0"
|
"verror": "1.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jss": {
|
||||||
|
"version": "10.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss/-/jss-10.6.0.tgz",
|
||||||
|
"integrity": "sha512-n7SHdCozmxnzYGXBHe0NsO0eUf9TvsHVq2MXvi4JmTn3x5raynodDVE/9VQmBdWFyyj9HpHZ2B4xNZ7MMy7lkw==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"csstype": "^3.0.2",
|
||||||
|
"indefinite-observable": "^2.0.1",
|
||||||
|
"is-in-browser": "^1.1.3",
|
||||||
|
"tiny-warning": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"jss-plugin-camel-case": {
|
||||||
|
"version": "10.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.6.0.tgz",
|
||||||
|
"integrity": "sha512-JdLpA3aI/npwj3nDMKk308pvnhoSzkW3PXlbgHAzfx0yHWnPPVUjPhXFtLJzgKZge8lsfkUxvYSQ3X2OYIFU6A==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"hyphenate-style-name": "^1.0.3",
|
||||||
|
"jss": "10.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"jss-plugin-default-unit": {
|
||||||
|
"version": "10.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.6.0.tgz",
|
||||||
|
"integrity": "sha512-7y4cAScMHAxvslBK2JRK37ES9UT0YfTIXWgzUWD5euvR+JR3q+o8sQKzBw7GmkQRfZijrRJKNTiSt1PBsLI9/w==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"jss": "10.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"jss-plugin-global": {
|
||||||
|
"version": "10.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.6.0.tgz",
|
||||||
|
"integrity": "sha512-I3w7ji/UXPi3VuWrTCbHG9rVCgB4yoBQLehGDTmsnDfXQb3r1l3WIdcO8JFp9m0YMmyy2CU7UOV6oPI7/Tmu+w==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"jss": "10.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"jss-plugin-nested": {
|
||||||
|
"version": "10.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.6.0.tgz",
|
||||||
|
"integrity": "sha512-fOFQWgd98H89E6aJSNkEh2fAXquC9aZcAVjSw4q4RoQ9gU++emg18encR4AT4OOIFl4lQwt5nEyBBRn9V1Rk8g==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"jss": "10.6.0",
|
||||||
|
"tiny-warning": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"jss-plugin-props-sort": {
|
||||||
|
"version": "10.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.6.0.tgz",
|
||||||
|
"integrity": "sha512-oMCe7hgho2FllNc60d9VAfdtMrZPo9n1Iu6RNa+3p9n0Bkvnv/XX5San8fTPujrTBScPqv9mOE0nWVvIaohNuw==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"jss": "10.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"jss-plugin-rule-value-function": {
|
||||||
|
"version": "10.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.6.0.tgz",
|
||||||
|
"integrity": "sha512-TKFqhRTDHN1QrPTMYRlIQUOC2FFQb271+AbnetURKlGvRl/eWLswcgHQajwuxI464uZk91sPiTtdGi7r7XaWfA==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"jss": "10.6.0",
|
||||||
|
"tiny-warning": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"jss-plugin-vendor-prefixer": {
|
||||||
|
"version": "10.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.6.0.tgz",
|
||||||
|
"integrity": "sha512-doJ7MouBXT1lypLLctCwb4nJ6lDYqrTfVS3LtXgox42Xz0gXusXIIDboeh6UwnSmox90QpVnub7au8ybrb0krQ==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"css-vendor": "^2.0.8",
|
||||||
|
"jss": "10.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"jsx-ast-utils": {
|
"jsx-ast-utils": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz",
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@material-ui/core": "^4.11.3",
|
||||||
"@reduxjs/toolkit": "^1.5.0",
|
"@reduxjs/toolkit": "^1.5.0",
|
||||||
"@testing-library/jest-dom": "^5.11.9",
|
"@testing-library/jest-dom": "^5.11.9",
|
||||||
"@testing-library/react": "^11.2.5",
|
"@testing-library/react": "^11.2.5",
|
||||||
@ -10,6 +11,7 @@
|
|||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"bootstrap": "^4.6.0",
|
"bootstrap": "^4.6.0",
|
||||||
"formik": "^2.2.6",
|
"formik": "^2.2.6",
|
||||||
|
"formik-material-ui": "^3.0.1",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-bootstrap": "^1.5.2",
|
"react-bootstrap": "^1.5.2",
|
||||||
|
@ -25,6 +25,7 @@ export const PostLogin = (formValues) => {
|
|||||||
uuid: expandedUser.sub,
|
uuid: expandedUser.sub,
|
||||||
exp: expandedUser.exp,
|
exp: expandedUser.exp,
|
||||||
username: expandedUser.username,
|
username: expandedUser.username,
|
||||||
|
admin: expandedUser.admin
|
||||||
}
|
}
|
||||||
|
|
||||||
toast.success('Successfully logged in.');
|
toast.success('Successfully logged in.');
|
||||||
@ -72,3 +73,31 @@ export const getRecentScrobbles = (id) => {
|
|||||||
return data.data;
|
return data.data;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getConfigs = () => {
|
||||||
|
return axios.get(process.env.REACT_APP_API_URL + "config", { headers: getHeaders() })
|
||||||
|
.then((data) => {
|
||||||
|
return data.data;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const postConfigs = (values, toggle) => {
|
||||||
|
if (toggle) {
|
||||||
|
values.REGISTRATION_ENABLED = "1"
|
||||||
|
} else {
|
||||||
|
values.REGISTRATION_ENABLED = "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
return axios.post(process.env.REACT_APP_API_URL + "config", values, { headers: getHeaders() })
|
||||||
|
.then((data) => {
|
||||||
|
if (data.data && data.data.message) {
|
||||||
|
toast.success(data.data.message);
|
||||||
|
} else if (data.data && data.data.error) {
|
||||||
|
toast.error(data.data.error);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
toast.error('Error updating values');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
import './App.css';
|
import { Route, Switch, withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
import Home from './Pages/Home';
|
import Home from './Pages/Home';
|
||||||
import About from './Pages/About';
|
import About from './Pages/About';
|
||||||
|
|
||||||
import Dashboard from './Pages/Dashboard';
|
import Dashboard from './Pages/Dashboard';
|
||||||
import Profile from './Pages/Profile';
|
import Profile from './Pages/Profile';
|
||||||
|
import Admin from './Pages/Admin';
|
||||||
import Login from './Pages/Login';
|
import Login from './Pages/Login';
|
||||||
import Settings from './Pages/Settings';
|
|
||||||
import Register from './Pages/Register';
|
import Register from './Pages/Register';
|
||||||
|
|
||||||
import Navigation from './Components/Navigation';
|
import Navigation from './Components/Navigation';
|
||||||
|
|
||||||
import { Route, Switch, withRouter } from 'react-router-dom';
|
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||||
|
import './App.css';
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
let boolTrue = true
|
let boolTrue = true
|
||||||
@ -24,7 +25,8 @@ const App = () => {
|
|||||||
|
|
||||||
<Route path="/dashboard" component={Dashboard} />
|
<Route path="/dashboard" component={Dashboard} />
|
||||||
<Route path="/profile" component={Profile} />
|
<Route path="/profile" component={Profile} />
|
||||||
<Route path="/settings" component={Settings} />
|
|
||||||
|
<Route path="/admin" component={Admin} />
|
||||||
|
|
||||||
<Route path="/login" component={Login} />
|
<Route path="/login" component={Login} />
|
||||||
<Route path="/register" component={Register} />
|
<Route path="/register" component={Register} />
|
||||||
|
@ -33,10 +33,14 @@ const Navigation = () => {
|
|||||||
let { user, Logout } = useContext(AuthContext);
|
let { user, Logout } = useContext(AuthContext);
|
||||||
let [collapsed, setCollapsed] = useState(true);
|
let [collapsed, setCollapsed] = useState(true);
|
||||||
|
|
||||||
|
const toggleCollapsed = () => {
|
||||||
|
setCollapsed(!collapsed)
|
||||||
|
}
|
||||||
|
|
||||||
const renderMobileNav = () => {
|
const renderMobileNav = () => {
|
||||||
return <Navbar color="dark" dark fixed="top">
|
return <Navbar color="dark" dark fixed="top">
|
||||||
<NavbarBrand className="mr-auto"><img src={logo} className="nav-logo" alt="logo" /> GoScrobble</NavbarBrand>
|
<NavbarBrand className="mr-auto"><img src={logo} className="nav-logo" alt="logo" /> GoScrobble</NavbarBrand>
|
||||||
<NavbarToggler onClick={setCollapsed(!collapsed)} className="mr-2" />
|
<NavbarToggler onClick={toggleCollapsed} className="mr-2" />
|
||||||
<Collapse isOpen={!collapsed} navbar>
|
<Collapse isOpen={!collapsed} navbar>
|
||||||
{user ?
|
{user ?
|
||||||
<Nav className="navLinkLoginMobile" navbar>
|
<Nav className="navLinkLoginMobile" navbar>
|
||||||
@ -55,6 +59,12 @@ const Navigation = () => {
|
|||||||
style={active === "profile" ? activeStyle : {}}
|
style={active === "profile" ? activeStyle : {}}
|
||||||
className="navLinkMobile"
|
className="navLinkMobile"
|
||||||
>Profile</Link>
|
>Profile</Link>
|
||||||
|
{user.admin &&
|
||||||
|
<Link
|
||||||
|
to="/admin"
|
||||||
|
style={active === "admin" ? activeStyle : {}}
|
||||||
|
className="navLink"
|
||||||
|
>Admin</Link>}
|
||||||
<Link to="/" className="navLink" onClick={Logout}>Logout</Link>
|
<Link to="/" className="navLink" onClick={Logout}>Logout</Link>
|
||||||
</Nav>
|
</Nav>
|
||||||
: <Nav className="navLinkLoginMobile" navbar>
|
: <Nav className="navLinkLoginMobile" navbar>
|
||||||
@ -126,7 +136,13 @@ const Navigation = () => {
|
|||||||
style={active === "profile" ? activeStyle : {}}
|
style={active === "profile" ? activeStyle : {}}
|
||||||
className="navLink"
|
className="navLink"
|
||||||
>{user.username}</Link>
|
>{user.username}</Link>
|
||||||
<Link to="/" className="navLink" onClick={Logout}>Logout</Link>
|
{user.admin &&
|
||||||
|
<Link
|
||||||
|
to="/admin"
|
||||||
|
style={active === "admin" ? activeStyle : {}}
|
||||||
|
className="navLink"
|
||||||
|
>Admin</Link>}
|
||||||
|
<Link to="/admin" className="navLink" onClick={Logout}>Logout</Link>
|
||||||
</div>
|
</div>
|
||||||
:
|
:
|
||||||
<div className="navLinkLogin">
|
<div className="navLinkLogin">
|
||||||
|
15
web/src/Pages/Admin.css
Normal file
15
web/src/Pages/Admin.css
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
.adminBody {
|
||||||
|
padding: 20px 5px 5px 5px;
|
||||||
|
font-size: 16pt;
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adminFields {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin {
|
||||||
|
height: 50px;
|
||||||
|
width: 100%;
|
||||||
|
margin-top:-5px;
|
||||||
|
}
|
107
web/src/Pages/Admin.js
Normal file
107
web/src/Pages/Admin.js
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import React, { useContext, useState, useEffect } from 'react';
|
||||||
|
import '../App.css';
|
||||||
|
import './Login.css';
|
||||||
|
import { Button } from 'reactstrap';
|
||||||
|
import { Formik, Form, Field } from 'formik';
|
||||||
|
import ScaleLoader from 'react-spinners/ScaleLoader';
|
||||||
|
import AuthContext from '../Contexts/AuthContext';
|
||||||
|
import { Switch } from 'formik-material-ui';
|
||||||
|
import { getConfigs, postConfigs } from '../Api/index'
|
||||||
|
|
||||||
|
const Admin = () => {
|
||||||
|
const { user } = useContext(AuthContext);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [configs, setConfigs] = useState({})
|
||||||
|
const [toggle, setToggle] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getConfigs()
|
||||||
|
.then(data => {
|
||||||
|
setConfigs(data.configs);
|
||||||
|
setToggle(data.configs.REGISTRATION_ENABLED === "1")
|
||||||
|
setLoading(false);
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!user || !user.admin) {
|
||||||
|
return (
|
||||||
|
<div className="pageWrapper">
|
||||||
|
<h1>Unauthorized</h1>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className="pageWrapper">
|
||||||
|
<ScaleLoader color="#6AD7E5" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleToggle = () => {
|
||||||
|
setToggle(!toggle);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="pageWrapper">
|
||||||
|
<h1>
|
||||||
|
Admin Panel
|
||||||
|
</h1>
|
||||||
|
<div className="loginBody">
|
||||||
|
<Formik
|
||||||
|
initialValues={configs}
|
||||||
|
onSubmit={(values) => postConfigs(values, toggle)}
|
||||||
|
>
|
||||||
|
<Form><br/>
|
||||||
|
<label>
|
||||||
|
<Field
|
||||||
|
type="checkbox"
|
||||||
|
name="REGISTRATION_ENABLED"
|
||||||
|
onChange={handleToggle}
|
||||||
|
component={Switch}
|
||||||
|
checked={toggle}
|
||||||
|
value={toggle}
|
||||||
|
/>
|
||||||
|
Registration Enabled
|
||||||
|
</label><br/><br/>
|
||||||
|
<label>
|
||||||
|
LastFM Api Key<br/>
|
||||||
|
<Field
|
||||||
|
name="LASTFM_API_KEY"
|
||||||
|
type="text"
|
||||||
|
className="loginFields"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<br/>
|
||||||
|
<label>
|
||||||
|
Spotify App ID<br/>
|
||||||
|
<Field
|
||||||
|
name="SPOTIFY_APP_ID"
|
||||||
|
type="text"
|
||||||
|
className="loginFields"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Spotify App Secret<br/>
|
||||||
|
<Field
|
||||||
|
name="SPOTIFY_APP_SECRET"
|
||||||
|
type="text"
|
||||||
|
className="loginFields"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<br/><br/>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
type="submit"
|
||||||
|
className="loginButton"
|
||||||
|
disabled={loading}
|
||||||
|
>Update</Button>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Admin;
|
@ -10,7 +10,7 @@ import AuthContext from '../Contexts/AuthContext';
|
|||||||
const Dashboard = () => {
|
const Dashboard = () => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
let { user } = useContext(AuthContext);
|
let { user } = useContext(AuthContext);
|
||||||
let [isLoading, setIsLoading] = useState(true);
|
let [loading, setLoading] = useState(true);
|
||||||
let [dashboardData, setDashboardData] = useState({});
|
let [dashboardData, setDashboardData] = useState({});
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
@ -24,7 +24,7 @@ const Dashboard = () => {
|
|||||||
getRecentScrobbles(user.uuid)
|
getRecentScrobbles(user.uuid)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
setDashboardData(data);
|
setDashboardData(data);
|
||||||
setIsLoading(false);
|
setLoading(false);
|
||||||
})
|
})
|
||||||
}, [user])
|
}, [user])
|
||||||
|
|
||||||
@ -33,8 +33,8 @@ const Dashboard = () => {
|
|||||||
<h1>
|
<h1>
|
||||||
Dashboard!
|
Dashboard!
|
||||||
</h1>
|
</h1>
|
||||||
{isLoading
|
{loading
|
||||||
? <ScaleLoader color="#FFF" size={60} />
|
? <ScaleLoader color="#6AD7E5" size={60} />
|
||||||
: <ScrobbleTable data={dashboardData} />
|
: <ScrobbleTable data={dashboardData} />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user