complete
This commit is contained in:
parent
fdb45f953e
commit
ca45a6df02
6 changed files with 58 additions and 35 deletions
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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, ()))
|
||||
|
|
Loading…
Reference in a new issue