This commit is contained in:
Daniel Mason 2022-01-05 20:58:05 +13:00
parent 6c52aa7d78
commit 89b20fbf6f
17 changed files with 233 additions and 127 deletions

View File

@ -21,6 +21,7 @@ This assumes you have goscrobble-api and goscrobble-web cloned in the same folde
Access API @ http://127.0.0.1:42069/api/v1
Access frontend @ http://127.0.0.1:3000
pgAdmin @ http://127.0.0.1:5050 (admin@admin.com / root)
## Prod deployment
cp .env.production .env # Fill in the blanks

View File

@ -26,6 +26,8 @@ services:
image: postgres:14.1
volumes:
- database-data:/var/lib/postgresql/data/
ports:
- 5432:5432
restart: always
environment:
- POSTGRES_USER=goscrobble
@ -37,6 +39,16 @@ services:
ports:
- "127.0.0.1:6379:6379"
pgadmin:
container_name: pgadmin4_container
image: dpage/pgadmin4
restart: always
environment:
PGADMIN_DEFAULT_EMAIL: admin@admin.com
PGADMIN_DEFAULT_PASSWORD: root
ports:
- "5050:80"
volumes:
database-data:
data:

95
docs/migrate.php Normal file
View File

@ -0,0 +1,95 @@
<?php
// Temp script to migrate old data from MySQL to PostgreSQL
// PLEASE RUN ALL MIGRATIONS ON POSTGRES BEFORE RUNNING THIS.
echo PHP_EOL . "Loading connections...";
global $mysqli;
$mysqli = new mysqli('172.27.138.37', 'goscrobble', 'X9u7jdfy', 'goscrobble');
if ($mysqli->connect_errno) {
die("Failed to connect to MySQL");
}
global $postgres;
$postgres = new PDO("pgsql:host=127.0.0.1;port=5432;dbname=goscrobble;user=goscrobble;password=supersecretdatabasepassword1");
function getArray($query): array
{
global $mysqli;
if (!$result = $mysqli->query($query)) {
die($mysqli->error);
}
while ($row = $result->fetch_assoc()) {
$data[] = $row;
}
return $data;
}
echo PHP_EOL . "Skipping schema_migrations (Already exists)";
echo PHP_EOL . "Migrating config";
// $config = getArray("SELECT * FROM config");
// $update = $postgres->prepare("UPDATE config SET value = ? WHERE key = ?");
// foreach ($config as $row) {
// $update->execute([
// $row['value'],
// $row['key'],
// ]);
// }
echo PHP_EOL . "Migrating users";
$users = getArray("SELECT
BIN_TO_UUID(uuid, true) as uuid,
created_at,
inet_ntoa(conv(created_ip, 16, 10)) as created_ip,
modified_at,
inet_ntoa(modified_ip) as modified_ip,
username,
password,
email,
verified,
active,
admin,
`mod`,
token,
private,
timezone
FROM users;");
$update = $postgres->prepare("INSERT INTO users (uuid, created_at, created_ip, modified_at, modified_ip, username, password, email, verified, active, admin, mod, token, private, timezone) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
foreach ($users as $row) {
echo PHP_EOL . $row['username'];
$update->execute([
$row['uuid'],
$row['created_at'],
$row['created_ip'],
$row['modified_at'],
$row['modified_ip'],
$row['username'],
$row['password'],
$row['email'],
$row['verified'],
$row['active'],
$row['admin'],
$row['mod'],
$row['token'],
$row['private'],
$row['timezone'],
]);
}
// echo PHP_EOL . "Migrating albums";
// echo PHP_EOL . "Migrating artists";
// echo PHP_EOL . "Migrating tracks";
// echo PHP_EOL . "Migrating genres";
// echo PHP_EOL . "Migrating links";
// echo PHP_EOL . "Migrating oauth_tokens";
// echo PHP_EOL . "Migrating refresh_tokens";
// echo PHP_EOL . "Migrating resettoken";
// echo PHP_EOL . "Migrating album_artist";
// echo PHP_EOL . "Migrating track_album";
// echo PHP_EOL . "Migrating track_artist";
// echo PHP_EOL . "Migrating scrobbles";

View File

@ -73,7 +73,7 @@ func insertAlbum(name string, mbid string, spotifyId string, artists []string, i
func getAlbumByCol(col string, val string, tx *sql.Tx) Album {
var album Album
err := tx.QueryRow(
"SELECT uuid, name, IFNULL(desc, ''), IFNULL(img,''), mbid, spotify_id FROM albums WHERE "+col+" = $1",
`SELECT uuid, name, COALESCE(desc, ''), COALESCE(img,''), mbid, spotify_id FROM albums WHERE "`+col+`" = $1`,
val).Scan(&album.UUID, &album.Name, &album.Desc, &album.Img, &album.MusicBrainzID, &album.SpotifyID)
if err != nil {
@ -92,8 +92,8 @@ func insertNewAlbum(album *Album, name string, mbid string, spotifyId string, im
album.SpotifyID = spotifyId
album.Img = img
_, err := tx.Exec("INSERT INTO albums (uuid, name, mbid, spotify_id, img) "+
"VALUES ($1,$2,$3,$4,$5)", album.UUID, album.Name, album.MusicBrainzID, album.SpotifyID, album.Img)
_, err := tx.Exec(`INSERT INTO albums (uuid, name, mbid, spotify_id, img) `+
`VALUES ($1,$2,$3,$4,$5)`, album.UUID, album.Name, album.MusicBrainzID, album.SpotifyID, album.Img)
return err
}
@ -101,8 +101,8 @@ func insertNewAlbum(album *Album, name string, mbid string, spotifyId string, im
func (album *Album) linkAlbumToArtists(artists []string, tx *sql.Tx) error {
var err error
for _, artist := range artists {
_, err = tx.Exec("INSERT INTO album_artist (album, artist) "+
"VALUES ($1,$2)", album.UUID, artist)
_, err = tx.Exec(`INSERT INTO album_artist (album, artist) `+
`VALUES ($1,$2)`, album.UUID, artist)
if err != nil {
return err
}
@ -112,14 +112,14 @@ func (album *Album) linkAlbumToArtists(artists []string, tx *sql.Tx) error {
}
func (album *Album) updateAlbum(col string, val string, tx *sql.Tx) error {
_, err := tx.Exec("UPDATE albums SET "+col+" = $1 WHERE uuid = $2", val, album.UUID)
_, err := tx.Exec(`UPDATE albums SET "`+col+`" = $1 WHERE uuid = $2`, val, album.UUID)
return err
}
func getAlbumByUUID(uuid string) (Album, error) {
var album Album
err := db.QueryRow("SELECT uuid, name, IFNULL(desc,''), IFNULL(img,''), mbid, spotify_id FROM albums WHERE uuid = $1",
err := db.QueryRow(`SELECT uuid, name, COALESCE(desc,''), COALESCE(img,''), mbid, spotify_id FROM albums WHERE uuid = $1`,
uuid).Scan(&album.UUID, &album.Name, &album.Desc, &album.Img, &album.MusicBrainzID, &album.SpotifyID)
if err != nil {

View File

@ -83,7 +83,7 @@ func insertArtist(name string, mbid string, spotifyId string, img string, tx *sq
func getArtistByCol(col string, val string, tx *sql.Tx) Artist {
var artist Artist
err := tx.QueryRow(
"SELECT uuid, name, IFNULL(desc,''), IFNULL(img,''), mbid, spotify_id FROM artists WHERE "+col+" = $1",
`SELECT uuid, name, COALESCE(desc,''), COALESCE(img,''), mbid, spotify_id FROM artists WHERE "`+col+`" = $1`,
val).Scan(&artist.UUID, &artist.Name, &artist.Desc, &artist.Img, &artist.MusicBrainzID, &artist.SpotifyID)
if err != nil {
@ -102,21 +102,21 @@ func insertNewArtist(artist *Artist, name string, mbid string, spotifyId string,
artist.SpotifyID = spotifyId
artist.Img = img
_, err := tx.Exec("INSERT INTO artists (uuid, name, mbid, spotify_id, img) "+
"VALUES ($1,$2,$3,$4,$5)", artist.UUID, artist.Name, artist.MusicBrainzID, artist.SpotifyID, artist.Img)
_, err := tx.Exec(`INSERT INTO artists (uuid, name, mbid, spotify_id, img) `+
`VALUES ($1,$2,$3,$4,$5)`, artist.UUID, artist.Name, artist.MusicBrainzID, artist.SpotifyID, artist.Img)
return err
}
func (artist *Artist) updateArtist(col string, val string, tx *sql.Tx) error {
_, err := tx.Exec("UPDATE artists SET "+col+" = $1 WHERE uuid = $2", val, artist.UUID)
_, err := tx.Exec(`UPDATE artists SET "`+col+`" = $1 WHERE uuid = $2`, val, artist.UUID)
return err
}
func getArtistByUUID(uuid string) (Artist, error) {
var artist Artist
err := db.QueryRow("SELECT uuid, name, IFNULL(desc, ''), IFNULL(img,''), mbid, spotify_id FROM artists WHERE uuid = $1",
err := db.QueryRow(`SELECT uuid, name, COALESCE(desc, ''), COALESCE(img,''), mbid, spotify_id FROM artists WHERE uuid = $1`,
uuid).Scan(&artist.UUID, &artist.Name, &artist.Desc, &artist.Img, &artist.MusicBrainzID, &artist.SpotifyID)
if err == sql.ErrNoRows {
@ -129,15 +129,16 @@ func getArtistByUUID(uuid string) (Artist, error) {
func getTopArtists(userUuid string) (TopArtists, error) {
var topArtist TopArtists
rows, err := db.Query("SELECT artists.uuid, artists.name, IFNULL(artists.uuid,''), count(*) "+
"FROM scrobbles "+
"JOIN tracks ON tracks.uuid = scrobbles.track "+
"JOIN track_artist ON track_artist.track = tracks.uuid "+
"JOIN artists ON track_artist.artist = artists.uuid "+
"WHERE scrobbles.user = $1 "+
"GROUP BY artists.uuid "+
"ORDER BY count(*) DESC "+
"LIMIT 14;",
log.Println(userUuid)
rows, err := db.Query(`SELECT artists.uuid, artists.name, COALESCE(artists.uuid,''), count(*) `+
`FROM scrobbles `+
`JOIN tracks ON tracks.uuid = scrobbles.track `+
`JOIN track_artist ON track_artist.track = tracks.uuid `+
`JOIN artists ON track_artist.artist = artists.uuid `+
`WHERE scrobbles."user" = $1 `+
`GROUP BY artists.uuid `+
`ORDER BY count(*) DESC `+
`LIMIT 14;`,
userUuid)
if err != nil {
log.Printf("Failed to fetch top artist: %+v", err)

View File

@ -20,7 +20,7 @@ func getAllConfigs() (Config, error) {
config := Config{}
configs := make(map[string]string)
rows, err := db.Query("SELECT key, value FROM config")
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")
@ -53,7 +53,7 @@ func getAllConfigs() (Config, error) {
}
func updateConfigValue(key string, value string) error {
_, err := db.Exec("UPDATE config SET value = $1 WHERE key = $2", value, key)
_, err := db.Exec(`UPDATE config SET value = $1 WHERE key = $2`, value, key)
if err != nil {
fmt.Printf("Failed to update config: %+v", err)
return errors.New("Failed to update config value.")
@ -65,8 +65,7 @@ func updateConfigValue(key string, value string) error {
func getConfigValue(key string) (string, error) {
var value string
err := db.QueryRow("SELECT value FROM config "+
"WHERE key = $1",
err := db.QueryRow(`SELECT value FROM config WHERE key = $1`,
key).Scan(&value)
if err == sql.ErrNoRows {

View File

@ -13,7 +13,7 @@ type Genre struct {
func getGenreByUUID(uuid string) Genre {
var genre Genre
err := db.QueryRow(
"SELECT uuid, name FROM artists WHERE uuid = $1",
`SELECT uuid, name FROM artists WHERE uuid = $1`,
uuid).Scan(&genre.UUID, &genre.Name)
if err != nil {
@ -28,7 +28,7 @@ func getGenreByUUID(uuid string) Genre {
func getGenreByName(name string) Genre {
var genre Genre
err := db.QueryRow(
"SELECT uuid, name FROM artists WHERE name = $1",
`SELECT uuid, name FROM artists WHERE name = $1`,
name).Scan(&genre.UUID, &genre.Name)
if err != nil {
@ -41,7 +41,7 @@ func getGenreByName(name string) Genre {
}
func (genre *Genre) updateGenreName(name string, value string) error {
_, err := db.Exec("UPDATE genres SET name = $1 WHERE uuid = $2", name, genre.UUID)
_, err := db.Exec(`UPDATE genres SET name = $1 WHERE uuid = $2`, name, genre.UUID)
return err
}

View File

@ -17,7 +17,7 @@ import (
// updateSpotifyData - Pull data for all users
func updateSpotifyData() {
// Lets ignore if not configured
val, _ := getConfigValue("SPOTIFY_APP_SECRET")
val, _ := getConfigValue("SPOTIFY_API_SECRET")
if val == "" {
return
}
@ -35,12 +35,12 @@ func updateSpotifyData() {
}
func getSpotifyAuthHandler() spotify.Authenticator {
appId, _ := getConfigValue("SPOTIFY_APP_ID")
appSecret, _ := getConfigValue("SPOTIFY_APP_SECRET")
appId, _ := getConfigValue("SPOTIFY_API_ID")
appSecret, _ := getConfigValue("SPOTIFY_API_SECRET")
redirectUrl := os.Getenv("GOSCROBBLE_DOMAIN") + "/api/v1/link/spotify"
if redirectUrl == "http://localhost:3000/api/v1/link/spotify" {
// Handle backend on a different port
// Handle backend on a different port if running in dev-env
redirectUrl = "http://localhost:42069/api/v1/link/spotify"
}
@ -217,7 +217,7 @@ func ParseSpotifyInput(userUUID string, data spotify.RecentlyPlayedItem, client
// updateImageDataFromSpotify update artist/album images from spotify ;D
func (user *User) updateImageDataFromSpotify() error {
// Check that data is set before we attempt to pull
val, _ := getConfigValue("SPOTIFY_APP_SECRET")
val, _ := getConfigValue("SPOTIFY_API_SECRET")
if val == "" {
return nil
}
@ -238,7 +238,7 @@ func (user *User) updateImageDataFromSpotify() error {
client := auth.NewClient(token)
client.AutoRetry = true
rows, err := db.Query("SELECT uuid, name FROM artists WHERE IFNULL(img,'') NOT IN ('pending', 'complete') LIMIT 100")
rows, err := db.Query(`SELECT uuid, name FROM artists WHERE COALESCE(img,'') NOT IN ('pending', 'complete') LIMIT 100`)
if err != nil {
log.Printf("Failed to fetch config: %+v", err)
return errors.New("Failed to fetch artists")
@ -282,7 +282,7 @@ func (user *User) updateImageDataFromSpotify() error {
}
tx.Commit()
rows, err = db.Query("SELECT uuid, name FROM albums WHERE IFNULL(img,'') NOT IN ('pending', 'complete') LIMIT 100")
rows, err = db.Query("SELECT uuid, name FROM albums WHERE COALESCE(img,'') NOT IN ('pending', 'complete') LIMIT 100")
if err != nil {
log.Printf("Failed to fetch config: %+v", err)
return errors.New("Failed to fetch artists")

View File

@ -20,8 +20,8 @@ type OauthToken struct {
func getOauthToken(userUuid string, service string) (OauthToken, error) {
var oauth OauthToken
err := db.QueryRow("SELECT user, service, access_token, refresh_token, expiry, username, last_synced, url FROM oauth_tokens "+
"WHERE user = $1 AND service = $2",
err := db.QueryRow(`SELECT "user", service, access_token, refresh_token, expiry, username, last_synced, url FROM oauth_tokens `+
`WHERE "user" = $1 AND service = $2`,
userUuid, service).Scan(&oauth.UserUUID, &oauth.Service, &oauth.AccessToken, &oauth.RefreshToken, &oauth.Expiry, &oauth.Username, &oauth.LastSynced, &oauth.URL)
if err == sql.ErrNoRows {
@ -32,14 +32,14 @@ func getOauthToken(userUuid string, service string) (OauthToken, error) {
}
func insertOauthToken(userUuid string, service string, token string, refresh string, expiry time.Time, username string, lastSynced time.Time, url string) error {
_, err := db.Exec("REPLACE INTO oauth_tokens (user, service, access_token, refresh_token, expiry, username, last_synced, url) "+
"VALUES ($1,$2,$3,$4,$5,$6,$7,$8)", userUuid, service, token, refresh, expiry, username, lastSynced, url)
_, err := db.Exec(`REPLACE INTO oauth_tokens ("user", service, access_token, refresh_token, expiry, username, last_synced, url) `+
`VALUES ($1,$2,$3,$4,$5,$6,$7,$8)`, userUuid, service, token, refresh, expiry, username, lastSynced, url)
return err
}
func removeOauthToken(userUuid string, service string) error {
_, err := db.Exec("DELETE FROM oauth_tokens WHERE user = $1 AND service = $2", userUuid, service)
_, err := db.Exec(`DELETE FROM oauth_tokens WHERE "user" = $1 AND service = $2`, userUuid, service)
return err
}

View File

@ -58,8 +58,7 @@ func getScrobblesForUser(userUuid string, limit int, page int) (ScrobbleResponse
var count int
// Yeah this isn't great. But for now.. it works! Cache later
total, err := getDbCount(
"SELECT COUNT(*) FROM scrobbles WHERE user = $1", userUuid)
total, err := getDbCount(`SELECT COUNT(*) FROM scrobbles WHERE "user" = $1`, userUuid)
if err != nil {
log.Printf("Failed to fetch scrobble count: %+v", err)
@ -67,16 +66,16 @@ func getScrobblesForUser(userUuid string, limit int, page int) (ScrobbleResponse
}
rows, err := db.Query(
"SELECT scrobbles.uuid, scrobbles.created_at, artists.uuid, artists.name, albums.name, tracks.uuid, tracks.name, scrobbles.source FROM scrobbles "+
"JOIN tracks ON scrobbles.track = tracks.uuid "+
"JOIN track_artist ON track_artist.track = tracks.uuid "+
"JOIN track_album ON track_album.track = tracks.uuid "+
"JOIN artists ON track_artist.artist = artists.uuid "+
"JOIN albums ON track_album.album = albums.uuid "+
"JOIN users ON scrobbles.user = users.uuid "+
"WHERE user = $1 "+
"GROUP BY scrobbles.uuid, albums.uuid "+
"ORDER BY scrobbles.created_at DESC LIMIT $2",
`SELECT scrobbles.uuid, scrobbles.created_at, artists.uuid, artists.name, albums.name, tracks.uuid, tracks.name, scrobbles.source FROM scrobbles `+
`JOIN tracks ON scrobbles.track = tracks.uuid `+
`JOIN track_artist ON track_artist.track = tracks.uuid `+
`JOIN track_album ON track_album.track = tracks.uuid `+
`JOIN artists ON track_artist.artist = artists.uuid `+
`JOIN albums ON track_album.album = albums.uuid `+
`JOIN users ON scrobbles.user = users.uuid `+
`WHERE "user" = $1 `+
`GROUP BY scrobbles.uuid, albums.uuid `+
`ORDER BY scrobbles.created_at DESC LIMIT $2`,
userUuid, limit)
if err != nil {

View File

@ -602,6 +602,8 @@ func getArtists(w http.ResponseWriter, r *http.Request) {
return
}
log.Println(uuid)
track, err := getTopArtists(uuid)
if err != nil {
throwOkError(w, err.Error())
@ -702,7 +704,7 @@ func postSpotifyReponse(w http.ResponseWriter, r *http.Request) {
// getSpotifyClientID - Returns public spotify APP ID
func getSpotifyClientID(w http.ResponseWriter, r *http.Request, claims CustomClaims, v string) {
key, err := getConfigValue("SPOTIFY_APP_ID")
key, err := getConfigValue("SPOTIFY_API_ID")
if err != nil {
throwOkError(w, "Failed to get Spotify ID")
return

View File

@ -53,25 +53,25 @@ func getAllStats() (StatsRequest, error) {
statsReq := StatsRequest{}
var err error
statsReq.Users, err = getDbCount("SELECT COUNT(*) FROM users WHERE active = true")
statsReq.Users, err = getDbCount(`SELECT COUNT(*) FROM users WHERE active = true`)
if err != nil {
log.Printf("Failed to fetch user count: %+v", err)
return statsReq, errors.New("Failed to fetch stats")
}
statsReq.Scrobbles, err = getDbCount("SELECT COUNT(*) FROM scrobbles")
statsReq.Scrobbles, err = getDbCount(`SELECT COUNT(*) FROM scrobbles`)
if err != nil {
log.Printf("Failed to fetch scrobble count: %+v", err)
return statsReq, errors.New("Failed to fetch stats")
}
statsReq.Tracks, err = getDbCount("SELECT COUNT(*) FROM tracks")
statsReq.Tracks, err = getDbCount(`SELECT COUNT(*) FROM tracks`)
if err != nil {
log.Printf("Failed to fetch track count: %+v", err)
return statsReq, errors.New("Failed to fetch stats")
}
statsReq.Artists, err = getDbCount("SELECT COUNT(*) FROM artists")
statsReq.Artists, err = getDbCount(`SELECT COUNT(*) FROM artists`)
if err != nil {
log.Printf("Failed to fetch artist count: %+v", err)
return statsReq, errors.New("Failed to fetch stats")

View File

@ -29,7 +29,7 @@ func getUserUuidForToken(token string) (string, error) {
var uuid string
cachedKey := getRedisVal("user_token:" + token)
if cachedKey == "" {
err := db.QueryRow("SELECT uuid FROM users WHERE token = $1 AND active = true", token).Scan(&uuid)
err := db.QueryRow(`SELECT uuid FROM users WHERE token = $1 AND active = true`, token).Scan(&uuid)
if err != nil {
return "", errors.New("Invalid Token")
}
@ -50,14 +50,14 @@ func insertRefreshToken(userUuid string, token string) error {
}
func deleteRefreshToken(token string) error {
_, err := db.Exec("DELETE FROM refresh_tokens WHERE token = $1", token)
_, err := db.Exec(`DELETE FROM refresh_tokens WHERE token = $1`, token)
return err
}
func isValidRefreshToken(refreshTokenStr string) (User, error) {
var refresh RefreshToken
err := db.QueryRow("SELECT uuid, user, token, expiry FROM refresh_tokens WHERE token = $1",
err := db.QueryRow(`SELECT uuid, "user", token, expiry FROM refresh_tokens WHERE token = $1`,
refreshTokenStr).Scan(&refresh.UUID, &refresh.User, &refresh.Token, &refresh.Expiry)
if err != nil {
return User{}, errors.New("Invalid Refresh Token")

View File

@ -98,7 +98,7 @@ func insertTrack(name string, legnth int, mbid string, spotifyId string, album s
func getTrackByCol(col string, val string, tx *sql.Tx) Track {
var track Track
err := tx.QueryRow(
"SELECT uuid, name, IFNULL(desc,''), IFNULL(img,''), mbid FROM tracks WHERE "+col+" = $1 LIMIT 1",
`SELECT uuid, name, COALESCE(desc,''), COALESCE(img,''), mbid FROM tracks WHERE "`+col+`" = $1 LIMIT 1`,
val).Scan(&track.UUID, &track.Name, &track.Desc, &track.Img, &track.MusicBrainzID)
if err != nil {
@ -114,11 +114,11 @@ func getTrackWithArtists(name string, artists []string, album string, tx *sql.Tx
var track Track
artistString := strings.Join(artists, "','")
err := tx.QueryRow(
"SELECT uuid, name, IFNULL(desc,''), IFNULL(img,''), mbid FROM tracks "+
"LEFT JOIN track_artist ON tracks.uuid = track_artist.track "+
"LEFT JOIN track_album ON tracks.uuid = track_album.track "+
"WHERE name = $1 AND track_artist.artistIN ('"+artistString+"') "+
"AND track_album.album = $2 LIMIT 1",
`SELECT uuid, name, COALESCE(desc,''), COALESCE(img,''), mbid FROM tracks `+
`LEFT JOIN track_artist ON tracks.uuid = track_artist.track `+
`LEFT JOIN track_album ON tracks.uuid = track_album.track `+
`WHERE name = $1 AND track_artist.artistIN ('`+artistString+`') `+
`AND track_album.album = $2 LIMIT 1`,
name, album).Scan(&track.UUID, &track.Name, &track.Desc, &track.Img, &track.MusicBrainzID)
if err != nil {
@ -137,8 +137,8 @@ func insertNewTrack(track *Track, name string, length int, mbid string, spotifyI
track.MusicBrainzID = mbid
track.SpotifyID = spotifyId
_, err := tx.Exec("INSERT INTO tracks (uuid, name, length, mbid, spotify_id) "+
"VALUES ($1,$2,$3,$4,$5)", track.UUID, track.Name, track.Length, track.MusicBrainzID, track.SpotifyID)
_, err := tx.Exec(`INSERT INTO tracks (uuid, name, length, mbid, spotify_id) `+
`VALUES ($1,$2,$3,$4,$5)`, track.UUID, track.Name, track.Length, track.MusicBrainzID, track.SpotifyID)
return err
}
@ -158,8 +158,8 @@ func (track *Track) linkTrack(album string, artists []string, tx *sql.Tx) error
}
func (track *Track) linkTrackToAlbum(albumUuid string, tx *sql.Tx) error {
_, err := tx.Exec("INSERT INTO track_album (track, album) "+
"VALUES ($1, $2)", track.UUID, albumUuid)
_, err := tx.Exec(`INSERT INTO track_album (track, album) `+
`VALUES ($1, $2)`, track.UUID, albumUuid)
return err
}
@ -167,8 +167,8 @@ func (track *Track) linkTrackToAlbum(albumUuid string, tx *sql.Tx) error {
func (track *Track) linkTrackToArtists(artists []string, tx *sql.Tx) error {
var err error
for _, artist := range artists {
_, err = tx.Exec("INSERT INTO track_artist (track, artist) "+
"VALUES ($1,$2)", track.UUID, artist)
_, err = tx.Exec(`INSERT INTO track_artist (track, artist) `+
`VALUES ($1,$2)`, track.UUID, artist)
if err != nil {
return err
}
@ -178,18 +178,18 @@ func (track *Track) linkTrackToArtists(artists []string, tx *sql.Tx) error {
}
func (track *Track) updateTrack(col string, val string, tx *sql.Tx) error {
_, err := tx.Exec("UPDATE tracks SET "+col+" = $1 WHERE uuid = $2", val, track.UUID)
_, err := tx.Exec(`UPDATE tracks SET "`+col+`" = $1 WHERE uuid = $2`, val, track.UUID)
return err
}
func getTrackByUUID(uuid string) (Track, error) {
var track Track
err := db.QueryRow("SELECT tracks.uuid, tracks.name, IFNULL(albums.desc,''), IFNULL(albums.uuid,''), tracks.length, tracks.mbid, tracks.spotify_id "+
"FROM tracks "+
"LEFT JOIN track_album ON track_album.track = tracks.uuid "+
"LEFT JOIN albums ON track_album.album = albums.uuid "+
"WHERE tracks.uuid = $1",
err := db.QueryRow(`SELECT tracks.uuid, tracks.name, COALESCE(albums.desc,''), COALESCE(albums.uuid,''), tracks.length, tracks.mbid, tracks.spotify_id `+
`FROM tracks `+
`LEFT JOIN track_album ON track_album.track = tracks.uuid `+
`LEFT JOIN albums ON track_album.album = albums.uuid `+
`WHERE tracks.uuid = $1`,
uuid).Scan(&track.UUID, &track.Name, &track.Desc, &track.Img, &track.Length, &track.MusicBrainzID, &track.SpotifyID)
if err != nil {
@ -203,15 +203,15 @@ func getTrackByUUID(uuid string) (Track, error) {
func getTopTracks(userUuid string) (TopTracks, error) {
var topTracks TopTracks
rows, err := db.Query("SELECT tracks.uuid, tracks.name, IFNULL(albums.uuid,''), count(*) "+
"FROM scrobbles "+
"JOIN tracks ON tracks.uuid = scrobbles.track "+
"JOIN track_album ON track_album.track = tracks.uuid "+
"JOIN albums ON track_album.album = albums.uuid "+
"WHERE user = $1 "+
"GROUP BY scrobbles.track "+
"ORDER BY count(*) DESC "+
"LIMIT 14",
rows, err := db.Query(`SELECT tracks.uuid, tracks.name, COALESCE(albums.uuid,''), count(*) `+
`FROM scrobbles `+
`JOIN tracks ON tracks.uuid = scrobbles.track `+
`JOIN track_album ON track_album.track = tracks.uuid `+
`JOIN albums ON track_album.album = albums.uuid `+
`WHERE "user" = $1 `+
`GROUP BY scrobbles.track `+
`ORDER BY count(*) DESC `+
`LIMIT 14`,
userUuid)
if err != nil {
log.Printf("Failed to fetch top tracks: %+v", err)
@ -251,9 +251,9 @@ func (track *Track) loadExtraTrackInfo() error {
func (track *Track) getArtistsForTrack() error {
artists := []Artist{}
rows, err := db.Query("SELECT track_artist.artist "+
"FROM track_artist "+
"WHERE track_artist.track = $1",
rows, err := db.Query(`SELECT track_artist.artist `+
`FROM track_artist `+
`WHERE track_artist.track = $1`,
track.UUID)
if err != nil {
log.Printf("Failed to fetch artists for track: %+v", err)
@ -283,9 +283,9 @@ func (track *Track) getArtistsForTrack() error {
func (track *Track) getAlbumsForTrack() error {
albums := []Album{}
rows, err := db.Query("SELECT track_album.album "+
"FROM track_album "+
"WHERE track_album.track = $1",
rows, err := db.Query(`SELECT track_album.album `+
`FROM track_album `+
`WHERE track_album.track = $1`,
track.UUID)
if err != nil {
log.Printf("Failed to fetch album for track: %+v", err)
@ -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 = $1 GROUP BY track, user", trackUUID)
`SELECT COUNT(*) FROM scrobbles WHERE track = $1 GROUP BY track, "user"`, trackUUID)
if err != nil {
log.Printf("Failed to fetch scrobble count: %+v", err)
@ -328,12 +328,12 @@ func getTopUsersForTrackUUID(trackUUID string, limit int, page int) (TopUserTrac
}
rows, err := db.Query(
"SELECT scrobbles.user, users.username, COUNT(*) "+
"FROM scrobbles "+
"JOIN users ON scrobbles.user = users.uuid "+
"WHERE track = $1 "+
"GROUP BY scrobbles.user "+
"ORDER BY COUNT(*) DESC LIMIT $2",
`SELECT scrobbles.user, users.username, COUNT(*) `+
`FROM scrobbles `+
`JOIN users ON scrobbles."user" = users.uuid `+
`WHERE track = $1 `+
`GROUP BY scrobbles."user" `+
`ORDER BY COUNT(*) DESC LIMIT $2`,
trackUUID, limit)
if err != nil {

View File

@ -99,7 +99,7 @@ func loginUser(logReq *RequestRequest, ip net.IP) ([]byte, error) {
}
if strings.Contains(logReq.Username, "@") {
err := db.QueryRow("SELECT uuid, username, email, password, admin, mod FROM users WHERE email = $1 AND active = true",
err := db.QueryRow(`SELECT uuid, username, email, password, admin, mod FROM users WHERE email = $1 AND active = true`,
logReq.Username).Scan(&user.UUID, &user.Username, &user.Email, &user.Password, &user.Admin, &user.Mod)
if err != nil {
if err == sql.ErrNoRows {
@ -107,7 +107,7 @@ func loginUser(logReq *RequestRequest, ip net.IP) ([]byte, error) {
}
}
} else {
err := db.QueryRow("SELECT uuid, username, email, password, admin, mod FROM users WHERE username = $1 AND active = true",
err := db.QueryRow(`SELECT uuid, username, email, password, admin, mod FROM users WHERE username = $1 AND active = true`,
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")
@ -140,20 +140,20 @@ func insertUser(username string, email string, password []byte, ip net.IP) error
log.Printf(ip.String())
_, err := db.Exec("INSERT INTO users (uuid, created_at, created_ip, modified_at, modified_ip, username, email, password, token) "+
"VALUES ($1,NOW(),$2,NOW(),$3,$4,$5,$6,$7)", uuid, ip.String(), ip.String(), username, email, password, token)
_, err := db.Exec(`INSERT INTO users (uuid, created_at, created_ip, modified_at, modified_ip, username, email, password, token) `+
`VALUES ($1,NOW(),$2,NOW(),$3,$4,$5,$6,$7)`, uuid, ip.String(), ip.String(), username, email, password, token)
return err
}
func (user *User) updateUser(field string, value string, ip net.IP) error {
_, err := db.Exec("UPDATE users SET "+field+" = $1, modified_at = NOW(), modified_ip = $2 WHERE uuid = $3", value, ip, user.UUID)
_, err := db.Exec(`UPDATE users SET "`+field+`" = $1, modified_at = NOW(), modified_ip = $2 WHERE uuid = $3`, value, ip, user.UUID)
return err
}
func (user *User) updateUserDirect(field string, value string) error {
_, err := db.Exec("UPDATE users SET "+field+" = $1 WHERE uuid = $2", value, user.UUID)
_, err := db.Exec(`UPDATE users SET "`+field+`" = $1 WHERE uuid = $2`, value, user.UUID)
return err
}
@ -176,7 +176,7 @@ func isValidPassword(password string, user User) bool {
// userAlreadyExists - Returns bool indicating if a record exists for either username or email
// Using two look ups to make use of DB indexes.
func userAlreadyExists(req *RequestRequest) bool {
count, err := getDbCount("SELECT COUNT(*) FROM users WHERE username = $1", req.Username)
count, err := getDbCount(`SELECT COUNT(*) FROM users WHERE username = $1`, req.Username)
if err != nil {
fmt.Printf("Error querying for duplicate users: %v", err)
return true
@ -188,7 +188,7 @@ func userAlreadyExists(req *RequestRequest) bool {
if req.Email != "" {
// Only run email check if there's an email...
count, err = getDbCount("SELECT COUNT(*) FROM users WHERE email = $1", req.Email)
count, err = getDbCount(`SELECT COUNT(*) FROM users WHERE email = $1`, req.Email)
}
if err != nil {
@ -201,7 +201,7 @@ func userAlreadyExists(req *RequestRequest) bool {
func getUserByUUID(uuid string) (User, error) {
var user User
err := db.QueryRow("SELECT uuid, created_at, created_ip, modified_at, modified_ip, username, email, password, verified, admin, mod, timezone, token FROM users WHERE uuid = $1 AND active = true",
err := db.QueryRow(`SELECT uuid, created_at, created_ip, modified_at, modified_ip, username, email, password, verified, admin, mod, timezone, token FROM users WHERE uuid = $1 AND active = true`,
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 {
@ -213,7 +213,7 @@ func getUserByUUID(uuid string) (User, error) {
func getUserByUsername(username string) (User, error) {
var user User
err := db.QueryRow("SELECT uuid, created_at, created_ip, modified_at, modified_ip, username, email, password, verified, admin, mod, timezone, token FROM users WHERE username = $1 AND active = true",
err := db.QueryRow(`SELECT uuid, created_at, created_ip, modified_at, modified_ip, username, email, password, verified, admin, mod, timezone, token FROM users WHERE username = $1 AND active = true`,
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 {
@ -225,7 +225,7 @@ func getUserByUsername(username string) (User, error) {
func getUserByEmail(email string) (User, error) {
var user User
err := db.QueryRow("SELECT uuid, created_at, created_ip, modified_at, modified_ip, username, email, password, verified, admin, mod, timezone, token FROM users WHERE email = $1 AND active = true",
err := db.QueryRow(`SELECT uuid, created_at, created_ip, modified_at, modified_ip, username, email, password, verified, admin, mod, timezone, token FROM users WHERE email = $1 AND active = true`,
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 {
@ -237,8 +237,8 @@ func getUserByEmail(email string) (User, error) {
func getUserByResetToken(token string) (User, error) {
var user User
err := db.QueryRow("SELECT users.uuid, 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 = $1 AND active = true",
err := db.QueryRow(`SELECT users.uuid, 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 = $1 AND active = true`,
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 {
@ -269,26 +269,26 @@ func (user *User) sendResetEmail(ip net.IP) error {
}
func (user *User) saveResetToken(token string, expiry time.Time) error {
_, _ = db.Exec("DELETE FROM resettoken WHERE user = $1", user.UUID)
_, err := db.Exec("INSERT INTO resettoken (user, token, expiry) "+
"VALUES ($1,$2,$3)", user.UUID, token, expiry)
_, _ = db.Exec(`DELETE FROM resettoken WHERE "user" = $1`, user.UUID)
_, err := db.Exec(`INSERT INTO resettoken ("user", token, expiry) `+
`VALUES ($1,$2,$3)`, user.UUID, token, expiry)
return err
}
func clearOldResetTokens() {
_, _ = db.Exec("DELETE FROM resettoken WHERE expiry < NOW()")
_, _ = db.Exec(`DELETE FROM resettoken WHERE expiry < NOW()`)
}
func clearResetToken(token string) error {
_, err := db.Exec("DELETE FROM resettoken WHERE token = $1", token)
_, err := db.Exec(`DELETE FROM resettoken WHERE token = $1`, token)
return err
}
// checkResetToken - If a token exists check it
func checkResetToken(token string) (bool, error) {
count, err := getDbCount("SELECT COUNT(*) FROM resettoken WHERE token = $1", token)
count, err := getDbCount(`SELECT COUNT(*) FROM resettoken WHERE token = $1`, token)
if err != nil {
return false, err
@ -303,7 +303,7 @@ func (user *User) updatePassword(newPassword string, ip net.IP) error {
return errors.New("Bad password")
}
_, err = db.Exec("UPDATE users SET password = $1 WHERE uuid = $2", hash, user.UUID)
_, err = db.Exec(`UPDATE users SET password = $1 WHERE uuid = $2`, hash, user.UUID)
if err != nil {
return errors.New("Failed to update password")
}
@ -321,8 +321,8 @@ func (user *User) getNavidromeTokens() (OauthToken, error) {
func getAllSpotifyUsers() ([]User, error) {
users := make([]User, 0)
rows, err := db.Query("SELECT users.uuid, 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 = true")
rows, err := db.Query(`SELECT users.uuid, 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 = true`)
if err != nil {
log.Printf("Failed to fetch spotify users: %+v", err)
@ -353,8 +353,8 @@ func getAllSpotifyUsers() ([]User, error) {
func getAllNavidromeUsers() ([]User, error) {
users := make([]User, 0)
rows, err := db.Query("SELECT users.uuid, 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 = true")
rows, err := db.Query(`SELECT users.uuid, 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 = true`)
if err != nil {
log.Printf("Failed to fetch navidrome users: %+v", err)

View File

@ -1,7 +1,6 @@
START TRANSACTION;
ALTER TABLE tracks DROP COLUMN spotify_id;
ALTER TABLE users DROP COLUMN spotify_id;
ALTER TABLE albums DROP COLUMN spotify_id;
ALTER TABLE artists DROP COLUMN spotify_id;

View File

@ -1,11 +1,9 @@
START TRANSACTION;
ALTER TABLE users ADD COLUMN spotify_id VARCHAR(255) NOT NULL DEFAULT '';
ALTER TABLE albums ADD COLUMN spotify_id VARCHAR(255) NOT NULL DEFAULT '';
ALTER TABLE artists ADD COLUMN spotify_id VARCHAR(255) NOT NULL DEFAULT '';
ALTER TABLE tracks ADD COLUMN spotify_id VARCHAR(255) NOT NULL DEFAULT '';
CREATE INDEX usersSpotifyLookup ON users (spotify_id);
CREATE INDEX albumsSpotifyLookup ON albums (spotify_id);
CREATE INDEX artistsSpotifyLookup ON artists (spotify_id);
CREATE INDEX tracksSpotifyLookup ON tracks (spotify_id);