diff --git a/src/main.rs b/src/main.rs index 5aa608c..9afce55 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ use axum::Router; use dotenvy::dotenv; use crate::routes::system_router::system_routers; +use crate::routes::user_management_router::user_management_routs; use crate::state::AppState; #[tokio::main] @@ -19,7 +20,7 @@ async fn main() { .unwrap_or_else(|_| panic!("Error creating pool for {}", database_url)); tracing_subscriber::fmt::init(); let app = Router::new() - .nest("/rest", system_routers()) + .nest("/rest", system_routers().merge(user_management_routs())) .with_state(state); let listener = tokio::net::TcpListener::bind("0.0.0.0:3311").await.unwrap(); diff --git a/src/routes/system_router.rs b/src/routes/system_router.rs index c6c7c91..461d765 100644 --- a/src/routes/system_router.rs +++ b/src/routes/system_router.rs @@ -1,5 +1,5 @@ use crate::state::AppState; -use crate::types::types::SubSonicResponses; +use crate::types::types::OpenSubSonicResponses; use axum::{Json, Router, http::StatusCode, routing::get}; pub fn system_routers() -> Router { @@ -9,9 +9,9 @@ pub fn system_routers() -> Router { ) } -async fn get_opensubsonic_extentions() -> (StatusCode, Json) { +async fn get_opensubsonic_extentions() -> (StatusCode, Json) { ( StatusCode::OK, - Json(SubSonicResponses::open_subsonic_extentions()), + Json(OpenSubSonicResponses::open_subsonic_extentions()), ) } diff --git a/src/routes/user_management_router.rs b/src/routes/user_management_router.rs index aa1dfc5..8018576 100644 --- a/src/routes/user_management_router.rs +++ b/src/routes/user_management_router.rs @@ -1,28 +1,56 @@ -use crate::{db::models::NewUser, schema::users, state::AppState}; -use axum::{Json, Router, extract::{Form, Query, State}, http::StatusCode, response::IntoResponse, routing::{Route, get}}; -use diesel::{RunQueryDsl, insert_into}; -use serde_json::json; +use crate::{ + db::models::NewUser, + schema::users, + state::AppState, + types::types::{OpenSubSonicResponses, OpenSubsonicErrorCode}, +}; +use axum::{ + Json, Router, + extract::{Form, State, rejection::FormRejection}, + http::StatusCode, + routing::get, +}; +use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl, insert_into}; -fn user_management_routs() -> Router { - let userm_routs = Router::new() - .route("/createUser.view", get())create_user)) +pub fn user_management_routs() -> Router { + let userm_routs = Router::new().route("/createUser.view", get(create_user).post(create_user)); + return userm_routs; +} + +fn check_if_user_exist(user_name: &String, db_con: &AppState) -> bool { + let conn = &mut db_con.pool.get().unwrap(); + let result = users::table + .select(users::id) + .filter(users::username.eq(user_name)) + .first::>(conn); + match result { + Ok(_) => true, + Err(_) => false, + } } async fn create_user( - query: Option>, - form: Option>, - State(db_con): State -) -> { - let params = match (query, form) { - (Some(q), _) => q.0, - (_, Some(f)) => f.0, - _ => { - return ( StatusCode::BAD_REQUEST, Json(json!({ "error": "missing parameters" }))) - } + State(db_con): State, + form: Result, FormRejection>, +) -> (StatusCode, Json) { + let params = if let Ok(Form(parsed)) = form { + parsed + } else { + return ( + StatusCode::BAD_REQUEST, + Json(OpenSubSonicResponses::open_subsonic_response()), + ); }; - insert_into(users::table).values(params).execute(db_con); - - - - )), - )} + if check_if_user_exist(¶ms.username, &db_con) { + let response = OpenSubSonicResponses::open_subsonic_response_error( + OpenSubsonicErrorCode::WrongUsernameOrPassword, + ); + return (StatusCode::CONFLICT, Json(response)); + }; + let conn = &mut db_con.pool.get().unwrap(); + let _ = insert_into(users::table).values(params).execute(conn); + ( + StatusCode::OK, + Json(OpenSubSonicResponses::open_subsonic_response()), + ) +} diff --git a/src/types/system.rs b/src/types/system.rs index cda2c7d..e949ac0 100644 --- a/src/types/system.rs +++ b/src/types/system.rs @@ -3,5 +3,10 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] pub struct OpenSubSonicExtension { pub name: String, - pub versions: Vec, + pub versions: Vec, +} + +#[derive(Serialize, Deserialize)] +pub struct OpenSubSonicExtensions { + pub openSubsonicExtensions: Vec, } diff --git a/src/types/types.rs b/src/types/types.rs index 1377da5..3acfe78 100644 --- a/src/types/types.rs +++ b/src/types/types.rs @@ -1,46 +1,121 @@ use serde::{Deserialize, Serialize}; -use crate::types::system::OpenSubSonicExtension; +use crate::types::system::{OpenSubSonicExtension, OpenSubSonicExtensions}; + +#[derive(Debug, Clone, Copy)] +pub enum OpenSubsonicErrorCode { + Generic = 0, + MissingParameter = 10, + ClientMustUpgrade = 20, + ServerMustUpgrade = 30, + WrongUsernameOrPassword = 40, + TokenAuthNotSupportedForLdap = 41, + AuthMechanismNotSupported = 42, + ConflictingAuthMechanisms = 43, + InvalidApiKey = 44, + NotAuthorized = 50, + TrialExpired = 60, + NotFound = 70, +} + +impl OpenSubsonicErrorCode { + pub const fn description(self) -> &'static str { + match self { + Self::Generic => "A generic error.", + Self::MissingParameter => "Required parameter is missing.", + Self::ClientMustUpgrade => { + "Incompatible Subsonic REST protocol version. Client must upgrade." + } + Self::ServerMustUpgrade => { + "Incompatible Subsonic REST protocol version. Server must upgrade." + } + Self::WrongUsernameOrPassword => "Wrong username or password.", + Self::TokenAuthNotSupportedForLdap => { + "Token authentication not supported for LDAP users." + } + Self::AuthMechanismNotSupported => "Provided authentication mechanism not supported.", + Self::ConflictingAuthMechanisms => { + "Multiple conflicting authentication mechanisms provided." + } + Self::InvalidApiKey => "Invalid API key.", + Self::NotAuthorized => "User is not authorized for the given operation.", + Self::TrialExpired => { + "The trial period for the Subsonic server is over. Please upgrade to Subsonic Premium." + } + Self::NotFound => "The requested data was not found.", + } + } +} #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct BaseResponse { +pub struct BaseResponse { status: String, version: String, #[serde(rename = "type")] type_name: String, server_version: String, open_subsonic: bool, + #[serde(flatten)] + data: Option, +} + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ErrorResponse { + code: u8, + message: String, } #[derive(Serialize, Deserialize)] #[serde(untagged)] -pub enum SubSonicResponses { - SubSonicResponse { +pub enum OpenSubSonicResponses { + OpenSubSonicResponseError { #[serde(rename = "subsonic-response")] - subsonic_reponse: BaseResponse, + subsonic_response: BaseResponse, + }, + OpenSubSonicResponse { + #[serde(rename = "subsonic-response")] + subsonic_response: BaseResponse<()>, }, OpenSubSonicExtensions { #[serde(rename = "subsonic-response")] - subsonic_reponse: BaseResponse, - openSubSonicExtensions: Vec, + subsonic_response: BaseResponse, + // openSubSonicExtensions: Vec, }, } -impl SubSonicResponses { - fn base_response() -> BaseResponse { +impl OpenSubSonicResponses { + fn base_response(inner_response: Option) -> BaseResponse { BaseResponse { status: "ok".to_string(), version: "1.16.1".to_string(), type_name: "SoundSonic".to_string(), - server_version: "1.".to_string(), + server_version: "1.16.1".to_string(), open_subsonic: true, + data: inner_response, } } - pub fn subsonic_response() -> Self { - Self::SubSonicResponse { - subsonic_reponse: Self::base_response(), + pub fn open_subsonic_response_error(error_code: OpenSubsonicErrorCode) -> Self { + Self::OpenSubSonicResponseError { + subsonic_response: BaseResponse { + status: "failed".to_string(), + version: "1.16.1".to_string(), + type_name: "SoundSonic".to_string(), + server_version: "1.16.1".to_string(), + open_subsonic: true, + data: Some(ErrorResponse { + code: error_code as u8, + message: error_code.description().to_string(), + }), + }, + } + } + + pub fn open_subsonic_response() -> Self { + Self::OpenSubSonicResponse { + subsonic_response: Self::base_response(None::<()>), } } @@ -72,8 +147,9 @@ impl SubSonicResponses { }); Self::OpenSubSonicExtensions { - subsonic_reponse: Self::base_response(), - openSubSonicExtensions: extension, + subsonic_response: Self::base_response(Some(OpenSubSonicExtensions { + openSubsonicExtensions: extension, + })), } } }