mirror of
https://github.com/idanoo/GoScrobble
synced 2025-07-01 13:42:20 +00:00
0.0.12
- Add client TZ support + Selectable on user page - Move token auth to GET ?key=XYZ for wider webhook support - Add Multiscrobbler support - Add /api/v1/serverinfo for version information
This commit is contained in:
parent
9866dea36b
commit
67e43b8984
14 changed files with 415 additions and 114 deletions
|
@ -11,6 +11,10 @@ type Config struct {
|
|||
Setting map[string]string `json:"configs"`
|
||||
}
|
||||
|
||||
type ServerInfo struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
func getAllConfigs() (Config, error) {
|
||||
config := Config{}
|
||||
configs := make(map[string]string)
|
||||
|
|
|
@ -4,63 +4,70 @@ import (
|
|||
"database/sql"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type MultiScrobblerInput struct {
|
||||
Artists []string `json:"artists"`
|
||||
Album string `json:"album"`
|
||||
Track string `json:"track"`
|
||||
PlayedAt time.Time `json:"playDate"`
|
||||
Duration string `json:"duration"`
|
||||
}
|
||||
|
||||
// ParseMultiScrobblerInput - Transform API data
|
||||
func ParseMultiScrobblerInput(userUUID string, data map[string]interface{}, ip net.IP, tx *sql.Tx) error {
|
||||
// Debugging
|
||||
fmt.Printf("%+v", data)
|
||||
|
||||
// if data["ItemType"] != "Audio" {
|
||||
// return errors.New("Media type not audio")
|
||||
// }
|
||||
|
||||
// // Safety Checks
|
||||
// if data["Artist"] == nil {
|
||||
// if data["artists"] == nil {
|
||||
// return errors.New("Missing artist data")
|
||||
// }
|
||||
|
||||
// if data["Album"] == nil {
|
||||
// if data["album"] == nil {
|
||||
// return errors.New("Missing album data")
|
||||
// }
|
||||
|
||||
// if data["Name"] == nil {
|
||||
// if data["track"] == 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 {
|
||||
// log.Printf("%+v", err)
|
||||
// return errors.New("Failed to map artist")
|
||||
// }
|
||||
// // Insert track artists
|
||||
// for _, artist := range data["artists"] {
|
||||
// artist, err := insertArtist(artist.Name, "", artist.ID.String(), tx)
|
||||
|
||||
// if err != nil {
|
||||
// log.Printf("%+v", err)
|
||||
// return errors.New("Failed to map artist: " + artist.Name)
|
||||
// }
|
||||
// artists = append(artists, artist.Uuid)
|
||||
// }
|
||||
// // Insert album if not exist
|
||||
// artists := []string{artist.Uuid}
|
||||
// album, err := insertAlbum(fmt.Sprintf("%s", data["Album"]), fmt.Sprintf("%s", data["Provider_musicbrainzalbum"]), artists, tx)
|
||||
// album, err := insertAlbum(fmt.Sprintf("%s", data["Album"]), fmt.Sprintf("%s", data["Provider_musicbrainzalbum"]), "", artists, tx)
|
||||
// if err != nil {
|
||||
// log.Printf("%+v", err)
|
||||
// return errors.New("Failed to map album")
|
||||
// }
|
||||
|
||||
// // Insert album if not exist
|
||||
// track, err := insertTrack(fmt.Sprintf("%s", data["Name"]), fmt.Sprintf("%s", data["Provider_musicbrainztrack"]), album.Uuid, artists, tx)
|
||||
// // Insert track if not exist
|
||||
// length := timestampToSeconds(fmt.Sprintf("%s", data["RunTime"]))
|
||||
// track, err := insertTrack(fmt.Sprintf("%s", data["Name"]), length, fmt.Sprintf("%s", data["Provider_musicbrainztrack"]), "", album.Uuid, artists, tx)
|
||||
// if err != nil {
|
||||
// log.Printf("%+v", err)
|
||||
// return errors.New("Failed to map track")
|
||||
// }
|
||||
|
||||
// // Insert album if not exist
|
||||
// err = insertScrobble(userUUID, track.Uuid, "jellyfin", ip, tx)
|
||||
// // Insert scrobble if not exist
|
||||
// timestamp := time.Now()
|
||||
// fmt.Println(timestamp)
|
||||
// err = insertScrobble(userUUID, track.Uuid, "jellyfin", timestamp, ip, tx)
|
||||
// if err != nil {
|
||||
// log.Printf("%+v", err)
|
||||
// return errors.New("Failed to map track")
|
||||
// }
|
||||
|
||||
// _ = album
|
||||
// _ = artist
|
||||
// _ = track
|
||||
|
||||
// Insert track if not exist
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -124,7 +124,6 @@ func (user *User) updateSpotifyPlaydata() {
|
|||
break
|
||||
}
|
||||
tx.Commit()
|
||||
fmt.Printf("Updated spotify track: %+v", v.Track.Name)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ func HandleRequests(port string) {
|
|||
|
||||
// JWT Auth - Own profile only (Uses uuid in JWT)
|
||||
v1.HandleFunc("/user", limitMiddleware(jwtMiddleware(fetchUser), lightLimiter)).Methods("GET")
|
||||
// v1.HandleFunc("/user", jwtMiddleware(fetchScrobbleResponse)).Methods("PATCH")
|
||||
v1.HandleFunc("/user", limitMiddleware(jwtMiddleware(patchUser), lightLimiter)).Methods("PATCH")
|
||||
v1.HandleFunc("/user/spotify", limitMiddleware(jwtMiddleware(getSpotifyClientID), lightLimiter)).Methods("GET")
|
||||
v1.HandleFunc("/user/spotify", limitMiddleware(jwtMiddleware(deleteSpotifyLink), lightLimiter)).Methods("DELETE")
|
||||
v1.HandleFunc("/user/{uuid}/scrobbles", jwtMiddleware(fetchScrobbleResponse)).Methods("GET")
|
||||
|
@ -55,6 +55,7 @@ func HandleRequests(port string) {
|
|||
v1.HandleFunc("/login", limitMiddleware(handleLogin, standardLimiter)).Methods("POST")
|
||||
v1.HandleFunc("/sendreset", limitMiddleware(handleSendReset, heavyLimiter)).Methods("POST")
|
||||
v1.HandleFunc("/resetpassword", limitMiddleware(handleResetPassword, heavyLimiter)).Methods("POST")
|
||||
v1.HandleFunc("/serverinfo", fetchServerInfo).Methods("GET")
|
||||
|
||||
// Redirect from Spotify Oauth
|
||||
v1.HandleFunc("/link/spotify", limitMiddleware(postSpotifyReponse, lightLimiter))
|
||||
|
@ -67,11 +68,12 @@ func HandleRequests(port string) {
|
|||
r.PathPrefix("/").Handler(spa)
|
||||
|
||||
c := cors.New(cors.Options{
|
||||
// Grrrr CORS. To clean up at a later date
|
||||
AllowedOrigins: []string{"*"},
|
||||
AllowCredentials: true,
|
||||
AllowedMethods: []string{"GET", "POST", "PATCH", "DELETE"},
|
||||
AllowedHeaders: []string{"*"},
|
||||
})
|
||||
|
||||
handler := c.Handler(r)
|
||||
|
||||
// Serve it up!
|
||||
|
@ -219,6 +221,7 @@ func handleResetPassword(w http.ResponseWriter, r *http.Request) {
|
|||
// serveEndpoint - API stuffs
|
||||
func handleIngress(w http.ResponseWriter, r *http.Request, userUuid string) {
|
||||
bodyJson, err := decodeJson(r.Body)
|
||||
fmt.Println(err)
|
||||
if err != nil {
|
||||
throwInvalidJson(w)
|
||||
return
|
||||
|
@ -291,6 +294,29 @@ func fetchUser(w http.ResponseWriter, r *http.Request, jwtUser string, reqUser s
|
|||
w.Write(json)
|
||||
}
|
||||
|
||||
// patchUser - Update specific values
|
||||
func patchUser(w http.ResponseWriter, r *http.Request, jwtUser string, reqUser string) {
|
||||
userFull, err := getUser(jwtUser)
|
||||
if err != nil {
|
||||
throwOkError(w, "Failed to fetch user information")
|
||||
return
|
||||
}
|
||||
|
||||
bodyJson, _ := decodeJson(r.Body)
|
||||
|
||||
ip := getUserIp(r)
|
||||
for k, v := range bodyJson {
|
||||
val := fmt.Sprintf("%s", v)
|
||||
if k == "timezone" {
|
||||
if isValidTimezone(val) {
|
||||
userFull.updateUser("timezone", val, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throwOkMessage(w, "User updated successfully")
|
||||
}
|
||||
|
||||
// fetchScrobbles - Return an array of scrobbles
|
||||
func fetchScrobbleResponse(w http.ResponseWriter, r *http.Request, jwtUser string, reqUser string) {
|
||||
resp, err := fetchScrobblesForUser(reqUser, 100, 1)
|
||||
|
@ -408,3 +434,13 @@ func deleteSpotifyLink(w http.ResponseWriter, r *http.Request, u string, v strin
|
|||
|
||||
throwOkMessage(w, "Spotify account successfully unlinked")
|
||||
}
|
||||
|
||||
func fetchServerInfo(w http.ResponseWriter, r *http.Request) {
|
||||
info := ServerInfo{
|
||||
Version: "0.0.11",
|
||||
}
|
||||
|
||||
js, _ := json.Marshal(&info)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(js)
|
||||
}
|
||||
|
|
|
@ -20,14 +20,21 @@ var lightLimiter = NewIPRateLimiter(10, 10)
|
|||
// tokenMiddleware - Validates token to a user
|
||||
func tokenMiddleware(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)
|
||||
if authToken == "" {
|
||||
key := ""
|
||||
urlParams := r.URL.Query()
|
||||
if val, ok := urlParams["key"]; ok {
|
||||
key = val[0]
|
||||
} else {
|
||||
throwUnauthorized(w, "No key parameter provided")
|
||||
return
|
||||
}
|
||||
|
||||
if key == "" {
|
||||
throwUnauthorized(w, "A token is required")
|
||||
return
|
||||
}
|
||||
|
||||
userUuid, err := getUserUuidForToken(authToken)
|
||||
userUuid, err := getUserUuidForToken(key)
|
||||
if err != nil {
|
||||
throwUnauthorized(w, err.Error())
|
||||
return
|
||||
|
|
|
@ -156,14 +156,14 @@ func insertUser(username string, email string, password []byte, ip net.IP) error
|
|||
return err
|
||||
}
|
||||
|
||||
func updateUser(uuid string, field string, value string, ip net.IP) error {
|
||||
_, err := db.Exec("UPDATE users SET `"+field+"` = ?, modified_at = NOW(), modified_ip = ? WHERE uuid = ?", value, uuid, ip)
|
||||
func (user *User) updateUser(field string, value string, ip net.IP) error {
|
||||
_, err := db.Exec("UPDATE users SET `"+field+"` = ?, modified_at = NOW(), modified_ip = ? WHERE uuid = UUID_TO_BIN(?, true)", value, ip, user.UUID)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func updateUserDirect(uuid string, field string, value string) error {
|
||||
_, err := db.Exec("UPDATE users SET `"+field+"` = ? WHERE uuid = ?", value, uuid)
|
||||
func (user *User) updateUserDirect(field string, value string) error {
|
||||
_, err := db.Exec("UPDATE users SET `"+field+"` = ? WHERE uuid = UUID_TO_BIN(?, true)", value, user.UUID)
|
||||
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
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])?)*$")
|
||||
|
@ -79,6 +80,10 @@ func getUserIp(r *http.Request) net.IP {
|
|||
}
|
||||
}
|
||||
|
||||
if host == "" {
|
||||
host = "0.0.0.0"
|
||||
}
|
||||
|
||||
ip = net.ParseIP(host)
|
||||
return ip
|
||||
}
|
||||
|
@ -144,3 +149,8 @@ func filterSlice(s []string) []string {
|
|||
fmt.Printf("RESTULS: %+v", result)
|
||||
return result
|
||||
}
|
||||
|
||||
func isValidTimezone(tz string) bool {
|
||||
_, err := time.LoadLocation(tz)
|
||||
return err == nil
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue