From ca45a6df02073cf185e62b53d7200f82e38739e3 Mon Sep 17 00:00:00 2001 From: JMARyA Date: Fri, 4 Oct 2024 19:39:51 +0200 Subject: [PATCH] complete --- docker-compose.yml | 6 +++++- migrations/0000_init.sql | 37 +++++++++++++++++++++---------------- src/library/event.rs | 6 +++--- src/library/track.rs | 30 ++++++++++++++++++++++-------- src/library/user.rs | 12 ++++++------ src/route/user.rs | 2 +- 6 files changed, 58 insertions(+), 35 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index dc8e996..c9273b6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,7 @@ services: synthwave: build: . ports: - - "8080:8080" + - "8080:8000" depends_on: - postgres volumes: @@ -11,10 +11,14 @@ services: - ./media:/media # Audio files environment: - "DATABASE_URL=postgres://user:pass@postgres/synthwave" + - "RUST_LOG=info" + - "ROCKET_ADDRESS=0.0.0.0" postgres: image: timescale/timescaledb:latest-pg16 restart: always + ports: + - 5432:5432 volumes: - ./db:/var/lib/postgresql/data/ environment: diff --git a/migrations/0000_init.sql b/migrations/0000_init.sql index 8af1755..62684d1 100644 --- a/migrations/0000_init.sql +++ b/migrations/0000_init.sql @@ -1,15 +1,16 @@ +CREATE TYPE user_role AS ENUM ('regular', 'admin'); -CREATE TABLE IF NOT EXISTS user ( - username varchar(255) NOT NULL PRIMARY KEY, - password text NOT NULL, - user_role text NOT NULL DEFAULT 'Regular' CHECK (user_role IN ('regular', 'admin')) +CREATE TABLE IF NOT EXISTS users ( + username VARCHAR(255) NOT NULL PRIMARY KEY, + "password" text NOT NULL, + user_role user_role NOT NULL DEFAULT 'regular' ); CREATE TABLE IF NOT EXISTS user_session ( id UUID NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(), token text NOT NULL, - user varchar(255) NOT NULL, - FOREIGN KEY(user) REFERENCES user(username) + "user" varchar(255) NOT NULL, + FOREIGN KEY("user") REFERENCES users(username) ); CREATE TABLE IF NOT EXISTS artist ( @@ -20,7 +21,7 @@ CREATE TABLE IF NOT EXISTS artist ( CREATE TABLE IF NOT EXISTS album ( id UUID NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(), title text NOT NULL, - artist UUID + artist UUID, FOREIGN KEY(artist) REFERENCES artist(id) ); @@ -28,7 +29,7 @@ CREATE TABLE IF NOT EXISTS track ( id UUID NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(), path text NOT NULL, title text, - date_added timestampz NOT NULL DEFAULT current_timestamp, + date_added timestamptz NOT NULL DEFAULT current_timestamp, album UUID, artist UUID, meta jsonb, @@ -36,23 +37,27 @@ CREATE TABLE IF NOT EXISTS track ( FOREIGN KEY(artist) REFERENCES artist(id) ); +CREATE TYPE event_kind AS ENUM ('play', 'played', 'stop'); + CREATE TABLE IF NOT EXISTS events ( - id UUID NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(), - time timestampz NOT NULL DEFAULT current_timestamp, - kind text CHECK (kind IN ('play', 'played', 'stop')), - user VARCHAR(255) NOT NULL, + id UUID NOT NULL DEFAULT gen_random_uuid(), + time timestamptz NOT NULL DEFAULT current_timestamp, + kind event_kind NOT NULL, + "user" VARCHAR(255) NOT NULL, track UUID NOT NULL, - FOREIGN KEY(user) REFERENCES user(username), + FOREIGN KEY("user") REFERENCES users(username), FOREIGN KEY(track) REFERENCES track(id) ); SELECT create_hypertable('events', by_range('time')); +CREATE TYPE visibility AS ENUM ('public', 'private'); + CREATE TABLE IF NOT EXISTS playlist ( id UUID NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(), owner VARCHAR(255) NOT NULL, title text NOT NULL, - visibility text NOT NULL DEFAULT 'private' CHECK (visibility IN ('public', 'private')), - tracks UUID[] NOT NULL DEFAULT [], - FOREIGN KEY(owner) REFERENCES user(username) + visibility visibility NOT NULL DEFAULT 'private', + tracks UUID[] NOT NULL DEFAULT '{}', + FOREIGN KEY(owner) REFERENCES users(username) ); diff --git a/src/library/event.rs b/src/library/event.rs index 3a92d2b..f5e0b77 100644 --- a/src/library/event.rs +++ b/src/library/event.rs @@ -6,7 +6,7 @@ use crate::{get_pg, library::user::User}; #[derive(Debug, Clone, Serialize, Deserialize, FromRow)] pub struct Event { pub id: uuid::Uuid, - pub timestamp: chrono::DateTime, + pub time: chrono::DateTime, pub kind: EventKind, pub user: String, pub track: uuid::Uuid, @@ -22,7 +22,7 @@ pub enum EventKind { impl Event { pub async fn create(kind: EventKind, user: &User, track: uuid::Uuid) -> Self { - sqlx::query_as("INSERT INTO events (kind, user, track) VALUES ($1, $2, $3) RETURNING *") + sqlx::query_as("INSERT INTO events (kind, \"user\", track) VALUES ($1, $2, $3) RETURNING *") .bind(kind) .bind(&user.username) .bind(track) @@ -32,7 +32,7 @@ impl Event { } pub async fn get_latest_events_of(u: &User) -> Vec { - sqlx::query_as("SELECT * FROM events WHERE user = $1 ORDER BY time DESC LIMIT 300") + sqlx::query_as("SELECT * FROM events WHERE \"user\" = $1 ORDER BY time DESC LIMIT 300") .bind(&u.username) .fetch_all(get_pg!()) .await diff --git a/src/library/track.rs b/src/library/track.rs index 587a02d..458c334 100644 --- a/src/library/track.rs +++ b/src/library/track.rs @@ -3,7 +3,11 @@ use serde_json::json; use sqlx::prelude::FromRow; use std::{collections::HashSet, str::FromStr}; -use crate::{get_pg, library::album::Album, route::ToAPI}; +use crate::{ + get_pg, + library::album::Album, + route::{to_uuid, ToAPI}, +}; use super::{event::Event, metadata::AudioMetadata, user::User}; @@ -20,13 +24,23 @@ pub struct Track { impl Track { pub async fn create(data: &serde_json::Map) { - sqlx::query("INSERT INTO track (path, title, meta) VALUES ($1, $2, $4)") - .bind(data.get("path").unwrap().as_str().unwrap().to_string()) - .bind(data.get("title").unwrap().as_str().unwrap().to_string()) - .bind(data.get("meta")) - .fetch_one(get_pg!()) - .await - .unwrap(); + sqlx::query( + "INSERT INTO track (path, title, meta, album, artist) VALUES ($1, $2, $3, $4, $5)", + ) + .bind(data.get("path").unwrap().as_str().unwrap().to_string()) + .bind(data.get("title").unwrap().as_str().unwrap().to_string()) + .bind(data.get("meta")) + .bind( + data.get("album") + .map(|x| to_uuid(x.as_str().unwrap()).unwrap()), + ) + .bind( + data.get("artist") + .map(|x| to_uuid(x.as_str().unwrap()).unwrap()), + ) + .execute(get_pg!()) + .await + .unwrap(); } pub async fn of_path(path: &str) -> Option { diff --git a/src/library/user.rs b/src/library/user.rs index 1d05be4..87b43b8 100644 --- a/src/library/user.rs +++ b/src/library/user.rs @@ -31,7 +31,7 @@ pub enum UserRole { impl User { pub async fn find(username: &str) -> Option { - sqlx::query_as("SELECT * FROM user WHERE username = $1") + sqlx::query_as("SELECT * FROM users WHERE username = $1") .bind(username) .fetch_optional(get_pg!()) .await @@ -49,11 +49,11 @@ impl User { user_role: role, }; - sqlx::query("INSERT INTO user (username, password, user_role) VALUES ($1, $2, $3)") + sqlx::query("INSERT INTO users (username, \"password\", user_role) VALUES ($1, $2, $3)") .bind(&u.username) .bind(&u.password) .bind(&u.user_role) - .fetch_one(get_pg!()) + .execute(get_pg!()) .await .unwrap(); @@ -73,7 +73,7 @@ impl User { /// Change the password of a `User` pub async fn passwd(self, old: &str, new: &str) -> Result<(), ()> { if self.verify_pw(old) { - sqlx::query("UPDATE user SET password = $1 WHERE username = $2;") + sqlx::query("UPDATE users SET \"password\" = $1 WHERE username = $2;") .bind(bcrypt::hash(new, bcrypt::DEFAULT_COST).unwrap()) .bind(&self.username) .fetch_one(get_pg!()) @@ -87,7 +87,7 @@ impl User { } pub async fn find_all() -> Vec { - sqlx::query_as("SELECT * FROM user") + sqlx::query_as("SELECT * FROM users") .fetch_all(get_pg!()) .await .unwrap() @@ -95,7 +95,7 @@ impl User { pub async fn session(&self) -> Session { sqlx::query_as( - "INSERT INTO user_session (token, user) VALUES ($1, $2) RETURNING id, token, user", + "INSERT INTO user_session (token, \"user\") VALUES ($1, $2) RETURNING id, token, \"user\"", ) .bind(gen_token(64)) .bind(&self.username) diff --git a/src/route/user.rs b/src/route/user.rs index ccceaca..6725501 100644 --- a/src/route/user.rs +++ b/src/route/user.rs @@ -30,7 +30,7 @@ impl<'r> FromRequest<'r> for User { async fn from_request(request: &'r Request<'_>) -> rocket::request::Outcome { match request.headers().get_one("token") { Some(key) => { - if let Some(user) = sqlx::query_as("SELECT * FROM user WHERE id = (SELECT user FROM user_session WHERE token = $1)").bind(key).fetch_optional(get_pg!()).await.unwrap() { + if let Some(user) = sqlx::query_as("SELECT * FROM users WHERE username = (SELECT \"user\" FROM user_session WHERE token = $1)").bind(key).fetch_optional(get_pg!()).await.unwrap() { Outcome::Success(user) } else { Outcome::Error((Status::Unauthorized, ()))