mirror of
https://github.com/idanoo/GoScrobble.git
synced 2024-11-23 08:55:18 +00:00
Merge remote-tracking branch 'origin/postgres'
This commit is contained in:
commit
ba3e35cb86
28
.env.example
28
.env.example
@ -1,28 +0,0 @@
|
|||||||
MYSQL_HOST=
|
|
||||||
MYSQL_USER=
|
|
||||||
MYSQL_PASS=
|
|
||||||
MYSQL_DB=
|
|
||||||
|
|
||||||
REDIS_HOST=127.0.0.1
|
|
||||||
REDIS_PORT=6379
|
|
||||||
REDIS_DB=
|
|
||||||
REDIS_PREFIX="gs:"
|
|
||||||
REDIS_AUTH=""
|
|
||||||
|
|
||||||
JWT_SECRET=
|
|
||||||
JWT_EXPIRY=1800
|
|
||||||
REFRESH_EXPIRY=604800
|
|
||||||
|
|
||||||
REVERSE_PROXIES=127.0.0.1
|
|
||||||
PORT=42069
|
|
||||||
|
|
||||||
SENDGRID_API_KEY=
|
|
||||||
MAIL_FROM_ADDRESS=
|
|
||||||
MAIL_FROM_NAME=
|
|
||||||
|
|
||||||
DEV_MODE=false
|
|
||||||
GOSCROBBLE_DOMAIN=""
|
|
||||||
|
|
||||||
DATA_DIRECTORY="/var/www/goscrobble-data"
|
|
||||||
FRONTEND_DIRECTORY="/var/www/goscrobble-web"
|
|
||||||
API_DOCS_DIRECTORY="/var/www/goscrobble-api/docs/api/build"
|
|
28
README.md
28
README.md
@ -2,37 +2,37 @@
|
|||||||
|
|
||||||
Golang based music scrobbler.
|
Golang based music scrobbler.
|
||||||
|
|
||||||
Stack: Go 1.18+, Node 15+, React 17+, MySQL 8.0+, Redis
|
Stack: Go 1.18+, Node 15+, React 17+, Postgresql 14.0+, Redis
|
||||||
|
|
||||||
|
There are prebuilt binaries/packages available.
|
||||||
|
|
||||||
|
Copy .env.example to .env and set variables. You can use https://www.grc.com/passwords.htm to generate a JWT_SECRET.
|
||||||
|
|
||||||
## More documentation
|
## More documentation
|
||||||
[Changelog](docs/changelog.md)
|
[Changelog](docs/changelog.md)
|
||||||
|
|
||||||
[Environment Variables](docs/config.md)
|
[Environment Variables](docs/config.md)
|
||||||
|
|
||||||
## Setup MySQL
|
## Local development with docker
|
||||||
create user 'goscrobble'@'%' identified by 'supersecurepass';
|
This assumes you have goscrobble-api and goscrobble-web cloned in the same folder.
|
||||||
create database goscrobble;
|
|
||||||
grant all privileges on goscrobble.* to 'goscrobble'@'%';
|
|
||||||
|
|
||||||
## Local Development
|
|
||||||
cp .env.example .env # Fill in the blanks
|
|
||||||
go mod tidy
|
|
||||||
CGO_ENABLED=0 go run cmd/go-scrobble/*.go
|
|
||||||
|
|
||||||
|
cp .env.development .env
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
Access API @ http://127.0.0.1:42069/api/v1
|
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
|
## Prod deployment
|
||||||
cp .env.example .env # Fill in the blanks
|
cp .env.production .env # Fill in the blanks
|
||||||
go build -o goscrobble cmd/go-scrobble/*.go
|
go build -o goscrobble cmd/go-scrobble/*.go
|
||||||
./goscrobble
|
./goscrobble
|
||||||
|
|
||||||
## Build API Docs
|
## Build API docs
|
||||||
cd docs/api && docker run --rm --name slate -v $(pwd)/build:/srv/slate/build -v $(pwd)/source:/srv/slate/source slatedocs/slate build
|
cd docs/api && docker run --rm --name slate -v $(pwd)/build:/srv/slate/build -v $(pwd)/source:/srv/slate/source slatedocs/slate build
|
||||||
|
|
||||||
|
|
||||||
## Test API Docs
|
## Test API Docs
|
||||||
cd docs/api && docker run --rm --name slate -p 4567:4567 -v $(pwd)/source:/srv/slate/source slatedocs/slate serve
|
cd docs/api && docker run --rm --name slate -p 4567:4567 -v $(pwd)/source:/srv/slate/source slatedocs/slate serve
|
||||||
|
|
||||||
## Support Development!
|
## Support development!
|
||||||
Feel free to support hosting and my coffee addiction https://liberapay.com/idanoo
|
Feel free to support hosting and my coffee addiction https://liberapay.com/idanoo
|
@ -16,29 +16,39 @@ services:
|
|||||||
image: golang:1.18
|
image: golang:1.18
|
||||||
volumes:
|
volumes:
|
||||||
- ./:/app
|
- ./:/app
|
||||||
|
- data:/data
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:42069:42069"
|
- "127.0.0.1:42069:42069"
|
||||||
restart: always
|
restart: always
|
||||||
command: bash -c "sleep 5 && cd /app && go mod tidy && go run cmd/goscrobble/*.go"
|
command: bash -c "cd /app && go mod tidy && go run cmd/goscrobble/*.go"
|
||||||
|
|
||||||
mysql:
|
postgres:
|
||||||
image: mysql:8.0.27
|
image: postgres:14.1
|
||||||
command: --default-authentication-plugin=mysql_native_password --init-file /app/migrations/0_create_db.sql --sql_mode=
|
|
||||||
restart: always
|
|
||||||
cap_add:
|
|
||||||
- SYS_NICE
|
|
||||||
volumes:
|
volumes:
|
||||||
- database-data:/var/lib/mysql
|
- database-data:/var/lib/postgresql/data/
|
||||||
- ./migrations/0_create_db.sql:/app/migrations/0_create_db.sql
|
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:3306:3306"
|
- 5432:5432
|
||||||
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
- MYSQL_ROOT_PASSWORD=supersecretdatabasepassword1
|
- POSTGRES_USER=goscrobble
|
||||||
|
- POSTGRES_PASSWORD=supersecretdatabasepassword1
|
||||||
|
- POSTGRES_DB=goscrobble
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:6.2
|
image: redis:6.2
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:6379:6379"
|
- "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:
|
volumes:
|
||||||
database-data:
|
database-data:
|
||||||
|
data:
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
- Split frontend/backend code into separate repos (https://gitlab.com/goscrobble/goscrobble-web)
|
- Split frontend/backend code into separate repos (https://gitlab.com/goscrobble/goscrobble-web)
|
||||||
- Added new ENV VARS to support unique configurations: DATA_DIRECTORY, FRONTEND_DIRECTORY, API_DOCS_DIRECTORY
|
- Added new ENV VARS to support unique configurations: DATA_DIRECTORY, FRONTEND_DIRECTORY, API_DOCS_DIRECTORY
|
||||||
- Started API documentation @ /docs (https://goscrobble.com/docs/)
|
- Started API documentation @ /docs (https://goscrobble.com/docs/)
|
||||||
|
- Added docker-compose file for local development!
|
||||||
|
|
||||||
# 0.0.33
|
# 0.0.33
|
||||||
- Add mod permission
|
- Add mod permission
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
## Timezones
|
## Timezones
|
||||||
GoScrobble runs as UTC and connects to MySQL as UTC. All timezone handling is done in the frontend.
|
GoScrobble runs as UTC and connects to postgres as UTC. All timezone handling is done in the frontend.
|
||||||
|
|
||||||
## FRONTEND VARS
|
## FRONTEND VARS
|
||||||
These are stored in `web/.env.production` and `web/.env.development`
|
These are stored in `web/.env.production` and `web/.env.development`
|
||||||
@ -8,10 +8,10 @@ These are stored in `web/.env.production` and `web/.env.development`
|
|||||||
|
|
||||||
|
|
||||||
## BACKEND VARS
|
## BACKEND VARS
|
||||||
MYSQL_HOST= // MySQL Server
|
POSTGRES_HOST= // postgres Server
|
||||||
MYSQL_USER= // MySQL User
|
POSTGRES_USER= // postgres User
|
||||||
MYSQL_PASS= // MySQL Password
|
POSTGRES_PASS= // postgres Password
|
||||||
MYSQL_DB= // MySQL Database
|
POSTGRES_DB= // postgres Database
|
||||||
|
|
||||||
REDIS_HOST=127.0.0.1 // Redis host
|
REDIS_HOST=127.0.0.1 // Redis host
|
||||||
REDIS_PORT= // Redis port (defaults 6379)
|
REDIS_PORT= // Redis port (defaults 6379)
|
||||||
|
95
docs/migrate.php
Normal file
95
docs/migrate.php
Normal 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";
|
8
go.mod
8
go.mod
@ -31,6 +31,14 @@ require (
|
|||||||
github.com/docker/go-units v0.4.0 // indirect
|
github.com/docker/go-units v0.4.0 // indirect
|
||||||
github.com/gogo/protobuf v1.3.1 // indirect
|
github.com/gogo/protobuf v1.3.1 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
|
github.com/go-redis/redis/v8 v8.8.0
|
||||||
|
github.com/gogo/protobuf v1.3.1 // indirect
|
||||||
|
github.com/golang-migrate/migrate v3.5.4+incompatible
|
||||||
|
github.com/golang/protobuf v1.4.3 // indirect
|
||||||
|
github.com/google/uuid v1.2.0
|
||||||
|
github.com/gorilla/mux v1.8.0
|
||||||
|
github.com/joho/godotenv v1.3.0
|
||||||
|
github.com/lib/pq v1.10.4
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
8
go.sum
8
go.sum
@ -79,12 +79,6 @@ github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV
|
|||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/go-redis/redis/v8 v8.8.0 h1:fDZP58UN/1RD3DjtTXP/fFZ04TFohSYhjZDkcDe2dnw=
|
github.com/go-redis/redis/v8 v8.8.0 h1:fDZP58UN/1RD3DjtTXP/fFZ04TFohSYhjZDkcDe2dnw=
|
||||||
github.com/go-redis/redis/v8 v8.8.0/go.mod h1:F7resOH5Kdug49Otu24RjHWwgK7u9AmtqWMnCV1iP5Y=
|
github.com/go-redis/redis/v8 v8.8.0/go.mod h1:F7resOH5Kdug49Otu24RjHWwgK7u9AmtqWMnCV1iP5Y=
|
||||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
|
||||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
|
||||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
|
||||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
|
||||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
|
||||||
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
||||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||||
github.com/golang-migrate/migrate v3.5.4+incompatible h1:R7OzwvCJTCgwapPCiX6DyBiu2czIUMDCB118gFTKTUA=
|
github.com/golang-migrate/migrate v3.5.4+incompatible h1:R7OzwvCJTCgwapPCiX6DyBiu2czIUMDCB118gFTKTUA=
|
||||||
@ -163,6 +157,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
|
|||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
|
||||||
|
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
@ -79,7 +79,7 @@ func insertAlbum(name string, mbid string, spotifyId string, artists []string, i
|
|||||||
func getAlbumByCol(col string, val string, tx *sql.Tx) Album {
|
func getAlbumByCol(col string, val string, tx *sql.Tx) Album {
|
||||||
var album Album
|
var album Album
|
||||||
err := tx.QueryRow(
|
err := tx.QueryRow(
|
||||||
"SELECT BIN_TO_UUID(`uuid`, true), `name`, IFNULL(`desc`, ''), IFNULL(`img`,''), `mbid`, `spotify_id` FROM `albums` WHERE `"+col+"` = ?",
|
`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)
|
val).Scan(&album.UUID, &album.Name, &album.Desc, &album.Img, &album.MusicBrainzID, &album.SpotifyID)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -98,8 +98,8 @@ func insertNewAlbum(album *Album, name string, mbid string, spotifyId string, im
|
|||||||
album.SpotifyID = spotifyId
|
album.SpotifyID = spotifyId
|
||||||
album.Img = img
|
album.Img = img
|
||||||
|
|
||||||
_, err := tx.Exec("INSERT INTO `albums` (`uuid`, `name`, `mbid`, `spotify_id`, `img`) "+
|
_, err := tx.Exec(`INSERT INTO albums (uuid, name, mbid, spotify_id, img) `+
|
||||||
"VALUES (UUID_TO_BIN(?, true),?,?,?,?)", album.UUID, album.Name, album.MusicBrainzID, album.SpotifyID, album.Img)
|
`VALUES ($1,$2,$3,$4,$5)`, album.UUID, album.Name, album.MusicBrainzID, album.SpotifyID, album.Img)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -107,8 +107,8 @@ func insertNewAlbum(album *Album, name string, mbid string, spotifyId string, im
|
|||||||
func (album *Album) linkAlbumToArtists(artists []string, tx *sql.Tx) error {
|
func (album *Album) linkAlbumToArtists(artists []string, tx *sql.Tx) error {
|
||||||
var err error
|
var err error
|
||||||
for _, artist := range artists {
|
for _, artist := range artists {
|
||||||
_, err = tx.Exec("INSERT INTO `album_artist` (`album`, `artist`) "+
|
_, err = tx.Exec(`INSERT INTO album_artist (album, artist) `+
|
||||||
"VALUES (UUID_TO_BIN(?, true), UUID_TO_BIN(?, true))", album.UUID, artist)
|
`VALUES ($1,$2)`, album.UUID, artist)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -118,14 +118,14 @@ func (album *Album) linkAlbumToArtists(artists []string, tx *sql.Tx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (album *Album) updateAlbum(col string, val string, tx *sql.Tx) error {
|
func (album *Album) updateAlbum(col string, val string, tx *sql.Tx) error {
|
||||||
_, err := tx.Exec("UPDATE `albums` SET `"+col+"` = ? WHERE `uuid` = UUID_TO_BIN(?,true)", val, album.UUID)
|
_, err := tx.Exec(`UPDATE albums SET "`+col+`" = $1 WHERE uuid = $2`, val, album.UUID)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAlbumByUUID(uuid string) (Album, error) {
|
func getAlbumByUUID(uuid string) (Album, error) {
|
||||||
var album Album
|
var album Album
|
||||||
err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), `name`, IFNULL(`desc`,''), IFNULL(`img`,''), `mbid`, `spotify_id` FROM `albums` WHERE `uuid` = UUID_TO_BIN(?, true)",
|
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)
|
uuid).Scan(&album.UUID, &album.Name, &album.Desc, &album.Img, &album.MusicBrainzID, &album.SpotifyID)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -89,7 +89,7 @@ func insertArtist(name string, mbid string, spotifyId string, img string, tx *sq
|
|||||||
func getArtistByCol(col string, val string, tx *sql.Tx) Artist {
|
func getArtistByCol(col string, val string, tx *sql.Tx) Artist {
|
||||||
var artist Artist
|
var artist Artist
|
||||||
err := tx.QueryRow(
|
err := tx.QueryRow(
|
||||||
"SELECT BIN_TO_UUID(`uuid`, true), `name`, IFNULL(`desc`,''), IFNULL(`img`,''), `mbid`, `spotify_id` FROM `artists` WHERE `"+col+"` = ?",
|
`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)
|
val).Scan(&artist.UUID, &artist.Name, &artist.Desc, &artist.Img, &artist.MusicBrainzID, &artist.SpotifyID)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -108,21 +108,21 @@ func insertNewArtist(artist *Artist, name string, mbid string, spotifyId string,
|
|||||||
artist.SpotifyID = spotifyId
|
artist.SpotifyID = spotifyId
|
||||||
artist.Img = img
|
artist.Img = img
|
||||||
|
|
||||||
_, err := tx.Exec("INSERT INTO `artists` (`uuid`, `name`, `mbid`, `spotify_id`, `img`) "+
|
_, err := tx.Exec(`INSERT INTO artists (uuid, name, mbid, spotify_id, img) `+
|
||||||
"VALUES (UUID_TO_BIN(?, true),?,?,?,?)", artist.UUID, artist.Name, artist.MusicBrainzID, artist.SpotifyID, artist.Img)
|
`VALUES ($1,$2,$3,$4,$5)`, artist.UUID, artist.Name, artist.MusicBrainzID, artist.SpotifyID, artist.Img)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (artist *Artist) updateArtist(col string, val string, tx *sql.Tx) error {
|
func (artist *Artist) updateArtist(col string, val string, tx *sql.Tx) error {
|
||||||
_, err := tx.Exec("UPDATE `artists` SET `"+col+"` = ? WHERE `uuid` = UUID_TO_BIN(?,true)", val, artist.UUID)
|
_, err := tx.Exec(`UPDATE artists SET "`+col+`" = $1 WHERE uuid = $2`, val, artist.UUID)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func getArtistByUUID(uuid string) (Artist, error) {
|
func getArtistByUUID(uuid string) (Artist, error) {
|
||||||
var artist Artist
|
var artist Artist
|
||||||
err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), `name`, IFNULL(`desc`, ''), IFNULL(`img`,''), `mbid`, `spotify_id` FROM `artists` WHERE `uuid` = UUID_TO_BIN(?, true)",
|
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)
|
uuid).Scan(&artist.UUID, &artist.Name, &artist.Desc, &artist.Img, &artist.MusicBrainzID, &artist.SpotifyID)
|
||||||
|
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
@ -136,26 +136,37 @@ func getArtistByUUID(uuid string) (Artist, error) {
|
|||||||
func getTopArtists(userUuid string, dayRange string) (TopArtists, error) {
|
func getTopArtists(userUuid string, dayRange string) (TopArtists, error) {
|
||||||
var topArtist TopArtists
|
var topArtist TopArtists
|
||||||
|
|
||||||
dateClause := ""
|
// dateClause := ""
|
||||||
if dayRange != "" {
|
// if dayRange != "" {
|
||||||
dateClause = " AND DATE(created_at) > SUBDATE(CURRENT_DATE, " + dayRange + ") "
|
// dateClause = " AND DATE(created_at) > SUBDATE(CURRENT_DATE, " + dayRange + ") "
|
||||||
}
|
// }
|
||||||
|
|
||||||
whereClause := ""
|
// whereClause := ""
|
||||||
if userUuid != "0" {
|
// if userUuid != "0" {
|
||||||
whereClause = "WHERE `scrobbles`.`user` = UUID_TO_BIN('" + userUuid + "', true) "
|
// whereClause = "WHERE `scrobbles`.`user` = UUID_TO_BIN('" + userUuid + "', true) "
|
||||||
}
|
// }
|
||||||
|
|
||||||
rows, err := db.Query("SELECT BIN_TO_UUID(`artists`.`uuid`, true), `artists`.`name`, IFNULL(BIN_TO_UUID(`artists`.`uuid`, true),''), count(*) " +
|
// rows, err := db.Query("SELECT BIN_TO_UUID(`artists`.`uuid`, true), `artists`.`name`, IFNULL(BIN_TO_UUID(`artists`.`uuid`, true),''), count(*) " +
|
||||||
"FROM `scrobbles` " +
|
// "FROM `scrobbles` " +
|
||||||
"JOIN `tracks` ON `tracks`.`uuid` = `scrobbles`.`track` " +
|
// "JOIN `tracks` ON `tracks`.`uuid` = `scrobbles`.`track` " +
|
||||||
"JOIN track_artist ON track_artist.track = tracks.uuid " +
|
// "JOIN track_artist ON track_artist.track = tracks.uuid " +
|
||||||
"JOIN artists ON track_artist.artist = artists.uuid " +
|
// "JOIN artists ON track_artist.artist = artists.uuid " +
|
||||||
whereClause +
|
// whereClause +
|
||||||
dateClause +
|
// dateClause +
|
||||||
"GROUP BY `artists`.`uuid` " +
|
// "GROUP BY `artists`.`uuid` " +
|
||||||
"ORDER BY count(*) DESC " +
|
// "ORDER BY count(*) DESC " +
|
||||||
"LIMIT 14;")
|
// "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 {
|
if err != nil {
|
||||||
log.Printf("Failed to fetch top artist: %+v", err)
|
log.Printf("Failed to fetch top artist: %+v", err)
|
||||||
return topArtist, errors.New("Failed to fetch top artist")
|
return topArtist, errors.New("Failed to fetch top artist")
|
||||||
|
@ -20,7 +20,7 @@ func getAllConfigs() (Config, error) {
|
|||||||
config := Config{}
|
config := Config{}
|
||||||
configs := make(map[string]string)
|
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 {
|
if err != nil {
|
||||||
log.Printf("Failed to fetch config: %+v", err)
|
log.Printf("Failed to fetch config: %+v", err)
|
||||||
return config, errors.New("Failed to fetch configs")
|
return config, errors.New("Failed to fetch configs")
|
||||||
@ -53,7 +53,7 @@ func getAllConfigs() (Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateConfigValue(key string, value string) error {
|
func updateConfigValue(key string, value string) error {
|
||||||
_, err := db.Exec("UPDATE `config` SET `value` = ? WHERE `key` = ?", value, key)
|
_, err := db.Exec(`UPDATE config SET value = $1 WHERE key = $2`, value, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Failed to update config: %+v", err)
|
fmt.Printf("Failed to update config: %+v", err)
|
||||||
return errors.New("Failed to update config value.")
|
return errors.New("Failed to update config value.")
|
||||||
@ -75,8 +75,8 @@ func getConfigValue(key string) (string, error) {
|
|||||||
// TODO: Handle unset vals in DB to prevent excess calls if not using spotify/etc.
|
// TODO: Handle unset vals in DB to prevent excess calls if not using spotify/etc.
|
||||||
configKey := getRedisVal(redisKey)
|
configKey := getRedisVal(redisKey)
|
||||||
if configKey == "" {
|
if configKey == "" {
|
||||||
err := db.QueryRow("SELECT `value` FROM `config` "+
|
err := db.QueryRow("SELECT value FROM config "+
|
||||||
"WHERE `key` = ?",
|
"WHERE key = ?",
|
||||||
key).Scan(&value)
|
key).Scan(&value)
|
||||||
|
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
|
@ -8,22 +8,26 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
_ "github.com/go-sql-driver/mysql"
|
|
||||||
"github.com/golang-migrate/migrate"
|
"github.com/golang-migrate/migrate"
|
||||||
"github.com/golang-migrate/migrate/database/mysql"
|
"github.com/golang-migrate/migrate/database/postgres"
|
||||||
_ "github.com/golang-migrate/migrate/source/file"
|
_ "github.com/golang-migrate/migrate/source/file"
|
||||||
|
_ "github.com/lib/pq"
|
||||||
)
|
)
|
||||||
|
|
||||||
var db *sql.DB
|
var db *sql.DB
|
||||||
|
|
||||||
// InitDb - Boots up a DB connection
|
// InitDb - Boots up a DB connection
|
||||||
func InitDb() {
|
func InitDb() {
|
||||||
dbHost := os.Getenv("MYSQL_HOST")
|
dbHost := os.Getenv("POSTGRES_HOST")
|
||||||
dbUser := os.Getenv("MYSQL_USER")
|
dbUser := os.Getenv("POSTGRES_USER")
|
||||||
dbPass := os.Getenv("MYSQL_PASS")
|
dbPass := os.Getenv("POSTGRES_PASS")
|
||||||
dbName := os.Getenv("MYSQL_DB")
|
dbName := os.Getenv("POSTGRES_DB")
|
||||||
|
|
||||||
|
dbConn, err := sql.Open(
|
||||||
|
"postgres",
|
||||||
|
fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", dbHost, 5432, dbUser, dbPass, dbName),
|
||||||
|
)
|
||||||
|
|
||||||
dbConn, err := sql.Open("mysql", dbUser+":"+dbPass+"@tcp("+dbHost+")/"+dbName+"?multiStatements=true&parseTime=true&loc=Etc%2FUTC")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -49,14 +53,14 @@ func CloseDbConn() {
|
|||||||
|
|
||||||
func runMigrations() {
|
func runMigrations() {
|
||||||
fmt.Println("Checking database migrations")
|
fmt.Println("Checking database migrations")
|
||||||
driver, err := mysql.WithInstance(db, &mysql.Config{})
|
driver, err := postgres.WithInstance(db, &postgres.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Unable to run migrations! %v", err)
|
log.Fatalf("Unable to run migrations! %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err := migrate.NewWithDatabaseInstance(
|
m, err := migrate.NewWithDatabaseInstance(
|
||||||
"file://migrations",
|
"file://migrations",
|
||||||
"mysql",
|
"postgres",
|
||||||
driver,
|
driver,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -13,7 +13,7 @@ type Genre struct {
|
|||||||
func getGenreByUUID(uuid string) Genre {
|
func getGenreByUUID(uuid string) Genre {
|
||||||
var genre Genre
|
var genre Genre
|
||||||
err := db.QueryRow(
|
err := db.QueryRow(
|
||||||
"SELECT BIN_TO_UUID(`uuid`, true), `name` FROM `artists` WHERE `uuid` = UUID_TO_BIN(?,true)",
|
`SELECT uuid, name FROM artists WHERE uuid = $1`,
|
||||||
uuid).Scan(&genre.UUID, &genre.Name)
|
uuid).Scan(&genre.UUID, &genre.Name)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -28,7 +28,7 @@ func getGenreByUUID(uuid string) Genre {
|
|||||||
func getGenreByName(name string) Genre {
|
func getGenreByName(name string) Genre {
|
||||||
var genre Genre
|
var genre Genre
|
||||||
err := db.QueryRow(
|
err := db.QueryRow(
|
||||||
"SELECT BIN_TO_UUID(`uuid`, true), `name` FROM `artists` WHERE `name` = ?",
|
`SELECT uuid, name FROM artists WHERE name = $1`,
|
||||||
name).Scan(&genre.UUID, &genre.Name)
|
name).Scan(&genre.UUID, &genre.Name)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -41,7 +41,7 @@ func getGenreByName(name string) Genre {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (genre *Genre) updateGenreName(name string, value string) error {
|
func (genre *Genre) updateGenreName(name string, value string) error {
|
||||||
_, err := db.Exec("UPDATE `genres` SET `name` = ? WHERE uuid = UUID_TO_BIN(?, true)", name, genre.UUID)
|
_, err := db.Exec(`UPDATE genres SET name = $1 WHERE uuid = $2`, name, genre.UUID)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ func getSpotifyAuthHandler() spotifyauth.Authenticator {
|
|||||||
setSpotifyEnvVars()
|
setSpotifyEnvVars()
|
||||||
redirectUrl := os.Getenv("GOSCROBBLE_DOMAIN") + "/api/v1/link/spotify"
|
redirectUrl := os.Getenv("GOSCROBBLE_DOMAIN") + "/api/v1/link/spotify"
|
||||||
if redirectUrl == "http://localhost:3000/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"
|
redirectUrl = "http://localhost:42069/api/v1/link/spotify"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,7 +249,7 @@ func (user *User) updateImageDataFromSpotify() {
|
|||||||
auth := getSpotifyAuthHandler()
|
auth := getSpotifyAuthHandler()
|
||||||
client := spotify.New(auth.Client(ctx, token))
|
client := spotify.New(auth.Client(ctx, token))
|
||||||
|
|
||||||
rows, err := db.Query("SELECT BIN_TO_UUID(`uuid`, true), `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 {
|
if err != nil {
|
||||||
log.Printf("Failed to fetch config: %+v", err)
|
log.Printf("Failed to fetch config: %+v", err)
|
||||||
return
|
return
|
||||||
@ -294,7 +294,7 @@ func (user *User) updateImageDataFromSpotify() {
|
|||||||
}
|
}
|
||||||
tx.Commit()
|
tx.Commit()
|
||||||
|
|
||||||
rows, err = db.Query("SELECT BIN_TO_UUID(`uuid`, true), `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 {
|
if err != nil {
|
||||||
log.Printf("Failed to fetch config: %+v", err)
|
log.Printf("Failed to fetch config: %+v", err)
|
||||||
return
|
return
|
||||||
|
@ -20,8 +20,8 @@ type OauthToken struct {
|
|||||||
func getOauthToken(userUuid string, service string) (OauthToken, error) {
|
func getOauthToken(userUuid string, service string) (OauthToken, error) {
|
||||||
var oauth OauthToken
|
var oauth OauthToken
|
||||||
|
|
||||||
err := db.QueryRow("SELECT BIN_TO_UUID(`user`, true), `service`, `access_token`, `refresh_token`, `expiry`, `username`, `last_synced`, `url` FROM `oauth_tokens` "+
|
err := db.QueryRow(`SELECT "user", service, access_token, refresh_token, expiry, username, last_synced, url FROM oauth_tokens `+
|
||||||
"WHERE `user` = UUID_TO_BIN(?, true) AND `service` = ?",
|
`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)
|
userUuid, service).Scan(&oauth.UserUUID, &oauth.Service, &oauth.AccessToken, &oauth.RefreshToken, &oauth.Expiry, &oauth.Username, &oauth.LastSynced, &oauth.URL)
|
||||||
|
|
||||||
if err == sql.ErrNoRows {
|
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 {
|
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`) "+
|
_, err := db.Exec(`REPLACE INTO oauth_tokens ("user", service, access_token, refresh_token, expiry, username, last_synced, url) `+
|
||||||
"VALUES (UUID_TO_BIN(?, true),?,?,?,?,?,?,?)", userUuid, service, token, refresh, expiry, username, lastSynced, url)
|
`VALUES ($1,$2,$3,$4,$5,$6,$7,$8)`, userUuid, service, token, refresh, expiry, username, lastSynced, url)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeOauthToken(userUuid string, service string) error {
|
func removeOauthToken(userUuid string, service string) error {
|
||||||
_, err := db.Exec("DELETE FROM `oauth_tokens` WHERE `user` = UUID_TO_BIN(?, true) AND `service` = ?", userUuid, service)
|
_, err := db.Exec(`DELETE FROM oauth_tokens WHERE "user" = $1 AND service = $2`, userUuid, service)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -59,8 +59,7 @@ func getScrobblesForUser(userUuid string, limit int, page int) (ScrobbleResponse
|
|||||||
var count int
|
var count int
|
||||||
|
|
||||||
// Yeah this isn't great. But for now.. it works! Cache later
|
// Yeah this isn't great. But for now.. it works! Cache later
|
||||||
total, err := getDbCount(
|
total, err := getDbCount(`SELECT COUNT(*) FROM scrobbles WHERE "user" = $1`, userUuid)
|
||||||
"SELECT COUNT(*) FROM `scrobbles` WHERE `user` = UUID_TO_BIN(?, true) ", userUuid)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to fetch scrobble count: %+v", err)
|
log.Printf("Failed to fetch scrobble count: %+v", err)
|
||||||
@ -68,16 +67,16 @@ func getScrobblesForUser(userUuid string, limit int, page int) (ScrobbleResponse
|
|||||||
}
|
}
|
||||||
|
|
||||||
rows, err := db.Query(
|
rows, err := db.Query(
|
||||||
"SELECT BIN_TO_UUID(`scrobbles`.`uuid`, true), `scrobbles`.`created_at`, BIN_TO_UUID(`artists`.`uuid`, true), `artists`.`name`, `albums`.`name`, BIN_TO_UUID(`tracks`.`uuid`, true), `tracks`.`name`, `scrobbles`.`source` FROM `scrobbles` "+
|
`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 tracks ON scrobbles.track = tracks.uuid `+
|
||||||
"JOIN track_artist ON track_artist.track = tracks.uuid "+
|
`JOIN track_artist ON track_artist.track = tracks.uuid `+
|
||||||
"JOIN track_album ON track_album.track = tracks.uuid "+
|
`JOIN track_album ON track_album.track = tracks.uuid `+
|
||||||
"JOIN artists ON track_artist.artist = artists.uuid "+
|
`JOIN artists ON track_artist.artist = artists.uuid `+
|
||||||
"JOIN albums ON track_album.album = albums.uuid "+
|
`JOIN albums ON track_album.album = albums.uuid `+
|
||||||
"JOIN users ON scrobbles.user = users.uuid "+
|
`JOIN users ON scrobbles.user = users.uuid `+
|
||||||
"WHERE user = UUID_TO_BIN(?, true) "+
|
`WHERE "user" = $1 `+
|
||||||
"GROUP BY scrobbles.uuid, albums.uuid "+
|
`GROUP BY scrobbles.uuid, albums.uuid `+
|
||||||
"ORDER BY scrobbles.created_at DESC LIMIT ?",
|
`ORDER BY scrobbles.created_at DESC LIMIT $2`,
|
||||||
userUuid, limit)
|
userUuid, limit)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -111,14 +110,14 @@ func getScrobblesForUser(userUuid string, limit int, page int) (ScrobbleResponse
|
|||||||
}
|
}
|
||||||
|
|
||||||
func insertNewScrobble(user string, track string, source string, timestamp time.Time, ip net.IP, tx *sql.Tx) error {
|
func insertNewScrobble(user string, track string, source string, timestamp time.Time, ip net.IP, tx *sql.Tx) error {
|
||||||
_, err := tx.Exec("INSERT INTO `scrobbles` (`uuid`, `created_at`, `created_ip`, `user`, `track`, `source`) "+
|
_, err := tx.Exec(`INSERT INTO scrobbles (uuid, created_at, created_ip, "user", track, source) `+
|
||||||
"VALUES (UUID_TO_BIN(?, true), ?, ?, UUID_TO_BIN(?, true), UUID_TO_BIN(?, true), ?)", newUUID(), timestamp, ip, user, track, source)
|
`VALUES ($1,$2,$3,$4,$5,$6)`, newUUID(), timestamp, ip.String(), user, track, source)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkIfScrobbleExists(userUuid string, timestamp time.Time, source string) bool {
|
func checkIfScrobbleExists(userUuid string, timestamp time.Time, source string) bool {
|
||||||
count, err := getDbCount("SELECT COUNT(*) FROM `scrobbles` WHERE `user` = UUID_TO_BIN(?, true) AND `created_at` = ? AND `source` = ?",
|
count, err := getDbCount(`SELECT COUNT(*) FROM scrobbles WHERE "user" = $1 AND created_at = $2 AND source = $3`,
|
||||||
userUuid, timestamp, source)
|
userUuid, timestamp, source)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -53,25 +53,25 @@ func getAllStats() (StatsRequest, error) {
|
|||||||
statsReq := StatsRequest{}
|
statsReq := StatsRequest{}
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
statsReq.Users, err = getDbCount("SELECT COUNT(*) FROM `users` WHERE `active` = 1")
|
statsReq.Users, err = getDbCount(`SELECT COUNT(*) FROM users WHERE active = true`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to fetch user count: %+v", err)
|
log.Printf("Failed to fetch user count: %+v", err)
|
||||||
return statsReq, errors.New("Failed to fetch stats")
|
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 {
|
if err != nil {
|
||||||
log.Printf("Failed to fetch scrobble count: %+v", err)
|
log.Printf("Failed to fetch scrobble count: %+v", err)
|
||||||
return statsReq, errors.New("Failed to fetch stats")
|
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 {
|
if err != nil {
|
||||||
log.Printf("Failed to fetch track count: %+v", err)
|
log.Printf("Failed to fetch track count: %+v", err)
|
||||||
return statsReq, errors.New("Failed to fetch stats")
|
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 {
|
if err != nil {
|
||||||
log.Printf("Failed to fetch artist count: %+v", err)
|
log.Printf("Failed to fetch artist count: %+v", err)
|
||||||
return statsReq, errors.New("Failed to fetch stats")
|
return statsReq, errors.New("Failed to fetch stats")
|
||||||
|
@ -29,7 +29,7 @@ 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 == "" {
|
||||||
err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true) FROM `users` WHERE `token` = ? AND `active` = 1", token).Scan(&uuid)
|
err := db.QueryRow(`SELECT uuid FROM users WHERE token = $1 AND active = true`, token).Scan(&uuid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.New("Invalid Token")
|
return "", errors.New("Invalid Token")
|
||||||
}
|
}
|
||||||
@ -43,21 +43,21 @@ func getUserUuidForToken(token string) (string, error) {
|
|||||||
|
|
||||||
func insertRefreshToken(userUuid string, token string) error {
|
func insertRefreshToken(userUuid string, token string) error {
|
||||||
uuid := newUUID()
|
uuid := newUUID()
|
||||||
_, err := db.Exec("INSERT INTO `refresh_tokens` (`uuid`, `user`, `token`) VALUES (UUID_TO_BIN(?,true),UUID_TO_BIN(?,true),?)",
|
_, err := db.Exec(`INSERT INTO refresh_tokens (uuid, "user", token) VALUES ($1,$2,$3)`,
|
||||||
uuid, userUuid, token)
|
uuid, userUuid, token)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteRefreshToken(token string) error {
|
func deleteRefreshToken(token string) error {
|
||||||
_, err := db.Exec("DELETE FROM `refresh_tokens` WHERE `token` = ?", token)
|
_, err := db.Exec(`DELETE FROM refresh_tokens WHERE token = $1`, token)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func isValidRefreshToken(refreshTokenStr string) (User, error) {
|
func isValidRefreshToken(refreshTokenStr string) (User, error) {
|
||||||
var refresh RefreshToken
|
var refresh RefreshToken
|
||||||
err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), BIN_TO_UUID(`user`, true), `token`, `expiry` FROM `refresh_tokens` WHERE `token` = ?",
|
err := db.QueryRow(`SELECT uuid, "user", token, expiry FROM refresh_tokens WHERE token = $1`,
|
||||||
refreshTokenStr).Scan(&refresh.UUID, &refresh.User, &refresh.Token, &refresh.Expiry)
|
refreshTokenStr).Scan(&refresh.UUID, &refresh.User, &refresh.Token, &refresh.Expiry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return User{}, errors.New("Invalid Refresh Token")
|
return User{}, errors.New("Invalid Refresh Token")
|
||||||
|
@ -82,7 +82,7 @@ func insertTrack(name string, legnth int, mbid string, spotifyId string, album s
|
|||||||
func getTrackByCol(col string, val string, tx *sql.Tx) Track {
|
func getTrackByCol(col string, val string, tx *sql.Tx) Track {
|
||||||
var track Track
|
var track Track
|
||||||
err := tx.QueryRow(
|
err := tx.QueryRow(
|
||||||
"SELECT BIN_TO_UUID(`uuid`, true), `name`, IFNULL(`desc`,''), IFNULL(`img`,''), `mbid` FROM `tracks` WHERE `"+col+"` = ? 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)
|
val).Scan(&track.UUID, &track.Name, &track.Desc, &track.Img, &track.MusicBrainzID)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -98,11 +98,11 @@ func getTrackWithArtists(name string, artists []string, album string, tx *sql.Tx
|
|||||||
var track Track
|
var track Track
|
||||||
artistString := strings.Join(artists, "','")
|
artistString := strings.Join(artists, "','")
|
||||||
err := tx.QueryRow(
|
err := tx.QueryRow(
|
||||||
"SELECT BIN_TO_UUID(`uuid`, true), `name`, IFNULL(`desc`,''), IFNULL(`img`,''), `mbid` FROM `tracks` "+
|
`SELECT uuid, name, COALESCE(desc,''), COALESCE(img,''), mbid FROM tracks `+
|
||||||
"LEFT JOIN `track_artist` ON `tracks`.`uuid` = `track_artist`.`track` "+
|
`LEFT JOIN track_artist ON tracks.uuid = track_artist.track `+
|
||||||
"LEFT JOIN `track_album` ON `tracks`.`uuid` = `track_album`.`track` "+
|
`LEFT JOIN track_album ON tracks.uuid = track_album.track `+
|
||||||
"WHERE `name` = ? AND BIN_TO_UUID(`track_artist`.`artist`, true) IN ('"+artistString+"') "+
|
`WHERE name = $1 AND track_artist.artistIN ('`+artistString+`') `+
|
||||||
"AND BIN_TO_UUID(`track_album`.`album`,true) = ? LIMIT 1",
|
`AND track_album.album = $2 LIMIT 1`,
|
||||||
name, album).Scan(&track.UUID, &track.Name, &track.Desc, &track.Img, &track.MusicBrainzID)
|
name, album).Scan(&track.UUID, &track.Name, &track.Desc, &track.Img, &track.MusicBrainzID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != sql.ErrNoRows {
|
if err != sql.ErrNoRows {
|
||||||
@ -192,8 +192,8 @@ func insertNewTrack(track *Track, name string, length int, mbid string, spotifyI
|
|||||||
track.MusicBrainzID = mbid
|
track.MusicBrainzID = mbid
|
||||||
track.SpotifyID = spotifyId
|
track.SpotifyID = spotifyId
|
||||||
|
|
||||||
_, err := tx.Exec("INSERT INTO `tracks` (`uuid`, `name`, `length`, `mbid`, `spotify_id`) "+
|
_, err := tx.Exec(`INSERT INTO tracks (uuid, name, length, mbid, spotify_id) `+
|
||||||
"VALUES (UUID_TO_BIN(?, true),?,?,?,?)", track.UUID, track.Name, track.Length, track.MusicBrainzID, track.SpotifyID)
|
`VALUES ($1,$2,$3,$4,$5)`, track.UUID, track.Name, track.Length, track.MusicBrainzID, track.SpotifyID)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -213,8 +213,8 @@ func (track *Track) linkTrack(album string, artists []string, tx *sql.Tx) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (track *Track) linkTrackToAlbum(albumUuid string, tx *sql.Tx) error {
|
func (track *Track) linkTrackToAlbum(albumUuid string, tx *sql.Tx) error {
|
||||||
_, err := tx.Exec("INSERT INTO `track_album` (`track`, `album`) "+
|
_, err := tx.Exec(`INSERT INTO track_album (track, album) `+
|
||||||
"VALUES (UUID_TO_BIN(?, true), UUID_TO_BIN(?, true))", track.UUID, albumUuid)
|
`VALUES ($1, $2)`, track.UUID, albumUuid)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -222,8 +222,8 @@ func (track *Track) linkTrackToAlbum(albumUuid string, tx *sql.Tx) error {
|
|||||||
func (track *Track) linkTrackToArtists(artists []string, tx *sql.Tx) error {
|
func (track *Track) linkTrackToArtists(artists []string, tx *sql.Tx) error {
|
||||||
var err error
|
var err error
|
||||||
for _, artist := range artists {
|
for _, artist := range artists {
|
||||||
_, err = tx.Exec("INSERT INTO `track_artist` (`track`, `artist`) "+
|
_, err = tx.Exec(`INSERT INTO track_artist (track, artist) `+
|
||||||
"VALUES (UUID_TO_BIN(?, true),UUID_TO_BIN(?, true))", track.UUID, artist)
|
`VALUES ($1,$2)`, track.UUID, artist)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -233,18 +233,18 @@ func (track *Track) linkTrackToArtists(artists []string, tx *sql.Tx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (track *Track) updateTrack(col string, val string, tx *sql.Tx) error {
|
func (track *Track) updateTrack(col string, val string, tx *sql.Tx) error {
|
||||||
_, err := tx.Exec("UPDATE `tracks` SET `"+col+"` = ? WHERE `uuid` = UUID_TO_BIN(?,true)", val, track.UUID)
|
_, err := tx.Exec(`UPDATE tracks SET "`+col+`" = $1 WHERE uuid = $2`, val, track.UUID)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTrackByUUID(uuid string) (Track, error) {
|
func getTrackByUUID(uuid string) (Track, error) {
|
||||||
var track Track
|
var track Track
|
||||||
err := db.QueryRow("SELECT BIN_TO_UUID(`tracks`.`uuid`, true), `tracks`.`name`, IFNULL(`albums`.`desc`,''), IFNULL(BIN_TO_UUID(`albums`.`uuid`, true),''), `tracks`.`length`, `tracks`.`mbid`, `tracks`.`spotify_id` "+
|
err := db.QueryRow(`SELECT tracks.uuid, tracks.name, COALESCE(albums.desc,''), COALESCE(albums.uuid,''), tracks.length, tracks.mbid, tracks.spotify_id `+
|
||||||
"FROM `tracks` "+
|
`FROM tracks `+
|
||||||
"LEFT JOIN track_album ON track_album.track = tracks.uuid "+
|
`LEFT JOIN track_album ON track_album.track = tracks.uuid `+
|
||||||
"LEFT JOIN albums ON track_album.album = albums.uuid "+
|
`LEFT JOIN albums ON track_album.album = albums.uuid `+
|
||||||
"WHERE `tracks`.`uuid` = UUID_TO_BIN(?, true)",
|
`WHERE tracks.uuid = $1`,
|
||||||
uuid).Scan(&track.UUID, &track.Name, &track.Desc, &track.Img, &track.Length, &track.MusicBrainzID, &track.SpotifyID)
|
uuid).Scan(&track.UUID, &track.Name, &track.Desc, &track.Img, &track.Length, &track.MusicBrainzID, &track.SpotifyID)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -258,27 +258,37 @@ func getTrackByUUID(uuid string) (Track, error) {
|
|||||||
func getTopTracks(userUuid string, dayRange string) (TopTracks, error) {
|
func getTopTracks(userUuid string, dayRange string) (TopTracks, error) {
|
||||||
var topTracks TopTracks
|
var topTracks TopTracks
|
||||||
|
|
||||||
dateClause := ""
|
// dateClause := ""
|
||||||
if dayRange != "" {
|
// if dayRange != "" {
|
||||||
dateClause = " AND DATE(created_at) > SUBDATE(CURRENT_DATE, " + dayRange + ") "
|
// dateClause = " AND DATE(created_at) > SUBDATE(CURRENT_DATE, " + dayRange + ") "
|
||||||
}
|
// }
|
||||||
|
|
||||||
whereClause := ""
|
// whereClause := ""
|
||||||
if userUuid != "0" {
|
// if userUuid != "0" {
|
||||||
whereClause = "WHERE `scrobbles`.`user` = UUID_TO_BIN('" + userUuid + "', true) "
|
// whereClause = "WHERE `scrobbles`.`user` = UUID_TO_BIN('" + userUuid + "', true) "
|
||||||
}
|
// }
|
||||||
|
|
||||||
rows, err := db.Query("SELECT BIN_TO_UUID(`tracks`.`uuid`, true), `tracks`.`name`, IFNULL(BIN_TO_UUID(`albums`.`uuid`, true),''), count(*) " +
|
// rows, err := db.Query("SELECT BIN_TO_UUID(`tracks`.`uuid`, true), `tracks`.`name`, IFNULL(BIN_TO_UUID(`albums`.`uuid`, true),''), count(*) " +
|
||||||
"FROM `scrobbles` " +
|
// "FROM `scrobbles` " +
|
||||||
"JOIN `tracks` ON `tracks`.`uuid` = `scrobbles`.`track` " +
|
// "JOIN `tracks` ON `tracks`.`uuid` = `scrobbles`.`track` " +
|
||||||
"JOIN track_album ON track_album.track = tracks.uuid " +
|
// "JOIN track_album ON track_album.track = tracks.uuid " +
|
||||||
"JOIN albums ON track_album.album = albums.uuid " +
|
// "JOIN albums ON track_album.album = albums.uuid " +
|
||||||
whereClause +
|
// whereClause +
|
||||||
dateClause +
|
// dateClause +
|
||||||
"GROUP BY `scrobbles`.`track` " +
|
// "GROUP BY `scrobbles`.`track` " +
|
||||||
"ORDER BY count(*) DESC " +
|
// "ORDER BY count(*) DESC " +
|
||||||
"LIMIT 14")
|
// "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 {
|
if err != nil {
|
||||||
log.Printf("Failed to fetch top tracks: %+v", err)
|
log.Printf("Failed to fetch top tracks: %+v", err)
|
||||||
return topTracks, errors.New("Failed to fetch top tracks")
|
return topTracks, errors.New("Failed to fetch top tracks")
|
||||||
@ -317,9 +327,9 @@ func (track *Track) loadExtraTrackInfo() error {
|
|||||||
func (track *Track) getArtistsForTrack() error {
|
func (track *Track) getArtistsForTrack() error {
|
||||||
artists := []Artist{}
|
artists := []Artist{}
|
||||||
|
|
||||||
rows, err := db.Query("SELECT BIN_TO_UUID(`track_artist`.`artist`, true) "+
|
rows, err := db.Query(`SELECT track_artist.artist `+
|
||||||
"FROM `track_artist` "+
|
`FROM track_artist `+
|
||||||
"WHERE `track_artist`.`track` = UUID_TO_BIN(?, true)",
|
`WHERE track_artist.track = $1`,
|
||||||
track.UUID)
|
track.UUID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to fetch artists for track: %+v", err)
|
log.Printf("Failed to fetch artists for track: %+v", err)
|
||||||
@ -349,9 +359,9 @@ func (track *Track) getArtistsForTrack() error {
|
|||||||
func (track *Track) getAlbumsForTrack() error {
|
func (track *Track) getAlbumsForTrack() error {
|
||||||
albums := []Album{}
|
albums := []Album{}
|
||||||
|
|
||||||
rows, err := db.Query("SELECT BIN_TO_UUID(`track_album`.`album`, true) "+
|
rows, err := db.Query(`SELECT track_album.album `+
|
||||||
"FROM `track_album` "+
|
`FROM track_album `+
|
||||||
"WHERE `track_album`.`track` = UUID_TO_BIN(?, true)",
|
`WHERE track_album.track = $1`,
|
||||||
track.UUID)
|
track.UUID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to fetch album for track: %+v", err)
|
log.Printf("Failed to fetch album for track: %+v", err)
|
||||||
@ -384,7 +394,7 @@ func getTopUsersForTrackUUID(trackUUID string, limit int, page int) (TopUserResp
|
|||||||
var count int
|
var count int
|
||||||
|
|
||||||
total, err := getDbCount(
|
total, err := getDbCount(
|
||||||
"SELECT COUNT(*) FROM `scrobbles` WHERE `track` = UUID_TO_BIN(?, true)", trackUUID)
|
`SELECT COUNT(*) FROM scrobbles WHERE track = $1 GROUP BY track, "user"`, trackUUID)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to fetch scrobble count: %+v", err)
|
log.Printf("Failed to fetch scrobble count: %+v", err)
|
||||||
@ -392,12 +402,12 @@ func getTopUsersForTrackUUID(trackUUID string, limit int, page int) (TopUserResp
|
|||||||
}
|
}
|
||||||
|
|
||||||
rows, err := db.Query(
|
rows, err := db.Query(
|
||||||
"SELECT BIN_TO_UUID(`scrobbles`.`user`, true), `users`.`username`, COUNT(*) "+
|
`SELECT scrobbles.user, users.username, COUNT(*) `+
|
||||||
"FROM `scrobbles` "+
|
`FROM scrobbles `+
|
||||||
"JOIN `users` ON `scrobbles`.`user` = `users`.`uuid` "+
|
`JOIN users ON scrobbles."user" = users.uuid `+
|
||||||
"WHERE `track` = UUID_TO_BIN(?, true) "+
|
`WHERE track = $1 `+
|
||||||
"GROUP BY `scrobbles`.`user` "+
|
`GROUP BY scrobbles."user" `+
|
||||||
"ORDER BY COUNT(*) DESC LIMIT ?",
|
`ORDER BY COUNT(*) DESC LIMIT $2`,
|
||||||
trackUUID, limit)
|
trackUUID, limit)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -116,7 +116,7 @@ func loginUser(logReq *RequestRequest, 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`, `admin`, `mod` FROM `users` WHERE `email` = ? AND `active` = 1",
|
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)
|
logReq.Username).Scan(&user.UUID, &user.Username, &user.Email, &user.Password, &user.Admin, &user.Mod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
@ -124,7 +124,7 @@ func loginUser(logReq *RequestRequest, ip net.IP) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), `username`, `email`, `password`, `admin`, `mod` FROM `users` WHERE `username` = ? AND `active` = 1",
|
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)
|
logReq.Username).Scan(&user.UUID, &user.Username, &user.Email, &user.Password, &user.Admin, &user.Mod)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return resp, errors.New("Invalid Username or Password")
|
return resp, errors.New("Invalid Username or Password")
|
||||||
@ -153,20 +153,24 @@ func loginUser(logReq *RequestRequest, ip net.IP) ([]byte, error) {
|
|||||||
// insertUser - Does the dirtywork!
|
// insertUser - Does the dirtywork!
|
||||||
func insertUser(username string, email string, password []byte, ip net.IP) error {
|
func insertUser(username string, email string, password []byte, ip net.IP) error {
|
||||||
token := generateToken(32)
|
token := generateToken(32)
|
||||||
_, err := db.Exec("INSERT INTO users (uuid, created_at, created_ip, modified_at, modified_ip, username, email, password, token) "+
|
uuid := newUUID()
|
||||||
"VALUES (UUID_TO_BIN(UUID(), true),NOW(),?,NOW(),?,?,?,?,?)", ip, ip, username, email, password, token)
|
|
||||||
|
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)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) updateUser(field string, value string, ip net.IP) error {
|
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)
|
_, err := db.Exec(`UPDATE users SET "`+field+`" = $1, modified_at = NOW(), modified_ip = $2 WHERE uuid = $3`, value, ip, user.UUID)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) updateUserDirect(field string, value string) error {
|
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)
|
_, err := db.Exec(`UPDATE users SET "`+field+`" = $1 WHERE uuid = $2`, value, user.UUID)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -189,7 +193,7 @@ func isValidPassword(password string, user User) bool {
|
|||||||
// userAlreadyExists - Returns bool indicating if a record exists for either username or email
|
// userAlreadyExists - Returns bool indicating if a record exists for either username or email
|
||||||
// Using two look ups to make use of DB indexes.
|
// Using two look ups to make use of DB indexes.
|
||||||
func userAlreadyExists(req *RequestRequest) bool {
|
func userAlreadyExists(req *RequestRequest) bool {
|
||||||
count, err := getDbCount("SELECT COUNT(*) FROM users WHERE username = ?", req.Username)
|
count, err := getDbCount(`SELECT COUNT(*) FROM users WHERE username = $1`, req.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error querying for duplicate users: %v", err)
|
fmt.Printf("Error querying for duplicate users: %v", err)
|
||||||
return true
|
return true
|
||||||
@ -201,7 +205,7 @@ func userAlreadyExists(req *RequestRequest) bool {
|
|||||||
|
|
||||||
if req.Email != "" {
|
if req.Email != "" {
|
||||||
// Only run email check if there's an email...
|
// Only run email check if there's an email...
|
||||||
count, err = getDbCount("SELECT COUNT(*) FROM users WHERE email = ?", req.Email)
|
count, err = getDbCount(`SELECT COUNT(*) FROM users WHERE email = $1`, req.Email)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -214,7 +218,7 @@ func userAlreadyExists(req *RequestRequest) bool {
|
|||||||
|
|
||||||
func getUserByUUID(uuid string) (User, error) {
|
func getUserByUUID(uuid string) (User, error) {
|
||||||
var user User
|
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`, `mod`, `timezone`, `token` FROM `users` WHERE `uuid` = UUID_TO_BIN(?, true) AND `active` = 1",
|
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)
|
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 {
|
if err == sql.ErrNoRows {
|
||||||
@ -226,7 +230,7 @@ func getUserByUUID(uuid string) (User, error) {
|
|||||||
|
|
||||||
func getUserByUsername(username string) (User, error) {
|
func getUserByUsername(username string) (User, error) {
|
||||||
var user User
|
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`, `mod`, `timezone`, `token` FROM `users` WHERE `username` = ? AND `active` = 1",
|
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)
|
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 {
|
if err == sql.ErrNoRows {
|
||||||
@ -238,7 +242,7 @@ func getUserByUsername(username string) (User, error) {
|
|||||||
|
|
||||||
func getUserByEmail(email string) (User, error) {
|
func getUserByEmail(email string) (User, error) {
|
||||||
var user User
|
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`, `mod`, `timezone`, `token` FROM `users` WHERE `email` = ? AND `active` = 1",
|
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)
|
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 {
|
if err == sql.ErrNoRows {
|
||||||
@ -250,8 +254,8 @@ func getUserByEmail(email string) (User, error) {
|
|||||||
|
|
||||||
func getUserByResetToken(token string) (User, error) {
|
func getUserByResetToken(token string) (User, error) {
|
||||||
var user User
|
var user User
|
||||||
err := db.QueryRow("SELECT BIN_TO_UUID(`users`.`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `username`, `email`, `password`, `verified`, `admin`, `mod`, `timezone`, `token` FROM `users` "+
|
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` = ? AND `active` = 1",
|
`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)
|
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 {
|
if err == sql.ErrNoRows {
|
||||||
@ -282,26 +286,26 @@ func (user *User) sendResetEmail(ip net.IP) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) saveResetToken(token string, expiry time.Time) error {
|
func (user *User) saveResetToken(token string, expiry time.Time) error {
|
||||||
_, _ = db.Exec("DELETE FROM `resettoken` WHERE `user` = UUID_TO_BIN(?, true)", user.UUID)
|
_, _ = db.Exec(`DELETE FROM resettoken WHERE "user" = $1`, user.UUID)
|
||||||
_, err := db.Exec("INSERT INTO `resettoken` (`user`, `token`, `expiry`) "+
|
_, err := db.Exec(`INSERT INTO resettoken ("user", token, expiry) `+
|
||||||
"VALUES (UUID_TO_BIN(?, true),?, ?)", user.UUID, token, expiry)
|
`VALUES ($1,$2,$3)`, user.UUID, token, expiry)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func clearOldResetTokens() {
|
func clearOldResetTokens() {
|
||||||
_, _ = db.Exec("DELETE FROM `resettoken` WHERE `expiry` < NOW()")
|
_, _ = db.Exec(`DELETE FROM resettoken WHERE expiry < NOW()`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func clearResetToken(token string) error {
|
func clearResetToken(token string) error {
|
||||||
_, err := db.Exec("DELETE FROM `resettoken` WHERE `token` = ?", token)
|
_, err := db.Exec(`DELETE FROM resettoken WHERE token = $1`, token)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkResetToken - If a token exists check it
|
// checkResetToken - If a token exists check it
|
||||||
func checkResetToken(token string) (bool, error) {
|
func checkResetToken(token string) (bool, error) {
|
||||||
count, err := getDbCount("SELECT COUNT(*) FROM `resettoken` WHERE `token` = ? ", token)
|
count, err := getDbCount(`SELECT COUNT(*) FROM resettoken WHERE token = $1`, token)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@ -316,7 +320,7 @@ func (user *User) updatePassword(newPassword string, ip net.IP) error {
|
|||||||
return errors.New("Bad password")
|
return errors.New("Bad password")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = db.Exec("UPDATE `users` SET `password` = ? WHERE `uuid` = UUID_TO_BIN(?, true)", hash, user.UUID)
|
_, err = db.Exec(`UPDATE users SET password = $1 WHERE uuid = $2`, hash, user.UUID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Failed to update password")
|
return errors.New("Failed to update password")
|
||||||
}
|
}
|
||||||
@ -334,8 +338,8 @@ func (user *User) getNavidromeTokens() (OauthToken, error) {
|
|||||||
|
|
||||||
func getAllSpotifyUsers() ([]User, error) {
|
func getAllSpotifyUsers() ([]User, error) {
|
||||||
users := make([]User, 0)
|
users := make([]User, 0)
|
||||||
rows, err := db.Query("SELECT BIN_TO_UUID(`users`.`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `users`.`username`, `email`, `password`, `verified`, `admin`, `mod`, `timezone` FROM `users` " +
|
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` = 1")
|
`JOIN oauth_tokens ON oauth_tokens."user" = users.uuid AND oauth_tokens.service = 'spotify' WHERE users.active = true`)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to fetch spotify users: %+v", err)
|
log.Printf("Failed to fetch spotify users: %+v", err)
|
||||||
@ -366,8 +370,8 @@ func getAllSpotifyUsers() ([]User, error) {
|
|||||||
|
|
||||||
func getAllNavidromeUsers() ([]User, error) {
|
func getAllNavidromeUsers() ([]User, error) {
|
||||||
users := make([]User, 0)
|
users := make([]User, 0)
|
||||||
rows, err := db.Query("SELECT BIN_TO_UUID(`users`.`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `users`.`username`, `email`, `password`, `verified`, `admin`, `mod`, `timezone` FROM `users` " +
|
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` = 1")
|
`JOIN oauth_tokens ON oauth_tokens."user" = users.uuid AND oauth_tokens.service = 'navidrome' WHERE users.active = true`)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to fetch navidrome users: %+v", err)
|
log.Printf("Failed to fetch navidrome users: %+v", err)
|
||||||
|
@ -87,7 +87,7 @@ func getUserIp(r *http.Request) net.IP {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if host == "" {
|
if host == "" {
|
||||||
host = "0.0.0.0"
|
host = "0.0.0.0/0"
|
||||||
}
|
}
|
||||||
|
|
||||||
ip = net.ParseIP(host)
|
ip = net.ParseIP(host)
|
||||||
@ -103,7 +103,7 @@ func Inet_Aton(ip net.IP) int64 {
|
|||||||
|
|
||||||
// Inet6_Aton converts an IP Address (IPv4 or IPv6) net.IP object to a hexadecimal
|
// Inet6_Aton converts an IP Address (IPv4 or IPv6) net.IP object to a hexadecimal
|
||||||
// representaiton. This function is the equivalent of
|
// representaiton. This function is the equivalent of
|
||||||
// inet6_aton({{ ip address }}) in MySQL.
|
// inet6_aton({{ ip address }}) in Postgres.
|
||||||
func Inet6_Aton(ip net.IP) string {
|
func Inet6_Aton(ip net.IP) string {
|
||||||
ipv4 := false
|
ipv4 := false
|
||||||
if ip.To4() != nil {
|
if ip.To4() != nil {
|
||||||
|
@ -1 +1 @@
|
|||||||
ALTER TABLE `tracks` DROP COLUMN `length`;
|
ALTER TABLE tracks DROP COLUMN length;
|
@ -1 +1 @@
|
|||||||
ALTER TABLE `tracks` ADD COLUMN `length` INT NOT NULL DEFAULT 0;
|
ALTER TABLE tracks ADD COLUMN length INT NOT NULL DEFAULT 0;
|
@ -1 +1 @@
|
|||||||
DROP TABLE IF EXISTS `genres`;
|
DROP TABLE IF EXISTS genres;
|
@ -1,5 +1,6 @@
|
|||||||
CREATE TABLE IF NOT EXISTS `genres` (
|
CREATE TABLE IF NOT EXISTS genres (
|
||||||
`uuid` BINARY(16) PRIMARY KEY,
|
uuid uuid PRIMARY KEY,
|
||||||
`name` VARCHAR(255) NOT NULL,
|
name VARCHAR(255) NOT NULL
|
||||||
KEY `nameLookup` (`name`)
|
);
|
||||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
|
|
||||||
|
CREATE INDEX genresNameLookup ON genres (name)
|
@ -1 +1 @@
|
|||||||
DROP TABLE IF EXISTS `refresh_tokens`;
|
DROP TABLE IF EXISTS refresh_tokens;
|
@ -1,10 +1,11 @@
|
|||||||
CREATE TABLE IF NOT EXISTS `refresh_tokens` (
|
CREATE TABLE IF NOT EXISTS refresh_tokens (
|
||||||
`uuid` BINARY(16) PRIMARY KEY,
|
uuid uuid PRIMARY KEY,
|
||||||
`user` BINARY(16),
|
"user" uuid,
|
||||||
`token` VARCHAR(64) NOT NULL,
|
token VARCHAR(64) NOT NULL,
|
||||||
`expiry` DATETIME NOT NULL DEFAULT NOW(),
|
expiry timestamptz NOT NULL DEFAULT NOW(),
|
||||||
KEY `userLookup` (`user`),
|
FOREIGN KEY ("user") REFERENCES users(uuid)
|
||||||
KEY `tokenLookup` (`token`),
|
);
|
||||||
KEY `expiryLookup` (`expiry`),
|
|
||||||
FOREIGN KEY (user) REFERENCES users(uuid)
|
CREATE INDEX refreshtokensUserLookup ON refresh_tokens ("user");
|
||||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
|
CREATE INDEX refreshtokensTokenLookup ON refresh_tokens (token);
|
||||||
|
CREATE INDEX refreshtokensExpiryLookup ON refresh_tokens (expiry);
|
||||||
|
@ -1 +1 @@
|
|||||||
ALTER TABLE `users` DROP COLUMN `mod`;
|
ALTER TABLE users DROP COLUMN mod;
|
@ -1 +1 @@
|
|||||||
ALTER TABLE `users` ADD COLUMN `mod` TINYINT(1) NOT NULL DEFAULT 0 AFTER `admin`;
|
ALTER TABLE users ADD COLUMN mod BOOL NOT NULL DEFAULT FALSE;
|
@ -1 +1 @@
|
|||||||
DROP TABLE IF EXISTS `config`;
|
DROP TABLE IF EXISTS config;
|
@ -1,5 +1,5 @@
|
|||||||
CREATE TABLE IF NOT EXISTS `config` (
|
CREATE TABLE IF NOT EXISTS config (
|
||||||
`key` VARCHAR(255) NOT NULL,
|
key VARCHAR(255) NOT NULL,
|
||||||
`value` VARCHAR(255) NULL DEFAULT NULL,
|
value VARCHAR(255) NULL,
|
||||||
PRIMARY KEY(`key`)
|
PRIMARY KEY(key)
|
||||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
|
);
|
@ -1 +1 @@
|
|||||||
DROP TABLE IF EXISTS `users`;
|
DROP TABLE IF EXISTS users;
|
@ -1,17 +1,18 @@
|
|||||||
CREATE TABLE IF NOT EXISTS `users` (
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
`uuid` BINARY(16) PRIMARY KEY,
|
uuid uuid PRIMARY KEY,
|
||||||
`created_at` DATETIME NOT NULL DEFAULT NOW(),
|
created_at timestamptz NOT NULL DEFAULT NOW(),
|
||||||
`created_ip` VARBINARY(16) NULL DEFAULT NULL,
|
created_ip INET NULL,
|
||||||
`modified_at` DATETIME NOT NULL DEFAULT NOW(),
|
modified_at timestamptz NOT NULL DEFAULT NOW(),
|
||||||
`modified_ip` VARBINARY(16) NULL DEFAULT NULL,
|
modified_ip INET NULL,
|
||||||
`username` VARCHAR(64) NOT NULL,
|
username VARCHAR(64) NOT NULL,
|
||||||
`password` VARCHAR(60) NOT NULL,
|
password VARCHAR(60) NOT NULL,
|
||||||
`email` VARCHAR(255) NULL DEFAULT NULL,
|
email VARCHAR(255) NULL,
|
||||||
`verified` TINYINT(1) NOT NULL DEFAULT 0,
|
verified BOOL NOT NULL DEFAULT FALSE,
|
||||||
`active` TINYINT(1) NOT NULL DEFAULT 1,
|
active BOOL NOT NULL DEFAULT TRUE,
|
||||||
`admin` TINYINT(1) NOT NULL DEFAULT 0,
|
admin BOOL NOT NULL DEFAULT FALSE,
|
||||||
`private` TINYINT(1) NOT NULL DEFAULT 0,
|
private BOOL NOT NULL DEFAULT FALSE,
|
||||||
`timezone` VARCHAR(100) NOT NULL DEFAULT 'Pacific/Auckland',
|
timezone VARCHAR(100) NOT NULL DEFAULT 'Pacific/Auckland'
|
||||||
KEY `usernameLookup` (`username`, `active`),
|
);
|
||||||
KEY `emailLookup` (`email`, `active`)
|
|
||||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
|
CREATE INDEX usersUsernameLookup ON users (username, active);
|
||||||
|
CREATE INDEX usersEmailLookup ON users (email, active);
|
@ -1,8 +1,8 @@
|
|||||||
START TRANSACTION;
|
START TRANSACTION;
|
||||||
DROP TABLE IF EXISTS `artists`;
|
DROP TABLE IF EXISTS artists;
|
||||||
DROP TABLE IF EXISTS `albums`;
|
DROP TABLE IF EXISTS albums;
|
||||||
DROP TABLE IF EXISTS `tracks`;
|
DROP TABLE IF EXISTS tracks;
|
||||||
DROP TABLE IF EXISTS `track_artist`;
|
DROP TABLE IF EXISTS track_artist;
|
||||||
DROP TABLE IF EXISTS `scrobble_track`;
|
DROP TABLE IF EXISTS scrobble_track;
|
||||||
|
|
||||||
COMMIT;
|
COMMIT;
|
@ -1,69 +1,71 @@
|
|||||||
START TRANSACTION;
|
START TRANSACTION;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `links` (
|
CREATE TABLE IF NOT EXISTS links (
|
||||||
`scrobble` BINARY(16) NOT NULL,
|
scrobble uuid NOT NULL,
|
||||||
`track` BINARY(16) NOT NULL,
|
track uuid NOT NULL,
|
||||||
PRIMARY KEY (`scrobble`, `track`),
|
PRIMARY KEY (scrobble, track)
|
||||||
KEY `trackLookup` (`track`)
|
);
|
||||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `artists` (
|
CREATE INDEX trackLookup ON links (track);
|
||||||
`uuid` BINARY(16) PRIMARY KEY,
|
|
||||||
`name` VARCHAR(255) NOT NULL,
|
|
||||||
`desc` TEXT,
|
|
||||||
`img` VARCHAR(255) DEFAULT NULL
|
|
||||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `albums` (
|
CREATE TABLE IF NOT EXISTS artists (
|
||||||
`uuid` BINARY(16) PRIMARY KEY,
|
uuid uuid PRIMARY KEY,
|
||||||
`name` VARCHAR(255) NOT NULL,
|
name VARCHAR(255) NOT NULL,
|
||||||
`desc` TEXT,
|
"desc" TEXT,
|
||||||
`img` VARCHAR(255) DEFAULT NULL
|
img VARCHAR(255)
|
||||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `tracks` (
|
CREATE TABLE IF NOT EXISTS albums (
|
||||||
`uuid` BINARY(16) PRIMARY KEY,
|
uuid uuid PRIMARY KEY,
|
||||||
`name` VARCHAR(255) NOT NULL,
|
name VARCHAR(255) NOT NULL,
|
||||||
`desc` TEXT,
|
"desc" TEXT,
|
||||||
`img` VARCHAR(255) DEFAULT NULL
|
img VARCHAR(255)
|
||||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `scrobbles` (
|
CREATE TABLE IF NOT EXISTS tracks (
|
||||||
`uuid` BINARY(16) PRIMARY KEY,
|
uuid uuid PRIMARY KEY,
|
||||||
`created_at` DATETIME NOT NULL,
|
name VARCHAR(255) NOT NULL,
|
||||||
`created_ip` VARBINARY(16) NULL DEFAULT NULL,
|
"desc" TEXT,
|
||||||
`user` BINARY(16) NOT NULL,
|
img VARCHAR(255)
|
||||||
`track` BINARY(16) NOT NULL,
|
);
|
||||||
`source` VARCHAR(100) NOT NULL DEFAULT '',
|
|
||||||
KEY `userLookup` (`user`),
|
CREATE TABLE IF NOT EXISTS scrobbles (
|
||||||
KEY `dateLookup` (`created_at`),
|
uuid uuid PRIMARY KEY,
|
||||||
KEY `sourceLookup` (`source`),
|
created_at timestamptz NOT NULL,
|
||||||
|
created_ip INET NULL,
|
||||||
|
"user" uuid NOT NULL,
|
||||||
|
track uuid NOT NULL,
|
||||||
|
source VARCHAR(100) NOT NULL DEFAULT '',
|
||||||
FOREIGN KEY (track) REFERENCES tracks(uuid),
|
FOREIGN KEY (track) REFERENCES tracks(uuid),
|
||||||
FOREIGN KEY (user) REFERENCES users(uuid)
|
FOREIGN KEY ("user") REFERENCES users(uuid)
|
||||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `album_artist` (
|
CREATE INDEX scrobblesUserLookup ON scrobbles ("user");
|
||||||
`album` BINARY(16) NOT NULL,
|
CREATE INDEX scrobblesDateLookup ON scrobbles (created_at);
|
||||||
`artist` BINARY(16) NOT NULL,
|
CREATE INDEX scrobblesSourceLookup ON scrobbles (source);
|
||||||
PRIMARY KEY (`album`, `artist`),
|
|
||||||
|
CREATE TABLE IF NOT EXISTS album_artist (
|
||||||
|
album uuid NOT NULL,
|
||||||
|
artist uuid NOT NULL,
|
||||||
|
PRIMARY KEY (album, artist),
|
||||||
FOREIGN KEY (album) REFERENCES albums(uuid),
|
FOREIGN KEY (album) REFERENCES albums(uuid),
|
||||||
FOREIGN KEY (artist) REFERENCES artists(uuid)
|
FOREIGN KEY (artist) REFERENCES artists(uuid)
|
||||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `track_album` (
|
CREATE TABLE IF NOT EXISTS track_album (
|
||||||
`track` BINARY(16) NOT NULL,
|
track uuid NOT NULL,
|
||||||
`album` BINARY(16) NOT NULL,
|
album uuid NOT NULL,
|
||||||
PRIMARY KEY (`track`, `album`),
|
PRIMARY KEY (track, album),
|
||||||
FOREIGN KEY (track) REFERENCES tracks(uuid),
|
FOREIGN KEY (track) REFERENCES tracks(uuid),
|
||||||
FOREIGN KEY (album) REFERENCES albums(uuid)
|
FOREIGN KEY (album) REFERENCES albums(uuid)
|
||||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `track_artist` (
|
CREATE TABLE IF NOT EXISTS track_artist (
|
||||||
`track` BINARY(16) NOT NULL,
|
track uuid NOT NULL,
|
||||||
`artist` BINARY(16) NOT NULL,
|
artist uuid NOT NULL,
|
||||||
PRIMARY KEY (`track`, `artist`),
|
PRIMARY KEY (track, artist),
|
||||||
FOREIGN KEY (track) REFERENCES tracks(uuid),
|
FOREIGN KEY (track) REFERENCES tracks(uuid),
|
||||||
FOREIGN KEY (artist) REFERENCES artists(uuid)
|
FOREIGN KEY (artist) REFERENCES artists(uuid)
|
||||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
|
);
|
||||||
|
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
START TRANSACTION;
|
START TRANSACTION;
|
||||||
|
|
||||||
ALTER TABLE albums DROP COLUMN `mbid`;
|
ALTER TABLE albums DROP COLUMN mbid;
|
||||||
ALTER TABLE artists DROP COLUMN `mbid`;
|
ALTER TABLE artists DROP COLUMN mbid;
|
||||||
ALTER TABLE tracks DROP COLUMN `mbid`;
|
ALTER TABLE tracks DROP COLUMN mbid;
|
||||||
|
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
START TRANSACTION;
|
START TRANSACTION;
|
||||||
|
|
||||||
ALTER TABLE albums ADD COLUMN `mbid` VARCHAR(36) DEFAULT '';
|
ALTER TABLE albums ADD COLUMN mbid VARCHAR(36) DEFAULT '';
|
||||||
ALTER TABLE artists ADD COLUMN `mbid` VARCHAR(36) DEFAULT '';
|
ALTER TABLE artists ADD COLUMN mbid VARCHAR(36) DEFAULT '';
|
||||||
ALTER TABLE tracks ADD COLUMN `mbid` VARCHAR(36) DEFAULT '';
|
ALTER TABLE tracks ADD COLUMN mbid VARCHAR(36) DEFAULT '';
|
||||||
|
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
@ -1 +1 @@
|
|||||||
ALTER TABLE `users` DROP COLUMN `token`;
|
ALTER TABLE users DROP COLUMN token;
|
@ -1 +1 @@
|
|||||||
ALTER TABLE `users` ADD COLUMN `token` VARCHAR(32) NOT NULL;
|
ALTER TABLE users ADD COLUMN token VARCHAR(32) NOT NULL;
|
@ -1 +1 @@
|
|||||||
TRUNCATE `config`;
|
TRUNCATE config;
|
@ -1,5 +1,5 @@
|
|||||||
INSERT INTO
|
INSERT INTO
|
||||||
`config`(`key`, `value`)
|
config(key, value)
|
||||||
VALUES
|
VALUES
|
||||||
('SPOTIFY_API_ID', ''),
|
('SPOTIFY_API_ID', ''),
|
||||||
('SPOTIFY_API_SECRET', ''),
|
('SPOTIFY_API_SECRET', ''),
|
||||||
|
@ -1 +1 @@
|
|||||||
DROP TABLE IF EXISTS `resettoken`;
|
DROP TABLE IF EXISTS resettoken;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
CREATE TABLE IF NOT EXISTS `resettoken` (
|
CREATE TABLE IF NOT EXISTS resettoken (
|
||||||
`user` BINARY(16) PRIMARY KEY,
|
"user" uuid PRIMARY KEY,
|
||||||
`token` VARCHAR(64) NOT NULL,
|
token VARCHAR(64) NOT NULL,
|
||||||
`expiry` DATETIME NOT NULL,
|
expiry timestamptz NOT NULL
|
||||||
KEY `tokenLookup` (`token`)
|
);
|
||||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
|
|
||||||
|
CREATE INDEX resettokenTokenLookup ON resettoken (token)
|
||||||
|
@ -1 +1 @@
|
|||||||
DROP TABLE IF EXISTS `oauth_tokens`;
|
DROP TABLE IF EXISTS oauth_tokens;
|
@ -1,11 +1,11 @@
|
|||||||
CREATE TABLE IF NOT EXISTS `oauth_tokens` (
|
CREATE TABLE IF NOT EXISTS oauth_tokens (
|
||||||
`user` BINARY(16),
|
"user" uuid,
|
||||||
`service` VARCHAR(64) NOT NULL,
|
service VARCHAR(64) NOT NULL,
|
||||||
`access_token` VARCHAR(255) NULL DEFAULT '',
|
access_token VARCHAR(255) NULL DEFAULT '',
|
||||||
`refresh_token` VARCHAR(255) NULL DEFAULT '',
|
refresh_token VARCHAR(255) NULL DEFAULT '',
|
||||||
`url` VARCHAR(255) NULL DEFAULT '',
|
url VARCHAR(255) NULL DEFAULT '',
|
||||||
`expiry` DATETIME NOT NULL,
|
expiry timestamptz NOT NULL,
|
||||||
`username` VARCHAR(100) NULL DEFAULT '',
|
username VARCHAR(100) NULL DEFAULT '',
|
||||||
`last_synced` DATETIME NOT NULL DEFAULT NOW(),
|
last_synced timestamptz NOT NULL DEFAULT NOW(),
|
||||||
PRIMARY KEY `userService` (`user`, `service`)
|
PRIMARY KEY ("user", "service")
|
||||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
|
);
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
START TRANSACTION;
|
START TRANSACTION;
|
||||||
|
|
||||||
ALTER TABLE `tracks` DROP COLUMN `spotify_id`;
|
ALTER TABLE tracks DROP COLUMN spotify_id;
|
||||||
ALTER TABLE `users` DROP COLUMN `spotify_id`;
|
ALTER TABLE albums DROP COLUMN spotify_id;
|
||||||
ALTER TABLE `albums` DROP COLUMN `spotify_id`;
|
ALTER TABLE artists DROP COLUMN spotify_id;
|
||||||
ALTER TABLE `artists` DROP COLUMN `spotify_id`;
|
|
||||||
|
|
||||||
COMMIT;
|
COMMIT;
|
@ -1,13 +1,11 @@
|
|||||||
START TRANSACTION;
|
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 `albums` ADD COLUMN `spotify_id` VARCHAR(255) NOT NULL DEFAULT '';
|
ALTER TABLE artists 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 '';
|
||||||
ALTER TABLE `tracks` ADD COLUMN `spotify_id` VARCHAR(255) NOT NULL DEFAULT '';
|
|
||||||
|
|
||||||
ALTER TABLE `users` ADD INDEX `spotifyLookup` (`spotify_id`);
|
CREATE INDEX albumsSpotifyLookup ON albums (spotify_id);
|
||||||
ALTER TABLE `albums` ADD INDEX `spotifyLookup` (`spotify_id`);
|
CREATE INDEX artistsSpotifyLookup ON artists (spotify_id);
|
||||||
ALTER TABLE `artists` ADD INDEX `spotifyLookup` (`spotify_id`);
|
CREATE INDEX tracksSpotifyLookup ON tracks (spotify_id);
|
||||||
ALTER TABLE `tracks` ADD INDEX `spotifyLookup` (`spotify_id`);
|
|
||||||
|
|
||||||
COMMIT;
|
COMMIT;
|
Loading…
Reference in New Issue
Block a user