This commit is contained in:
JMARyA 2024-10-04 19:39:51 +02:00
parent fdb45f953e
commit ca45a6df02
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
6 changed files with 58 additions and 35 deletions

View file

@ -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:

View file

@ -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)
);

View file

@ -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<chrono::Utc>,
pub time: chrono::DateTime<chrono::Utc>,
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<Self> {
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

View file

@ -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,11 +24,21 @@ pub struct Track {
impl Track {
pub async fn create(data: &serde_json::Map<String, serde_json::Value>) {
sqlx::query("INSERT INTO track (path, title, meta) VALUES ($1, $2, $4)")
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"))
.fetch_one(get_pg!())
.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();
}

View file

@ -31,7 +31,7 @@ pub enum UserRole {
impl User {
pub async fn find(username: &str) -> Option<Self> {
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<Self> {
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)

View file

@ -30,7 +30,7 @@ impl<'r> FromRequest<'r> for User {
async fn from_request(request: &'r Request<'_>) -> rocket::request::Outcome<Self, Self::Error> {
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, ()))