finish postgres
This commit is contained in:
parent
7b7e1a4014
commit
08e24f63f4
16 changed files with 454 additions and 306 deletions
|
@ -1,8 +1,7 @@
|
|||
use super::api_error;
|
||||
use super::FallibleApiResponse;
|
||||
use mongod::vec_to_api;
|
||||
use mongod::Model;
|
||||
use mongodb::bson::doc;
|
||||
use crate::get_pg;
|
||||
use crate::route::vec_to_api;
|
||||
use rocket::{get, State};
|
||||
use serde_json::json;
|
||||
|
||||
|
@ -21,13 +20,11 @@ pub async fn clean_library(lib: &State<Libary>, u: User) -> FallibleApiResponse
|
|||
#[get("/library/singles")]
|
||||
pub async fn get_singles_route(u: User) -> FallibleApiResponse {
|
||||
check_admin!(u);
|
||||
let singles = Track::find(
|
||||
doc! { "album_id": None::<String>, "artist_id": {"$ne": None::<String> }},
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let singles: Vec<Track> =
|
||||
sqlx::query_as("SELECT * FROM track WHERE album IS NULL AND artist IS NOT NULL")
|
||||
.fetch_all(get_pg!())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Ok(json!(vec_to_api(&singles).await))
|
||||
}
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
use std::cmp::Ordering;
|
||||
|
||||
use super::api_error;
|
||||
use super::to_uuid;
|
||||
use super::FallibleApiResponse;
|
||||
use mongod::vec_to_api;
|
||||
use mongod::Model;
|
||||
use mongod::Referencable;
|
||||
use mongodb::bson::doc;
|
||||
use rocket::fs::NamedFile;
|
||||
use rocket::{get, State};
|
||||
use serde_json::json;
|
||||
|
@ -14,13 +11,14 @@ use crate::cache::RouteCache;
|
|||
use crate::library::album::Album;
|
||||
use crate::library::track::Track;
|
||||
use crate::library::Libary;
|
||||
use crate::route::vec_to_api;
|
||||
use crate::route::ToAPI;
|
||||
use crate::use_api_cache;
|
||||
use mongod::ToAPI;
|
||||
|
||||
#[get("/artist/<artist_id>/albums")]
|
||||
pub async fn albums_route(artist_id: &str, lib: &State<Libary>) -> FallibleApiResponse {
|
||||
let albums = lib.get_albums_by_artist(artist_id).await;
|
||||
let singles = lib.get_singles_by_artist(artist_id).await;
|
||||
let albums = lib.get_albums_by_artist(&to_uuid(artist_id)?).await;
|
||||
let singles = lib.get_singles_by_artist(&to_uuid(artist_id)?).await;
|
||||
|
||||
Ok(json!({
|
||||
"artist": artist_id,
|
||||
|
@ -49,7 +47,7 @@ pub async fn album_cover_route(
|
|||
NamedFile::open(
|
||||
cache
|
||||
.get_option("album_cover_route", album_id, || async {
|
||||
let album = lib.get_album_by_id(album_id).await?;
|
||||
let album = lib.get_album_by_id(&to_uuid(album_id).unwrap()).await?;
|
||||
album.get_cover().await
|
||||
})
|
||||
.await?,
|
||||
|
@ -60,18 +58,16 @@ pub async fn album_cover_route(
|
|||
|
||||
#[get("/albums/latest")]
|
||||
pub async fn latest_albums_route(cache: &State<RouteCache>) -> FallibleApiResponse {
|
||||
// todo : fix
|
||||
|
||||
use_api_cache!("albums", "latest", cache);
|
||||
|
||||
let albums = Album::find_all().await.unwrap();
|
||||
let albums = Album::find_all().await;
|
||||
|
||||
let mut albums_tracks = vec![];
|
||||
|
||||
for album in &albums {
|
||||
albums_tracks.push(
|
||||
Track::find_one(doc! { "album_id": album.reference()})
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
albums_tracks.push(Track::find_first_of_album(&album.id).await.unwrap());
|
||||
}
|
||||
|
||||
let mut joined: Vec<(_, _)> = albums.into_iter().zip(albums_tracks).collect();
|
||||
|
@ -100,11 +96,11 @@ pub async fn album_route(
|
|||
use_api_cache!("album_route", album_id, cache);
|
||||
|
||||
let album = lib
|
||||
.get_album_by_id(album_id)
|
||||
.get_album_by_id(&to_uuid(album_id)?)
|
||||
.await
|
||||
.ok_or_else(|| api_error("No album with that ID found"))?;
|
||||
|
||||
let tracks = Album::get_tracks_of_album(&format!("album::{}", album._id)).await;
|
||||
let tracks = Album::get_tracks_of_album(&album.id).await;
|
||||
|
||||
let mut tracks = vec_to_api(&tracks).await;
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use super::api_error;
|
||||
use super::to_uuid;
|
||||
use super::vec_to_api;
|
||||
use super::FallibleApiResponse;
|
||||
use super::ToAPI;
|
||||
use fs::NamedFile;
|
||||
use mongod::vec_to_api;
|
||||
use mongod::Model;
|
||||
use mongod::ToAPI;
|
||||
use mongodb::bson::doc;
|
||||
use rocket::{fs, get, State};
|
||||
|
||||
|
@ -22,7 +22,7 @@ pub async fn artists_route(lib: &State<Libary>) -> FallibleApiResponse {
|
|||
pub async fn artist_image_route(id: &str, cache: &State<RouteCache>) -> Option<NamedFile> {
|
||||
let image = cache
|
||||
.get_option("artist_image_route", id, || async {
|
||||
Artist::get_image_of(id).await
|
||||
Artist::get_image_of(&to_uuid(id).ok()?).await
|
||||
})
|
||||
.await;
|
||||
|
||||
|
@ -31,7 +31,7 @@ pub async fn artist_image_route(id: &str, cache: &State<RouteCache>) -> Option<N
|
|||
|
||||
#[get("/artist/<id>")]
|
||||
pub async fn artist_route(id: &str) -> FallibleApiResponse {
|
||||
Ok(Artist::get(id)
|
||||
Ok(Artist::get(&to_uuid(id)?)
|
||||
.await
|
||||
.ok_or_else(|| api_error("No artist with that ID found"))?
|
||||
.api()
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
use super::api_error;
|
||||
use super::to_uuid;
|
||||
use super::FallibleApiResponse;
|
||||
use mongod::reference_of;
|
||||
use mongod::Model;
|
||||
use mongodb::bson::doc;
|
||||
use rocket::post;
|
||||
use rocket::serde::json::Json;
|
||||
use serde::Deserialize;
|
||||
|
@ -12,7 +10,6 @@ use crate::library::event::Event;
|
|||
use crate::library::event::EventKind;
|
||||
use crate::library::track::Track;
|
||||
use crate::library::user::User;
|
||||
use mongod::Referencable;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct EventJson {
|
||||
|
@ -26,7 +23,10 @@ pub async fn event_report_route(report: Json<EventJson>, u: User) -> FallibleApi
|
|||
Event::create(
|
||||
report.kind.clone(),
|
||||
&u,
|
||||
reference_of!(Track, track).ok_or_else(|| api_error("Invalid track"))?,
|
||||
Track::get(&to_uuid(&track)?)
|
||||
.await
|
||||
.ok_or_else(|| api_error("Invalid track"))?
|
||||
.id,
|
||||
)
|
||||
.await;
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use rocket::{
|
||||
get,
|
||||
response::{status::BadRequest, Redirect},
|
||||
|
@ -16,9 +18,34 @@ pub mod user;
|
|||
|
||||
// todo : rework api
|
||||
|
||||
/// A trait to generate a Model API representation in JSON format.
|
||||
pub trait ToAPI: Sized {
|
||||
/// Generate public API JSON
|
||||
fn api(&self) -> impl std::future::Future<Output = serde_json::Value>;
|
||||
}
|
||||
|
||||
/// Converts a slice of items implementing the `ToAPI` trait into a `Vec` of JSON values.
|
||||
pub async fn vec_to_api(items: &[impl ToAPI]) -> Vec<serde_json::Value> {
|
||||
let mut ret = Vec::with_capacity(items.len());
|
||||
|
||||
for e in items {
|
||||
ret.push(e.api().await);
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn to_uuid(id: &str) -> Result<uuid::Uuid, ApiError> {
|
||||
uuid::Uuid::from_str(id).map_err(|_| no_uuid_error())
|
||||
}
|
||||
|
||||
type ApiError = BadRequest<serde_json::Value>;
|
||||
type FallibleApiResponse = Result<serde_json::Value, ApiError>;
|
||||
|
||||
pub fn no_uuid_error() -> ApiError {
|
||||
api_error("No valid UUID")
|
||||
}
|
||||
|
||||
pub fn api_error(msg: &str) -> ApiError {
|
||||
BadRequest(json!({
|
||||
"error": msg
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
use crate::get_pg;
|
||||
use crate::library::track::Track;
|
||||
use crate::library::user::User;
|
||||
use mongod::reference_of;
|
||||
use mongod::vec_to_api;
|
||||
use mongod::Model;
|
||||
use mongod::Referencable;
|
||||
use mongodb::bson::doc;
|
||||
use rocket::get;
|
||||
use rocket::post;
|
||||
|
@ -15,7 +12,8 @@ use crate::library::playlist::Visibility;
|
|||
use crate::route::FallibleApiResponse;
|
||||
|
||||
use super::api_error;
|
||||
use mongod::ToAPI;
|
||||
use super::to_uuid;
|
||||
use super::vec_to_api;
|
||||
|
||||
#[get("/playlists")]
|
||||
pub async fn playlists_route(u: User) -> FallibleApiResponse {
|
||||
|
@ -24,13 +22,11 @@ pub async fn playlists_route(u: User) -> FallibleApiResponse {
|
|||
json!({"id": "recentlyAdded", "name": "Recently Added"}),
|
||||
];
|
||||
|
||||
let own_playlists = Playlist::find(doc! { "owner": u.reference()}, None, None)
|
||||
.await
|
||||
.unwrap();
|
||||
let own_playlists = Playlist::of(&u).await;
|
||||
|
||||
for playlist in own_playlists {
|
||||
playlists.push(json!({
|
||||
"id": playlist._id,
|
||||
"id": playlist.id,
|
||||
"name": playlist.title
|
||||
}));
|
||||
}
|
||||
|
@ -39,9 +35,7 @@ pub async fn playlists_route(u: User) -> FallibleApiResponse {
|
|||
}
|
||||
|
||||
pub async fn recently_added_playlist() -> FallibleApiResponse {
|
||||
let tracks = Track::find(doc! {}, Some(90), Some(doc! { "date_added": -1 }))
|
||||
.await
|
||||
.unwrap();
|
||||
let tracks = Track::find_recently_added().await;
|
||||
Ok(json!(vec_to_api(&tracks).await))
|
||||
}
|
||||
|
||||
|
@ -49,8 +43,8 @@ pub async fn recently_added_playlist() -> FallibleApiResponse {
|
|||
pub async fn playlist_route(id: &str, u: User) -> FallibleApiResponse {
|
||||
if id == "recents" {
|
||||
return Ok(Playlist {
|
||||
_id: "recents".to_string(),
|
||||
owner: u.reference(),
|
||||
id: "recents".to_string(),
|
||||
owner: u.username,
|
||||
title: "Recently Played".to_string(),
|
||||
visibility: Visibility::Public,
|
||||
tracks: vec![],
|
||||
|
@ -61,8 +55,8 @@ pub async fn playlist_route(id: &str, u: User) -> FallibleApiResponse {
|
|||
|
||||
if id == "recentlyAdded" {
|
||||
return Ok(Playlist {
|
||||
_id: "recentlyAdded".to_string(),
|
||||
owner: u.reference(),
|
||||
id: "recentlyAdded".to_string(),
|
||||
owner: u.username,
|
||||
title: "Recently Added".to_string(),
|
||||
visibility: Visibility::Public,
|
||||
tracks: vec![],
|
||||
|
@ -71,13 +65,11 @@ pub async fn playlist_route(id: &str, u: User) -> FallibleApiResponse {
|
|||
.await);
|
||||
}
|
||||
|
||||
let playlist = Playlist::get(id)
|
||||
let playlist = Playlist::get(&to_uuid(id)?)
|
||||
.await
|
||||
.ok_or_else(|| api_error("No playlist with that ID found"))?;
|
||||
|
||||
if matches!(playlist.visibility, Visibility::Private)
|
||||
&& u.username != playlist.owner.get::<User>().await.username
|
||||
{
|
||||
if matches!(playlist.visibility, Visibility::Private) && u.username != playlist.owner {
|
||||
return Err(api_error("Forbidden"));
|
||||
}
|
||||
|
||||
|
@ -95,22 +87,26 @@ pub async fn playlist_tracks_route(id: &str, u: User) -> FallibleApiResponse {
|
|||
return recently_added_playlist().await;
|
||||
}
|
||||
|
||||
let playlist = Playlist::get(id)
|
||||
let playlist = Playlist::get(&to_uuid(id)?)
|
||||
.await
|
||||
.ok_or_else(|| api_error("No playlist with that ID found"))?;
|
||||
|
||||
if matches!(playlist.visibility, Visibility::Private)
|
||||
&& u.username != playlist.owner.get::<User>().await.username
|
||||
{
|
||||
if matches!(playlist.visibility, Visibility::Private) && u.username != playlist.owner {
|
||||
return Err(api_error("Forbidden"));
|
||||
}
|
||||
|
||||
let mut tracks: Vec<Track> = vec![];
|
||||
let mut tracks: Vec<uuid::Uuid> = vec![];
|
||||
|
||||
for track in playlist.tracks {
|
||||
tracks.push(track.get().await);
|
||||
tracks.push(track);
|
||||
}
|
||||
|
||||
let tracks: Vec<Track> = sqlx::query_as("SELECT * FROM track WHERE id IN ANY($1)")
|
||||
.bind(tracks)
|
||||
.fetch_all(get_pg!())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Ok(json!(vec_to_api(&tracks).await))
|
||||
}
|
||||
|
||||
|
@ -118,7 +114,7 @@ pub async fn playlist_tracks_route(id: &str, u: User) -> FallibleApiResponse {
|
|||
pub struct PlaylistData {
|
||||
pub title: Option<String>,
|
||||
pub visibility: Option<Visibility>,
|
||||
pub tracks: Vec<String>,
|
||||
pub tracks: Vec<uuid::Uuid>,
|
||||
}
|
||||
|
||||
#[post("/playlist", data = "<playlist>")]
|
||||
|
@ -137,9 +133,8 @@ pub async fn playlist_add_route(playlist: Json<PlaylistData>, u: User) -> Fallib
|
|||
)
|
||||
.await
|
||||
.ok_or_else(|| api_error("Failed to create playlist"))?;
|
||||
playlist.insert().await.unwrap();
|
||||
|
||||
Ok(json!({"created": playlist._id}))
|
||||
Ok(json!({"created": playlist.id}))
|
||||
}
|
||||
|
||||
#[post("/playlist/<id>", data = "<edit>")]
|
||||
|
@ -148,34 +143,36 @@ pub async fn playlist_edit_route(
|
|||
edit: Json<PlaylistData>,
|
||||
u: User,
|
||||
) -> FallibleApiResponse {
|
||||
let playlist = Playlist::get(id)
|
||||
let playlist = Playlist::get(&to_uuid(id)?)
|
||||
.await
|
||||
.ok_or_else(|| api_error("No playlist with that ID found"))?;
|
||||
|
||||
if playlist.owner.id() != u._id {
|
||||
if playlist.owner != u.username {
|
||||
return Err(api_error("Forbidden"));
|
||||
}
|
||||
|
||||
let mut tracks_ref = vec![];
|
||||
|
||||
for track in &edit.tracks {
|
||||
tracks_ref
|
||||
.push(reference_of!(Track, track).ok_or_else(|| api_error("Invalid tracks found"))?);
|
||||
tracks_ref.push(
|
||||
Track::get(track)
|
||||
.await
|
||||
.ok_or_else(|| api_error("Invalid tracks found"))?
|
||||
.id,
|
||||
);
|
||||
}
|
||||
|
||||
let playlist_id = playlist._id.clone();
|
||||
let playlist_id = playlist.id.clone();
|
||||
|
||||
let mut changed = playlist.change();
|
||||
let new_title = edit.title.as_ref().unwrap_or(&playlist.title);
|
||||
let new_vis = edit.visibility.as_ref().unwrap_or(&playlist.visibility);
|
||||
|
||||
if let Some(title) = &edit.title {
|
||||
changed = changed.title(title);
|
||||
}
|
||||
|
||||
if let Some(visibility) = &edit.visibility {
|
||||
changed = changed.visibility(visibility.clone());
|
||||
}
|
||||
|
||||
changed.tracks(tracks_ref).update().await.unwrap();
|
||||
sqlx::query("UPDATE playlist SET title = $1, visibility = $2, tracks = $3 WHERE id = $4")
|
||||
.bind(new_title)
|
||||
.bind(new_vis)
|
||||
.bind(tracks_ref)
|
||||
.bind(&to_uuid(&playlist_id)?)
|
||||
.fetch(get_pg!());
|
||||
|
||||
Ok(json!({"edited": playlist_id}))
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use super::api_error;
|
||||
use super::no_uuid_error;
|
||||
use super::to_uuid;
|
||||
use super::FallibleApiResponse;
|
||||
use super::ToAPI;
|
||||
use crate::library::user::User;
|
||||
use fs::NamedFile;
|
||||
use mongod::ToAPI;
|
||||
use mongodb::bson::doc;
|
||||
use rocket::{fs, get, State};
|
||||
use serde_json::json;
|
||||
|
||||
|
@ -13,7 +16,7 @@ use crate::library::Libary;
|
|||
#[get("/track/<track_id>")]
|
||||
pub async fn track_route(track_id: &str, lib: &State<Libary>) -> FallibleApiResponse {
|
||||
Ok(lib
|
||||
.get_track_by_id(track_id)
|
||||
.get_track_by_id(&to_uuid(track_id)?)
|
||||
.await
|
||||
.ok_or_else(|| api_error("No track with that ID found"))?
|
||||
.api()
|
||||
|
@ -27,7 +30,7 @@ pub async fn track_reload_meta_route(
|
|||
u: User,
|
||||
) -> FallibleApiResponse {
|
||||
check_admin!(u);
|
||||
lib.reload_metadata(track_id)
|
||||
lib.reload_metadata(&to_uuid(track_id)?)
|
||||
.await
|
||||
.map_err(|_| api_error("Error reloading metadata"))?;
|
||||
Ok(json!({"ok": 1}))
|
||||
|
@ -35,7 +38,9 @@ pub async fn track_reload_meta_route(
|
|||
|
||||
#[get("/track/<track_id>/audio")]
|
||||
pub async fn track_audio_route(track_id: &str, lib: &State<Libary>) -> Option<NamedFile> {
|
||||
let track = lib.get_track_by_id(track_id).await?;
|
||||
let track = lib
|
||||
.get_track_by_id(&uuid::Uuid::from_str(track_id).ok()?)
|
||||
.await?;
|
||||
NamedFile::open(std::path::Path::new(&track.path))
|
||||
.await
|
||||
.ok()
|
||||
|
@ -43,12 +48,16 @@ pub async fn track_audio_route(track_id: &str, lib: &State<Libary>) -> Option<Na
|
|||
|
||||
#[get("/track/<track_id>/audio/opus128")]
|
||||
pub async fn track_audio_opus128_route(track_id: &str, lib: &State<Libary>) -> Option<NamedFile> {
|
||||
let track = lib.get_track_by_id(track_id).await?;
|
||||
let track = lib
|
||||
.get_track_by_id(&uuid::Uuid::from_str(track_id).ok()?)
|
||||
.await?;
|
||||
NamedFile::open(track.get_opus(128)?).await.ok()
|
||||
}
|
||||
|
||||
#[get("/track/<track_id>/audio/aac128")]
|
||||
pub async fn track_audio_aac128_route(track_id: &str, lib: &State<Libary>) -> Option<NamedFile> {
|
||||
let track = lib.get_track_by_id(track_id).await?;
|
||||
let track = lib
|
||||
.get_track_by_id(&uuid::Uuid::from_str(track_id).ok()?)
|
||||
.await?;
|
||||
NamedFile::open(track.get_aac(128)?).await.ok()
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
use crate::get_pg;
|
||||
use crate::library::user::Session;
|
||||
use crate::library::user::User;
|
||||
use mongod::vec_to_api;
|
||||
use mongod::Model;
|
||||
use mongod::ToAPI;
|
||||
use mongodb::bson::doc;
|
||||
use crate::route::vec_to_api;
|
||||
use rocket::get;
|
||||
use rocket::http::Status;
|
||||
use rocket::outcome::Outcome;
|
||||
|
@ -33,8 +31,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(session) = Session::find_one(doc! { "token": key}).await {
|
||||
let user = session.user.get().await;
|
||||
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() {
|
||||
Outcome::Success(user)
|
||||
} else {
|
||||
Outcome::Error((Status::Unauthorized, ()))
|
||||
|
@ -84,7 +81,7 @@ pub async fn passwd_route(passwd: Json<PasswdData>, u: User) -> FallibleApiRespo
|
|||
pub async fn users_route(u: User) -> FallibleApiResponse {
|
||||
check_admin!(u);
|
||||
|
||||
let users: Vec<_> = vec_to_api(&User::find_all().await.unwrap()).await;
|
||||
let users: Vec<_> = vec_to_api(&User::find_all().await).await;
|
||||
|
||||
Ok(json!({"users": users}))
|
||||
}
|
||||
|
@ -101,5 +98,5 @@ pub async fn user_create_route(user: Json<LoginData>, u: User) -> FallibleApiRes
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
Ok(json!({"created": new_user._id}))
|
||||
Ok(json!({"created": new_user.username}))
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue