updates
This commit is contained in:
parent
292ff60720
commit
fa8ed2b599
19 changed files with 349 additions and 216 deletions
59
Cargo.lock
generated
59
Cargo.lock
generated
|
@ -65,7 +65,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
|
checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.46",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -179,7 +179,7 @@ dependencies = [
|
||||||
"actix-router",
|
"actix-router",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.46",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -257,7 +257,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.46",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -455,9 +455,9 @@ checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.11"
|
version = "0.2.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
|
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
@ -527,7 +527,7 @@ dependencies = [
|
||||||
"diesel_table_macro_syntax",
|
"diesel_table_macro_syntax",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.46",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -547,7 +547,7 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5"
|
checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"syn 2.0.46",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -967,9 +967,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.151"
|
version = "0.2.152"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
|
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
|
@ -1160,7 +1160,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.46",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1267,9 +1267,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.74"
|
version = "1.0.76"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db"
|
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
@ -1497,35 +1497,35 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "1.0.20"
|
version = "1.0.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
|
checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.194"
|
version = "1.0.195"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773"
|
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.194"
|
version = "1.0.195"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0"
|
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.46",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.110"
|
version = "1.0.111"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6fbd975230bada99c8bb618e0c365c2eefa219158d5c6c29610fd09ff1833257"
|
checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
|
@ -1646,9 +1646,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.46"
|
version = "2.0.48"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "89456b690ff72fddcecf231caedbe615c59480c93358a93dfae7fc29e3ebbf0e"
|
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -1715,7 +1715,7 @@ checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.46",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1931,6 +1931,7 @@ name = "vybr-api"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-rt",
|
"actix-rt",
|
||||||
|
"actix-service",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bcrypt",
|
"bcrypt",
|
||||||
|
@ -1985,7 +1986,7 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.46",
|
"syn 2.0.48",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2019,7 +2020,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.46",
|
"syn 2.0.48",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
@ -2214,9 +2215,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.5.31"
|
version = "0.5.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "97a4882e6b134d6c28953a387571f1acdd3496830d5e36c5e3a1075580ea641c"
|
checksum = "b7520bbdec7211caa7c4e682eb1fbe07abe20cee6756b6e00f537c82c11816aa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
@ -2248,7 +2249,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.46",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -23,3 +23,4 @@ lazy_static = "1"
|
||||||
jsonwebtoken = "9"
|
jsonwebtoken = "9"
|
||||||
serde = "1"
|
serde = "1"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
actix-service = "2.0.2"
|
||||||
|
|
|
@ -45,7 +45,7 @@ CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||||
-- allowing for customization to meet specific needs.
|
-- allowing for customization to meet specific needs.
|
||||||
DROP FUNCTION IF EXISTS nanoid(int, text, float);
|
DROP FUNCTION IF EXISTS nanoid(int, text, float);
|
||||||
CREATE OR REPLACE FUNCTION nanoid(
|
CREATE OR REPLACE FUNCTION nanoid(
|
||||||
size int DEFAULT 21, -- The number of symbols in the NanoId String. Must be greater than 0.
|
size int DEFAULT 24, -- The number of symbols in the NanoId String. Must be greater than 0.
|
||||||
alphabet text DEFAULT '_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', -- The symbols used in the NanoId String. Must contain between 1 and 255 symbols.
|
alphabet text DEFAULT '_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', -- The symbols used in the NanoId String. Must contain between 1 and 255 symbols.
|
||||||
additionalBytesFactor float DEFAULT 1.6 -- The additional bytes factor used for calculating the step size. Must be equal or greater then 1.
|
additionalBytesFactor float DEFAULT 1.6 -- The additional bytes factor used for calculating the step size. Must be equal or greater then 1.
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
|
create type playlist_type as enum ('playlist', 'folder');
|
||||||
|
|
||||||
create table if not exists playlists
|
create table if not exists playlists
|
||||||
(
|
(
|
||||||
id varchar(24) default nanoid(24),
|
id varchar(24) default nanoid(24),
|
||||||
name varchar(255) not null,
|
name varchar(255) not null,
|
||||||
public bool default false not null,
|
public bool default false not null,
|
||||||
|
type playlist_type default 'playlist' not null,
|
||||||
|
|
||||||
creator_id varchar(24) not null,
|
creator_id varchar(24) not null,
|
||||||
|
parent_id varchar(24) references playlists (id) on delete set null,
|
||||||
|
|
||||||
created_at timestamp default now(),
|
created_at timestamp default now(),
|
||||||
updated_at timestamp,
|
updated_at timestamp,
|
||||||
|
|
|
@ -2,5 +2,6 @@ create table if not exists playlists_tracks
|
||||||
(
|
(
|
||||||
playlist_id varchar(24) references playlists (id) on delete cascade,
|
playlist_id varchar(24) references playlists (id) on delete cascade,
|
||||||
track_id varchar(24) references tracks (id) on delete cascade,
|
track_id varchar(24) references tracks (id) on delete cascade,
|
||||||
|
added_at timestamp default now() not null,
|
||||||
primary key (playlist_id, track_id)
|
primary key (playlist_id, track_id)
|
||||||
);
|
);
|
||||||
|
|
|
@ -12,10 +12,10 @@ use crate::helpers::db;
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
use actix_web::{web, App, HttpServer};
|
use actix_web::{App, HttpServer};
|
||||||
|
|
||||||
dotenvy::dotenv().expect("No .env file found");
|
dotenvy::dotenv().expect("No .env file found");
|
||||||
std::env::set_var("RUST_LOG", "debug");
|
env_logger::init();
|
||||||
|
|
||||||
// Register services.
|
// Register services.
|
||||||
let _ = services::spotify::instance().await;
|
let _ = services::spotify::instance().await;
|
||||||
|
@ -24,9 +24,11 @@ async fn main() -> std::io::Result<()> {
|
||||||
|
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
App::new()
|
App::new()
|
||||||
.service(web::scope("/playlists").service(routes::playlists::get_playlist))
|
|
||||||
.service(routes::auth::routes())
|
.service(routes::auth::routes())
|
||||||
|
// .wrap(middlewares::auth::auth())
|
||||||
|
.service(routes::playlists::routes())
|
||||||
.service(routes::me::routes())
|
.service(routes::me::routes())
|
||||||
|
.service(routes::import::routes())
|
||||||
.service(routes::users::routes())
|
.service(routes::users::routes())
|
||||||
.service(routes::search::routes())
|
.service(routes::search::routes())
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::helpers::jwt::get_token;
|
use crate::helpers::jwt::get_token;
|
||||||
use crate::middlewares::error::ErrorResponse;
|
use crate::middlewares::error::ErrorResponse;
|
||||||
use crate::models::user::Users;
|
use crate::models::users::Users;
|
||||||
use actix_web::http::{header, StatusCode};
|
use actix_web::http::{header, StatusCode};
|
||||||
use actix_web::HttpRequest;
|
use actix_web::HttpRequest;
|
||||||
|
|
||||||
|
@ -36,3 +36,12 @@ pub fn get_user(req: HttpRequest) -> Result<Users, ErrorResponse> {
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_user_option(req: HttpRequest) -> Option<Users> {
|
||||||
|
let user = get_user(req);
|
||||||
|
|
||||||
|
match user {
|
||||||
|
Ok(user) => Some(user),
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
use crate::helpers::db;
|
use crate::helpers::db;
|
||||||
use crate::schema::{artists, artists_tracks};
|
use crate::schema::{artists, artists_tracks};
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
|
use diesel::prelude::*;
|
||||||
use diesel::result::Error;
|
use diesel::result::Error;
|
||||||
use diesel::{
|
|
||||||
AsChangeset, ExpressionMethods, Insertable, QueryDsl, Queryable, RunQueryDsl, Selectable,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(AsChangeset, Insertable, Queryable, Selectable, Deserialize, Serialize)]
|
#[derive(AsChangeset, Identifiable, Insertable, Queryable, Selectable, Deserialize, Serialize)]
|
||||||
#[diesel(table_name = crate::schema::artists)]
|
#[diesel(table_name = artists)]
|
||||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
pub struct Artists {
|
||||||
pub struct Artist {
|
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub slug: String,
|
pub slug: String,
|
||||||
|
@ -22,14 +19,11 @@ pub struct Artist {
|
||||||
pub tidal_id: Option<String>,
|
pub tidal_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Queryable, Serialize)]
|
#[derive(Debug, Insertable, Deserialize, Serialize)]
|
||||||
pub struct Artists {
|
#[diesel(table_name = artists)]
|
||||||
|
pub struct NewArtist {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub title: String,
|
pub name: String,
|
||||||
pub slug: String,
|
|
||||||
|
|
||||||
pub created_at: Option<NaiveDateTime>,
|
|
||||||
pub updated_at: Option<NaiveDateTime>,
|
|
||||||
|
|
||||||
pub spotify_id: Option<String>,
|
pub spotify_id: Option<String>,
|
||||||
pub tidal_id: Option<String>,
|
pub tidal_id: Option<String>,
|
||||||
|
@ -38,14 +32,14 @@ pub struct Artists {
|
||||||
impl Artists {
|
impl Artists {
|
||||||
pub fn find(id: String) -> Result<Self, Error> {
|
pub fn find(id: String) -> Result<Self, Error> {
|
||||||
let conn = &mut db::connection()?;
|
let conn = &mut db::connection()?;
|
||||||
let playlist = artists::table.filter(artists::id.eq(id)).first(conn)?;
|
let artist = artists::table.filter(artists::id.eq(id)).first(conn)?;
|
||||||
Ok(playlist)
|
Ok(artist)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(artist: Artist) -> Result<Self, Error> {
|
pub fn create(artist: NewArtist) -> Result<Self, Error> {
|
||||||
let conn = &mut db::connection()?;
|
let conn = &mut db::connection()?;
|
||||||
let playlist = diesel::insert_into(artists::table)
|
let playlist = diesel::insert_into(artists::table)
|
||||||
.values(Artist::from(artist))
|
.values(artist)
|
||||||
.get_result(conn)?;
|
.get_result(conn)?;
|
||||||
Ok(playlist)
|
Ok(playlist)
|
||||||
}
|
}
|
||||||
|
@ -64,19 +58,3 @@ impl Artists {
|
||||||
Ok(artists)
|
Ok(artists)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Artist {
|
|
||||||
fn from(artist: Artist) -> Artist {
|
|
||||||
Artist {
|
|
||||||
id: artist.id,
|
|
||||||
name: artist.name,
|
|
||||||
slug: artist.slug,
|
|
||||||
|
|
||||||
created_at: artist.created_at,
|
|
||||||
updated_at: artist.updated_at,
|
|
||||||
|
|
||||||
spotify_id: artist.spotify_id,
|
|
||||||
tidal_id: artist.tidal_id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
52
src/models/artists_tracks.rs
Normal file
52
src/models/artists_tracks.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
use diesel::prelude::*;
|
||||||
|
use diesel::result::Error;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::helpers::db;
|
||||||
|
|
||||||
|
use super::artists::Artists;
|
||||||
|
use super::tracks::Tracks;
|
||||||
|
|
||||||
|
use crate::schema::{artists, artists_tracks, tracks};
|
||||||
|
|
||||||
|
#[derive(Identifiable, Selectable, Queryable, Associations, Debug)]
|
||||||
|
#[diesel(belongs_to(Artists, foreign_key = artist_id))]
|
||||||
|
#[diesel(belongs_to(Tracks, foreign_key = track_id))]
|
||||||
|
#[diesel(table_name = artists_tracks)]
|
||||||
|
#[diesel(primary_key(artist_id, track_id))]
|
||||||
|
pub struct ArtistTracks {
|
||||||
|
pub artist_id: String,
|
||||||
|
pub track_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArtistTracks {
|
||||||
|
pub fn get_tracks(artist: &Artists) -> Result<Vec<Tracks>, Error> {
|
||||||
|
let conn = &mut db::connection()?;
|
||||||
|
let track_ids = ArtistTracks::belonging_to(artist).select(artists_tracks::track_id);
|
||||||
|
|
||||||
|
let tracks: Vec<Tracks> = tracks::table
|
||||||
|
.filter(tracks::id.eq_any(track_ids))
|
||||||
|
.load::<Tracks>(conn)?;
|
||||||
|
|
||||||
|
Ok(tracks)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_artists(track: &Tracks) -> Result<Vec<TrackArtist>, Error> {
|
||||||
|
let conn = &mut db::connection()?;
|
||||||
|
let artist_ids = ArtistTracks::belonging_to(track).select(artists_tracks::artist_id);
|
||||||
|
|
||||||
|
let artists = artists::table
|
||||||
|
.select((artists::id, artists::name))
|
||||||
|
.filter(artists::id.eq_any(artist_ids))
|
||||||
|
.load::<TrackArtist>(conn)?;
|
||||||
|
|
||||||
|
Ok(artists)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Queryable)]
|
||||||
|
#[diesel(table_name = artists)]
|
||||||
|
pub struct TrackArtist {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
pub mod artists;
|
pub mod artists;
|
||||||
|
pub mod artists_tracks;
|
||||||
pub mod playlists;
|
pub mod playlists;
|
||||||
|
pub mod playlists_tracks;
|
||||||
pub mod spotify;
|
pub mod spotify;
|
||||||
pub mod tracks;
|
pub mod tracks;
|
||||||
pub mod user;
|
pub mod users;
|
||||||
|
|
|
@ -1,43 +1,43 @@
|
||||||
use crate::helpers::db;
|
use crate::helpers::db;
|
||||||
use crate::models::tracks::{Tracks, TracksWithArtists};
|
use crate::schema::{playlists, playlists_tracks};
|
||||||
use crate::models::user::Users;
|
|
||||||
use crate::schema::playlists;
|
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use diesel::result::Error;
|
use diesel::{prelude::*, result::Error};
|
||||||
use diesel::{
|
|
||||||
AsChangeset, ExpressionMethods, Insertable, QueryDsl, Queryable, RunQueryDsl, Selectable,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
use super::artists_tracks::{ArtistTracks, TrackArtist};
|
||||||
|
use super::{playlists_tracks::PlaylistTracks, tracks::Tracks, users::Users};
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct PlaylistCreator {
|
pub struct PlaylistCreator {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(AsChangeset, Insertable, Queryable, Selectable, Deserialize, Serialize)]
|
#[derive(
|
||||||
#[diesel(table_name = crate::schema::playlists)]
|
AsChangeset, Debug, Deserialize, Identifiable, Queryable, Selectable, Serialize, PartialEq,
|
||||||
#[diesel(belongs_to(Users))]
|
)]
|
||||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
#[diesel(table_name = playlists)]
|
||||||
pub struct Playlist {
|
pub struct Playlists {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub public: bool,
|
pub public: bool,
|
||||||
|
|
||||||
|
pub playlist_type: String,
|
||||||
|
pub parent_id: Option<String>,
|
||||||
|
|
||||||
pub creator_id: String,
|
pub creator_id: String,
|
||||||
|
|
||||||
pub created_at: Option<NaiveDateTime>,
|
pub created_at: Option<NaiveDateTime>,
|
||||||
pub updated_at: Option<NaiveDateTime>,
|
pub updated_at: Option<NaiveDateTime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Queryable, Serialize)]
|
#[derive(Debug, Deserialize, Insertable)]
|
||||||
pub struct Playlists {
|
#[diesel(table_name = playlists)]
|
||||||
pub id: String,
|
pub struct NewPlaylist {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub public: bool,
|
pub public: bool,
|
||||||
|
// pub playlist_type: String,
|
||||||
pub creator_id: String,
|
pub creator_id: String,
|
||||||
|
|
||||||
pub created_at: Option<NaiveDateTime>,
|
|
||||||
pub updated_at: Option<NaiveDateTime>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Playlists {
|
impl Playlists {
|
||||||
|
@ -59,23 +59,26 @@ impl Playlists {
|
||||||
let conn = &mut db::connection()?;
|
let conn = &mut db::connection()?;
|
||||||
|
|
||||||
let mut playlists = playlists::table
|
let mut playlists = playlists::table
|
||||||
|
.distinct()
|
||||||
|
.select(Playlists::as_select())
|
||||||
|
.order((playlists::playlist_type.desc(), playlists::parent_id.desc()))
|
||||||
.filter(playlists::creator_id.eq(user_id))
|
.filter(playlists::creator_id.eq(user_id))
|
||||||
.into_boxed();
|
.into_boxed();
|
||||||
|
|
||||||
if filter_public {
|
if filter_public {
|
||||||
playlists = playlists.filter(playlists::public.eq(true));
|
playlists = playlists.filter(
|
||||||
|
playlists::public
|
||||||
|
.eq(true)
|
||||||
|
.and(playlists::playlist_type.ne("folder")),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let playlists = playlists.get_results(conn)?;
|
let playlists = playlists.get_results(conn)?;
|
||||||
Ok(playlists)
|
Ok(playlists)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_tracks(&self) -> Result<Vec<TracksWithArtists>, Error> {
|
pub fn get_tracks(&self) -> Result<Vec<PlaylistTrack>, Error> {
|
||||||
let tracks = Tracks::find_by_playlist(&self.id)?;
|
let tracks: Vec<PlaylistTrack> = PlaylistTracks::get_tracks(self)?;
|
||||||
let tracks: Vec<TracksWithArtists> = tracks
|
|
||||||
.into_iter()
|
|
||||||
.map(Tracks::with_artists)
|
|
||||||
.collect::<Result<Vec<TracksWithArtists>, _>>()?;
|
|
||||||
|
|
||||||
Ok(tracks)
|
Ok(tracks)
|
||||||
}
|
}
|
||||||
|
@ -84,34 +87,69 @@ impl Playlists {
|
||||||
let creator = Users::find(&self.creator_id)?;
|
let creator = Users::find(&self.creator_id)?;
|
||||||
Ok(creator)
|
Ok(creator)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Insertable, Deserialize)]
|
pub fn get_data(&self, tracks: &[PlaylistTrack]) -> (usize, usize) {
|
||||||
#[diesel(table_name = playlists)]
|
if tracks.is_empty() {
|
||||||
pub struct NewPlaylist {
|
return (0, 0);
|
||||||
pub name: String,
|
|
||||||
pub public: bool,
|
|
||||||
pub creator_id: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Playlist {
|
|
||||||
pub fn create(name: &str, public: bool, creator_id: &str) -> NewPlaylist {
|
|
||||||
NewPlaylist {
|
|
||||||
name: name.to_string(),
|
|
||||||
public,
|
|
||||||
creator_id: Some(creator_id.to_string()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from(playlist: Playlist) -> Playlist {
|
let duration = tracks
|
||||||
Playlist {
|
.iter()
|
||||||
id: playlist.id,
|
.map(|track| track.duration_ms)
|
||||||
name: playlist.name,
|
.reduce(|a, b| a + b)
|
||||||
public: playlist.public,
|
.unwrap() as usize;
|
||||||
creator_id: playlist.creator_id,
|
|
||||||
|
|
||||||
created_at: playlist.created_at,
|
(duration, tracks.len())
|
||||||
updated_at: playlist.updated_at,
|
}
|
||||||
|
|
||||||
|
pub fn can_see(&self, creator: Option<Users>) -> bool {
|
||||||
|
self.public || creator.map_or(false, |user| user.id == self.creator_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Folder
|
||||||
|
// struct Folder {
|
||||||
|
// id: String,
|
||||||
|
// name: String,
|
||||||
|
// playlists: Vec<Playlists>,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl Folder {
|
||||||
|
// pub fn get_playlists(&self) -> Result<Vec<Playlists>, Error> {
|
||||||
|
// let playlists = playlists::table
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct PlaylistTrack {
|
||||||
|
pub id: String,
|
||||||
|
pub title: String,
|
||||||
|
pub duration_ms: i32,
|
||||||
|
|
||||||
|
pub artists: Vec<TrackArtist>,
|
||||||
|
pub added_at: NaiveDateTime,
|
||||||
|
pub spotify_id: Option<String>,
|
||||||
|
pub tidal_id: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Tracks> for PlaylistTrack {
|
||||||
|
fn from(track: Tracks) -> Self {
|
||||||
|
let artists = ArtistTracks::get_artists(&track).unwrap();
|
||||||
|
|
||||||
|
let added_at = playlists_tracks::table
|
||||||
|
.filter(playlists_tracks::track_id.eq(&track.id))
|
||||||
|
.select(playlists_tracks::added_at)
|
||||||
|
.first::<NaiveDateTime>(&mut db::connection().unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
PlaylistTrack {
|
||||||
|
id: track.id,
|
||||||
|
title: track.title,
|
||||||
|
duration_ms: track.duration_ms,
|
||||||
|
artists,
|
||||||
|
added_at,
|
||||||
|
spotify_id: track.spotify_id,
|
||||||
|
tidal_id: track.tidal_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
38
src/models/playlists_tracks.rs
Normal file
38
src/models/playlists_tracks.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use diesel::prelude::*;
|
||||||
|
use diesel::result::Error;
|
||||||
|
|
||||||
|
use crate::helpers::db;
|
||||||
|
|
||||||
|
use super::playlists::{PlaylistTrack, Playlists};
|
||||||
|
use super::tracks::Tracks;
|
||||||
|
|
||||||
|
use crate::schema::{playlists_tracks, tracks};
|
||||||
|
|
||||||
|
#[derive(Identifiable, Selectable, Queryable, Associations, Debug)]
|
||||||
|
#[diesel(belongs_to(Playlists, foreign_key = playlist_id))]
|
||||||
|
#[diesel(belongs_to(Tracks, foreign_key = track_id))]
|
||||||
|
#[diesel(table_name = playlists_tracks)]
|
||||||
|
#[diesel(primary_key(playlist_id, track_id))]
|
||||||
|
pub struct PlaylistTracks {
|
||||||
|
pub playlist_id: String,
|
||||||
|
pub track_id: String,
|
||||||
|
pub added_at: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlaylistTracks {
|
||||||
|
pub fn get_tracks(playlist: &Playlists) -> Result<Vec<PlaylistTrack>, Error> {
|
||||||
|
let conn = &mut db::connection()?;
|
||||||
|
let track_ids = PlaylistTracks::belonging_to(playlist).select(playlists_tracks::track_id);
|
||||||
|
|
||||||
|
let tracks: Vec<Tracks> = tracks::table
|
||||||
|
.filter(tracks::id.eq_any(track_ids))
|
||||||
|
.load::<Tracks>(conn)?;
|
||||||
|
|
||||||
|
let tracks = tracks
|
||||||
|
.into_iter()
|
||||||
|
.map(PlaylistTrack::from)
|
||||||
|
.collect::<Vec<PlaylistTrack>>();
|
||||||
|
|
||||||
|
Ok(tracks)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +1,15 @@
|
||||||
use crate::helpers::db;
|
use crate::helpers::db;
|
||||||
use crate::models::{artists::Artists, spotify};
|
use crate::models::{artists::Artists, spotify};
|
||||||
use crate::schema::tracks;
|
use crate::schema::{playlists_tracks, tracks};
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
|
use diesel::prelude::*;
|
||||||
use diesel::result::Error;
|
use diesel::result::Error;
|
||||||
use diesel::{
|
|
||||||
AsChangeset, ExpressionMethods, Insertable, QueryDsl, Queryable, RunQueryDsl, Selectable,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::schema::playlists_tracks;
|
#[derive(
|
||||||
|
AsChangeset, Debug, Deserialize, Serialize, Queryable, Identifiable, Selectable, Clone,
|
||||||
#[derive(AsChangeset, Clone, Insertable, Queryable, Selectable, Deserialize, Serialize)]
|
)]
|
||||||
#[diesel(table_name = crate::schema::tracks)]
|
#[diesel(table_name = tracks)]
|
||||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
|
||||||
pub struct Tracks {
|
pub struct Tracks {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
@ -25,6 +22,17 @@ pub struct Tracks {
|
||||||
pub tidal_id: Option<String>,
|
pub tidal_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(AsChangeset, Clone, Debug, Deserialize, Insertable)]
|
||||||
|
#[diesel(table_name = tracks)]
|
||||||
|
pub struct NewTrack {
|
||||||
|
pub id: String,
|
||||||
|
pub title: String,
|
||||||
|
pub duration_ms: i32,
|
||||||
|
|
||||||
|
pub spotify_id: Option<String>,
|
||||||
|
pub tidal_id: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Tracks {
|
impl Tracks {
|
||||||
pub fn find(id: String) -> Result<Self, Error> {
|
pub fn find(id: String) -> Result<Self, Error> {
|
||||||
let conn = &mut db::connection()?;
|
let conn = &mut db::connection()?;
|
||||||
|
@ -32,29 +40,15 @@ impl Tracks {
|
||||||
Ok(playlist)
|
Ok(playlist)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(track: Tracks) -> Result<Self, Error> {
|
pub fn create(track: NewTrack) -> Result<Self, Error> {
|
||||||
let conn = &mut db::connection()?;
|
let conn = &mut db::connection()?;
|
||||||
let playlist = diesel::insert_into(tracks::table)
|
let playlist = diesel::insert_into(tracks::table)
|
||||||
.values(Tracks::from(track))
|
.values(track)
|
||||||
.get_result(conn)?;
|
.get_result(conn)?;
|
||||||
Ok(playlist)
|
Ok(playlist)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_playlist(playlist_id: &str) -> Result<Vec<Tracks>, Error> {
|
pub fn create_or_update(track: NewTrack) -> Result<Self, Error> {
|
||||||
let conn = &mut db::connection()?;
|
|
||||||
let tracks: Vec<(String, String)> = playlists_tracks::table
|
|
||||||
.filter(playlists_tracks::playlist_id.eq(playlist_id))
|
|
||||||
.get_results::<(String, String)>(conn)?;
|
|
||||||
|
|
||||||
let tracks = tracks
|
|
||||||
.into_iter()
|
|
||||||
.map(|(_, track_id)| Tracks::find(track_id).unwrap())
|
|
||||||
.collect::<Vec<Tracks>>();
|
|
||||||
|
|
||||||
Ok(tracks)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_or_update(track: Tracks) -> Result<Self, Error> {
|
|
||||||
let conn = &mut db::connection()?;
|
let conn = &mut db::connection()?;
|
||||||
let playlist = diesel::insert_into(tracks::table)
|
let playlist = diesel::insert_into(tracks::table)
|
||||||
.values(track.clone())
|
.values(track.clone())
|
||||||
|
@ -70,20 +64,6 @@ impl Tracks {
|
||||||
Ok(artists)
|
Ok(artists)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from(track: Tracks) -> Self {
|
|
||||||
Tracks {
|
|
||||||
id: track.id,
|
|
||||||
title: track.title,
|
|
||||||
duration_ms: track.duration_ms,
|
|
||||||
|
|
||||||
created_at: track.created_at,
|
|
||||||
updated_at: track.updated_at,
|
|
||||||
|
|
||||||
spotify_id: track.spotify_id,
|
|
||||||
tidal_id: track.tidal_id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_artists(track: Tracks) -> Result<TracksWithArtists, Error> {
|
pub fn with_artists(track: Tracks) -> Result<TracksWithArtists, Error> {
|
||||||
let artists = track.get_artists()?;
|
let artists = track.get_artists()?;
|
||||||
|
|
||||||
|
|
|
@ -11,18 +11,6 @@ use serde::{Deserialize, Serialize};
|
||||||
#[derive(AsChangeset, Insertable, Queryable, Selectable, Deserialize, Serialize)]
|
#[derive(AsChangeset, Insertable, Queryable, Selectable, Deserialize, Serialize)]
|
||||||
#[diesel(table_name = crate::schema::users)]
|
#[diesel(table_name = crate::schema::users)]
|
||||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||||
pub struct User {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub email: String,
|
|
||||||
|
|
||||||
pub password: String,
|
|
||||||
|
|
||||||
pub created_at: Option<NaiveDateTime>,
|
|
||||||
pub updated_at: Option<NaiveDateTime>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Queryable, Serialize)]
|
|
||||||
pub struct Users {
|
pub struct Users {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -41,14 +29,6 @@ impl Users {
|
||||||
Ok(user)
|
Ok(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(user: User) -> Result<Self, Error> {
|
|
||||||
let conn = &mut db::connection()?;
|
|
||||||
let user = diesel::insert_into(users::table)
|
|
||||||
.values(User::from(user))
|
|
||||||
.get_result(conn)?;
|
|
||||||
Ok(user)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn find_by_email(email: &str) -> Result<Self, Error> {
|
pub fn find_by_email(email: &str) -> Result<Self, Error> {
|
||||||
let conn = &mut db::connection()?;
|
let conn = &mut db::connection()?;
|
||||||
let user = users::table.filter(users::email.eq(email)).first(conn)?;
|
let user = users::table.filter(users::email.eq(email)).first(conn)?;
|
||||||
|
@ -59,16 +39,3 @@ impl Users {
|
||||||
bcrypt::verify(password, hash)
|
bcrypt::verify(password, hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl User {
|
|
||||||
fn from(user: User) -> User {
|
|
||||||
User {
|
|
||||||
id: user.id,
|
|
||||||
name: user.name,
|
|
||||||
email: user.email,
|
|
||||||
password: user.password,
|
|
||||||
created_at: user.created_at,
|
|
||||||
updated_at: user.updated_at,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::helpers::jwt::get_encoding_key;
|
use crate::helpers::jwt::get_encoding_key;
|
||||||
use crate::middlewares::error::ErrorResponse;
|
use crate::middlewares::error::ErrorResponse;
|
||||||
use crate::models::user::Users;
|
use crate::models::users::Users;
|
||||||
use actix_web::http::StatusCode;
|
use actix_web::http::StatusCode;
|
||||||
use actix_web::{post, web, HttpResponse, Scope};
|
use actix_web::{post, web, HttpResponse, Scope};
|
||||||
use chrono::{Days, Utc};
|
use chrono::{Days, Utc};
|
||||||
|
@ -17,7 +17,7 @@ pub struct JWTClaims {
|
||||||
pub exp: i64,
|
pub exp: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/auth/login")]
|
#[post("/login")]
|
||||||
async fn login(body: web::Json<LoginBody>) -> Result<HttpResponse, ErrorResponse> {
|
async fn login(body: web::Json<LoginBody>) -> Result<HttpResponse, ErrorResponse> {
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
struct Response {
|
struct Response {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::middlewares::error::ErrorResponse;
|
use crate::middlewares::error::ErrorResponse;
|
||||||
use crate::middlewares::user::get_user;
|
use crate::middlewares::user::get_user;
|
||||||
use crate::models::playlists::{NewPlaylist, Playlists};
|
use crate::models::playlists::{NewPlaylist, PlaylistCreator, Playlists};
|
||||||
|
use crate::routes::playlists::GetPlaylistResponse;
|
||||||
use actix_web::{get, post, web, HttpRequest, HttpResponse, Scope};
|
use actix_web::{get, post, web, HttpRequest, HttpResponse, Scope};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
@ -33,14 +34,32 @@ async fn me(req: HttpRequest) -> Result<HttpResponse, ErrorResponse> {
|
||||||
async fn me_playlists(req: HttpRequest) -> Result<HttpResponse, ErrorResponse> {
|
async fn me_playlists(req: HttpRequest) -> Result<HttpResponse, ErrorResponse> {
|
||||||
let user = get_user(req)?;
|
let user = get_user(req)?;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
let playlists = Playlists::find_for_user(&user.id, false)?;
|
||||||
struct Response {
|
let playlists = playlists
|
||||||
playlists: Vec<Playlists>,
|
.into_iter()
|
||||||
}
|
.map(|playlist| {
|
||||||
|
let creator = playlist.get_creator().unwrap();
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(Response {
|
let tracks = playlist.get_tracks().unwrap();
|
||||||
playlists: Playlists::find_for_user(&user.id, false)?,
|
let (duration, tracks_count) = playlist.get_data(&tracks);
|
||||||
}))
|
|
||||||
|
GetPlaylistResponse {
|
||||||
|
id: playlist.id.to_string(),
|
||||||
|
name: playlist.name.to_string(),
|
||||||
|
playlist_type: playlist.playlist_type.to_string(),
|
||||||
|
parent_id: playlist.parent_id,
|
||||||
|
creator: PlaylistCreator {
|
||||||
|
id: creator.id,
|
||||||
|
name: creator.name,
|
||||||
|
},
|
||||||
|
tracks,
|
||||||
|
tracks_count,
|
||||||
|
duration,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<GetPlaylistResponse>>();
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().json(playlists))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/playlists")]
|
#[post("/playlists")]
|
||||||
|
@ -50,7 +69,7 @@ async fn create_playlist(
|
||||||
) -> Result<HttpResponse, ErrorResponse> {
|
) -> Result<HttpResponse, ErrorResponse> {
|
||||||
let user = get_user(req)?;
|
let user = get_user(req)?;
|
||||||
|
|
||||||
playlist.creator_id = Option::from(user.id);
|
playlist.creator_id = user.id;
|
||||||
|
|
||||||
Playlists::create(playlist.into_inner())?;
|
Playlists::create(playlist.into_inner())?;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::middlewares::error::ErrorResponse;
|
use crate::middlewares::error::ErrorResponse;
|
||||||
use crate::models::playlists::{PlaylistCreator, Playlists};
|
use crate::middlewares::user::get_user_option;
|
||||||
use crate::models::tracks::TracksWithArtists;
|
use crate::models::playlists::{PlaylistCreator, PlaylistTrack, Playlists};
|
||||||
use actix_web::{get, web, HttpResponse, Scope};
|
use actix_web::{get, web, HttpRequest, HttpResponse, Scope};
|
||||||
|
use reqwest::StatusCode;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub fn routes() -> Scope {
|
pub fn routes() -> Scope {
|
||||||
|
@ -9,33 +10,42 @@ pub fn routes() -> Scope {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
struct GetPlaylistResponse {
|
pub struct GetPlaylistResponse {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub playlist_type: String,
|
||||||
|
pub parent_id: Option<String>,
|
||||||
pub creator: PlaylistCreator,
|
pub creator: PlaylistCreator,
|
||||||
pub tracks: Vec<TracksWithArtists>,
|
pub tracks: Vec<PlaylistTrack>,
|
||||||
pub tracks_count: usize,
|
pub tracks_count: usize,
|
||||||
pub duration: usize,
|
pub duration: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/{playlist_id}")]
|
#[get("/{playlist_id}")]
|
||||||
async fn get_playlist(path: web::Path<String>) -> Result<HttpResponse, ErrorResponse> {
|
async fn get_playlist(
|
||||||
|
req: HttpRequest,
|
||||||
|
path: web::Path<String>,
|
||||||
|
) -> Result<HttpResponse, ErrorResponse> {
|
||||||
let playlist_id = path.into_inner();
|
let playlist_id = path.into_inner();
|
||||||
let playlist = Playlists::find(playlist_id.as_str())?;
|
let playlist = Playlists::find(playlist_id.as_str())?;
|
||||||
|
|
||||||
|
if !playlist.can_see(get_user_option(req)) {
|
||||||
|
return Err(ErrorResponse::new(
|
||||||
|
"Playlist not found",
|
||||||
|
StatusCode::NOT_FOUND,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let creator = playlist.get_creator()?;
|
let creator = playlist.get_creator()?;
|
||||||
|
|
||||||
let tracks = playlist.get_tracks()?;
|
let tracks = playlist.get_tracks()?;
|
||||||
let tracks_count = tracks.len();
|
let (duration, tracks_count) = playlist.get_data(&tracks);
|
||||||
let duration = tracks
|
|
||||||
.iter()
|
|
||||||
.map(|track| track.duration_ms)
|
|
||||||
.reduce(|a, b| a + b)
|
|
||||||
.unwrap() as usize;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(GetPlaylistResponse {
|
Ok(HttpResponse::Ok().json(GetPlaylistResponse {
|
||||||
id: playlist.id.to_string(),
|
id: playlist.id.to_string(),
|
||||||
name: playlist.name.to_string(),
|
name: playlist.name.to_string(),
|
||||||
|
playlist_type: playlist.playlist_type.to_string(),
|
||||||
|
parent_id: playlist.parent_id,
|
||||||
creator: PlaylistCreator {
|
creator: PlaylistCreator {
|
||||||
id: creator.id,
|
id: creator.id,
|
||||||
name: creator.name,
|
name: creator.name,
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
use crate::middlewares::error::ErrorResponse;
|
use crate::middlewares::error::ErrorResponse;
|
||||||
use crate::models::playlists::Playlists;
|
use crate::models::{
|
||||||
use crate::models::user::Users;
|
playlists::{PlaylistCreator, Playlists},
|
||||||
|
users::Users,
|
||||||
|
};
|
||||||
|
use crate::routes::playlists::GetPlaylistResponse;
|
||||||
use actix_web::http::StatusCode;
|
use actix_web::http::StatusCode;
|
||||||
use actix_web::{get, web, HttpResponse, Result, Scope};
|
use actix_web::{get, web, HttpResponse, Result, Scope};
|
||||||
use diesel::result::Error as DBError;
|
use diesel::result::Error as DBError;
|
||||||
|
@ -49,6 +52,29 @@ async fn get_user_playlists(path: web::Path<String>) -> Result<HttpResponse, Err
|
||||||
let user = get_a_user(&path.into_inner())?;
|
let user = get_a_user(&path.into_inner())?;
|
||||||
|
|
||||||
let playlists = Playlists::find_for_user(&user.id, true)?;
|
let playlists = Playlists::find_for_user(&user.id, true)?;
|
||||||
|
let playlists = playlists
|
||||||
|
.into_iter()
|
||||||
|
.map(|playlist| {
|
||||||
|
let creator = playlist.get_creator().unwrap();
|
||||||
|
|
||||||
|
let tracks = playlist.get_tracks().unwrap();
|
||||||
|
let (duration, tracks_count) = playlist.get_data(&tracks);
|
||||||
|
|
||||||
|
GetPlaylistResponse {
|
||||||
|
id: playlist.id.to_string(),
|
||||||
|
name: playlist.name.to_string(),
|
||||||
|
playlist_type: playlist.playlist_type.to_string(),
|
||||||
|
parent_id: playlist.parent_id,
|
||||||
|
creator: PlaylistCreator {
|
||||||
|
id: creator.id,
|
||||||
|
name: creator.name,
|
||||||
|
},
|
||||||
|
tracks,
|
||||||
|
tracks_count,
|
||||||
|
duration,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<GetPlaylistResponse>>();
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(playlists))
|
Ok(HttpResponse::Ok().json(playlists))
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,10 @@ diesel::table! {
|
||||||
name -> Varchar,
|
name -> Varchar,
|
||||||
public -> Bool,
|
public -> Bool,
|
||||||
#[max_length = 24]
|
#[max_length = 24]
|
||||||
|
playlist_type -> Varchar,
|
||||||
|
#[max_length = 24]
|
||||||
|
parent_id -> Nullable<Varchar>,
|
||||||
|
#[max_length = 24]
|
||||||
creator_id -> Varchar,
|
creator_id -> Varchar,
|
||||||
created_at -> Nullable<Timestamp>,
|
created_at -> Nullable<Timestamp>,
|
||||||
updated_at -> Nullable<Timestamp>,
|
updated_at -> Nullable<Timestamp>,
|
||||||
|
@ -45,6 +49,7 @@ diesel::table! {
|
||||||
playlist_id -> Varchar,
|
playlist_id -> Varchar,
|
||||||
#[max_length = 24]
|
#[max_length = 24]
|
||||||
track_id -> Varchar,
|
track_id -> Varchar,
|
||||||
|
added_at -> Timestamp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue