summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml4
-rw-r--r--README.md4
-rw-r--r--curlie_guide8
-rw-r--r--src/api/album.rs51
-rw-r--r--src/api/artist.rs53
-rw-r--r--src/api/mod.rs6
-rw-r--r--src/api/search_results.rs12
-rw-r--r--src/api/song.rs51
-rw-r--r--src/auth.rs53
-rw-r--r--src/database/album.rs35
-rw-r--r--src/database/artist.rs11
-rw-r--r--src/database/mod.rs4
-rw-r--r--src/database/song.rs18
-rw-r--r--src/database/user.rs5
-rw-r--r--src/doc.rs56
-rw-r--r--src/extractors/auth_token.rs21
-rw-r--r--src/main.rs59
17 files changed, 336 insertions, 115 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 884e50d..58dbb62 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
-name = "ver1"
-version = "0.1.0"
+name = "Balalaika"
+version = "1.0.0"
edition = "2021"
[dependencies]
diff --git a/README.md b/README.md
index 4edf304..0156eb0 100644
--- a/README.md
+++ b/README.md
@@ -49,7 +49,3 @@ Routes such as /song are now /api/song.
### Authentication routes have been added
In order to create/remove users and retrieve the tokens,
a new scope has been created (/auth).
-
-## TODO
-[X] - User / Password system, use user id to generate tokens
-[ ] - Add documentation (Utoipa)
diff --git a/curlie_guide b/curlie_guide
index 7db110d..df77402 100644
--- a/curlie_guide
+++ b/curlie_guide
@@ -4,11 +4,3 @@ curlie
(:8000/api/[song/album/artist] || :8000/auth/[encode_token/decode_token/protected])
argument=value (ex: id=2)
header:value (Authorization:token_here)
-
-== decode-auth ==
-curlie [-f POST] :8000/auth/decode-token token="token_here"
-
-
-
-
-(saving just in case: Accept:application/json)
diff --git a/src/api/album.rs b/src/api/album.rs
index 9094495..f8731fb 100644
--- a/src/api/album.rs
+++ b/src/api/album.rs
@@ -5,15 +5,25 @@ use crate::extractors::auth_token::AuthenticationToken;
use crate::AppState;
use actix_web::{delete, get, post, put, web, HttpResponse};
use serde::Deserialize;
+use utoipa::IntoParams;
/* Possible arguments ( /album?arg=value ) */
-#[derive(Deserialize)]
+#[derive(Deserialize, IntoParams)]
struct AlbumQueryOptions {
id: Option<String>,
name: Option<String>,
artist: Option<String>,
}
+#[utoipa::path(
+ params(AlbumQueryOptions),
+ context_path = "/api",
+ description = "Gets a list of the current albums and applies filters based on the url parameters recieved. It only accepts one parameter at a time.",
+ responses(
+ (status = 200, description = "Return a list of albums", body = Vec<Album>),
+ (status = 400, description = "Errors found, unfulfilled request"),
+ ),
+)]
#[get("/album")]
pub async fn get_album(
app_state: web::Data<AppState>,
@@ -53,6 +63,19 @@ pub async fn get_album(
}
}
+#[utoipa::path(
+ request_body = AlbumPost,
+ context_path = "/api",
+ description = "Creates a new album with the specified values.",
+ responses(
+ (status = 200, description = "Create new album", body = Response),
+ (status = 400, description = "Errors found, unfulfilled request"),
+ (status = 401, description = "Authentication failed"),
+ ),
+ security(
+ {"bearer_auth" = []}
+ ),
+)]
#[post("/album")]
pub async fn post_album(
app_state: web::Data<AppState>,
@@ -68,6 +91,19 @@ pub async fn post_album(
)
}
+#[utoipa::path(
+ request_body = AlbumPut,
+ context_path = "/api",
+ description = "Edits the values of the specified album.",
+ responses(
+ (status = 200, description = "Edit album values", body = Response),
+ (status = 400, description = "Errors found or album not found"),
+ (status = 401, description = "Authentication failed"),
+ ),
+ security(
+ {"bearer_auth" = []}
+ ),
+)]
#[put("/album")]
pub async fn put_album(
app_state: web::Data<AppState>,
@@ -83,6 +119,19 @@ pub async fn put_album(
)
}
+#[utoipa::path(
+ request_body = Delete,
+ context_path = "/api",
+ description = "Deletes the specified album.",
+ responses(
+ (status = 200, description = "Delete existing album", body = Response),
+ (status = 400, description = "Errors found or album not found"),
+ (status = 401, description = "Authentication failed"),
+ ),
+ security(
+ {"bearer_auth" = []}
+ ),
+)]
#[delete("/album")]
pub async fn delete_album(
app_state: web::Data<AppState>,
diff --git a/src/api/artist.rs b/src/api/artist.rs
index d36ccf4..1fdae37 100644
--- a/src/api/artist.rs
+++ b/src/api/artist.rs
@@ -5,14 +5,24 @@ use crate::extractors::auth_token::AuthenticationToken;
use crate::AppState;
use actix_web::{delete, get, post, put, web, HttpResponse};
use serde::Deserialize;
+use utoipa::IntoParams;
/* Possible arguments ( /artist?arg=value ) */
-#[derive(Deserialize)]
-struct ArtistQueryOptions {
+#[derive(Deserialize, IntoParams)]
+pub struct ArtistQueryOptions {
id: Option<String>,
name: Option<String>,
}
+#[utoipa::path(
+ params(ArtistQueryOptions),
+ context_path = "/api",
+ description = "Gets a list of the current artists and applies filters based on the url parameters recieved. It only accepts one parameter at a time.",
+ responses(
+ (status = 200, description = "Return a list of artists", body = Vec<Artist>),
+ (status = 400, description = "Errors found, unfulfilled request"),
+ ),
+)]
#[get("/artist")]
pub async fn get_artist(
app_state: web::Data<AppState>,
@@ -48,6 +58,19 @@ pub async fn get_artist(
}
}
+#[utoipa::path(
+ request_body = ArtistPost,
+ context_path = "/api",
+ description = "Creates a new artist with the specified name.",
+ responses(
+ (status = 200, description = "Create new artist", body = Response),
+ (status = 400, description = "Errors found, unfulfilled request"),
+ (status = 401, description = "Authentication failed"),
+ ),
+ security(
+ {"bearer_auth" = []}
+ ),
+)]
#[post("/artist")]
pub async fn post_artist(
app_state: web::Data<AppState>,
@@ -63,6 +86,19 @@ pub async fn post_artist(
)
}
+#[utoipa::path(
+ request_body = ArtistPut,
+ context_path = "/api",
+ description = "Edits the name of the specified artist.",
+ responses(
+ (status = 200, description = "Edit artist name", body = Response),
+ (status = 400, description = "Errors found or artist not found"),
+ (status = 401, description = "Authentication failed"),
+ ),
+ security(
+ {"bearer_auth" = []}
+ ),
+)]
#[put("/artist")]
pub async fn put_artist(
app_state: web::Data<AppState>,
@@ -78,6 +114,19 @@ pub async fn put_artist(
)
}
+#[utoipa::path(
+ request_body = Delete,
+ context_path = "/api",
+ description = "Deletes the specified artist.",
+ responses(
+ (status = 200, description = "Delete existing artist", body = Response),
+ (status = 400, description = "Errors found or artist not found"),
+ (status = 401, description = "Authentication failed"),
+ ),
+ security(
+ {"bearer_auth" = []}
+ ),
+)]
#[delete("/artist")]
pub async fn delete_artist(
app_state: web::Data<AppState>,
diff --git a/src/api/mod.rs b/src/api/mod.rs
index 2a7c3a8..fc97f47 100644
--- a/src/api/mod.rs
+++ b/src/api/mod.rs
@@ -1,5 +1,6 @@
use actix_web::{web, HttpResponse, Scope};
use serde::{Deserialize, Serialize};
+use utoipa::ToSchema;
pub mod album;
pub mod artist;
@@ -24,9 +25,10 @@ pub fn api_scope() -> Scope {
.service(search_results::search_results)
}
-#[derive(Serialize, Deserialize)]
+#[derive(Serialize, Deserialize, ToSchema)]
pub struct Response {
- message: String,
+ #[schema(default = "response")]
+ pub message: String,
}
pub fn get_response_from_query(
diff --git a/src/api/search_results.rs b/src/api/search_results.rs
index f53bce8..cd47054 100644
--- a/src/api/search_results.rs
+++ b/src/api/search_results.rs
@@ -4,12 +4,22 @@ use crate::database::song::Song;
use crate::AppState;
use actix_web::{get, web, HttpResponse};
use serde::Deserialize;
+use utoipa::IntoParams;
-#[derive(Deserialize)]
+#[derive(Deserialize, IntoParams)]
struct SearchQueryOptions {
name: Option<String>,
}
+#[utoipa::path(
+ params(SearchQueryOptions),
+ context_path = "/api",
+ description = "Performs a search based on the 'name' parameter and returns a list.",
+ responses(
+ (status = 200, description = "Return a list of artists, albums and songs"),
+ (status = 400, description = "Errors found, unfulfilled request"),
+ ),
+)]
#[get("/search-results")]
pub async fn search_results(
app_state: web::Data<AppState>,
diff --git a/src/api/song.rs b/src/api/song.rs
index 1fec41c..06bc4b6 100644
--- a/src/api/song.rs
+++ b/src/api/song.rs
@@ -5,9 +5,10 @@ use crate::extractors::auth_token::AuthenticationToken;
use crate::AppState;
use actix_web::{delete, get, post, put, web, HttpResponse};
use serde::Deserialize;
+use utoipa::IntoParams;
/* Possible arguments ( /song?arg=value ) */
-#[derive(Deserialize)]
+#[derive(Deserialize, IntoParams)]
struct SongQueryOptions {
id: Option<String>,
name: Option<String>,
@@ -15,6 +16,15 @@ struct SongQueryOptions {
album: Option<String>,
}
+#[utoipa::path(
+ params(SongQueryOptions),
+ context_path = "/api",
+ description = "Gets a list of the current songs and applies filters based on the url parameters recieved. It only accepts one parameter at a time.",
+ responses(
+ (status = 200, description = "Return a list of songs", body = Vec<Song>),
+ (status = 400, description = "Errors found, unfulfilled request"),
+ ),
+)]
#[get("/song")]
pub async fn get_song(
app_state: web::Data<AppState>,
@@ -58,6 +68,19 @@ pub async fn get_song(
}
}
+#[utoipa::path(
+ request_body = SongPost,
+ context_path = "/api",
+ description = "Creates a new song with the specified values.",
+ responses(
+ (status = 200, description = "Create new song", body = Response),
+ (status = 400, description = "Errors found, unfulfilled request"),
+ (status = 401, description = "Authentication failed"),
+ ),
+ security(
+ {"bearer_auth" = []}
+ ),
+)]
#[post("/song")]
pub async fn post_song(
app_state: web::Data<AppState>,
@@ -73,6 +96,19 @@ pub async fn post_song(
)
}
+#[utoipa::path(
+ request_body = SongPut,
+ context_path = "/api",
+ description = "Edits the values of the specified song.",
+ responses(
+ (status = 200, description = "Edit song values", body = Response),
+ (status = 400, description = "Errors found or song not found"),
+ (status = 401, description = "Authentication failed"),
+ ),
+ security(
+ {"bearer_auth" = []}
+ ),
+)]
#[put("/song")]
pub async fn put_song(
app_state: web::Data<AppState>,
@@ -88,6 +124,19 @@ pub async fn put_song(
)
}
+#[utoipa::path(
+ request_body = Delete,
+ context_path = "/api",
+ description = "Deletes the specified song.",
+ responses(
+ (status = 200, description = "Delete existing song", body = Response),
+ (status = 400, description = "Errors found or song not found"),
+ (status = 401, description = "Authentication failed"),
+ ),
+ security(
+ {"bearer_auth" = []}
+ ),
+)]
#[delete("/song")]
pub async fn delete_song(
app_state: web::Data<AppState>,
diff --git a/src/auth.rs b/src/auth.rs
index 9c6f978..857ef6b 100644
--- a/src/auth.rs
+++ b/src/auth.rs
@@ -4,6 +4,8 @@ use actix_web::{delete, post, web, HttpResponse, Scope};
use chrono::{Duration, Utc};
use jsonwebtoken::{encode, EncodingKey, Header};
use serde::{Deserialize, Serialize};
+pub use crate::api::Response;
+use utoipa::ToSchema;
/* Set up scope */
pub fn auth_scope() -> Scope {
@@ -19,28 +21,23 @@ pub struct Claims {
pub exp: usize,
}
-#[derive(Serialize, Deserialize)]
-struct Response {
- message: String,
-}
-
-#[derive(Serialize, Deserialize)]
+#[derive(Serialize, Deserialize, ToSchema)]
struct EncodeResponse {
+ #[schema(example = "response")]
message: String,
+ #[schema(example = "4f4bf0b9ef653818a56df74cffb024bd")]
token: String,
}
-#[derive(Serialize, Deserialize)]
-struct DecodeResponse {
- message: String,
- id: usize,
-}
-
-#[derive(Serialize, Deserialize)]
-struct DecodeBody {
- token: String,
-}
-
+#[utoipa::path(
+ request_body = UserForm,
+ context_path = "/auth",
+ description = "Creates a new user with the specified values.",
+ responses(
+ (status = 200, description = "Create new user", body = Response),
+ (status = 400, description = "Errors found, unfulfilled request"),
+ ),
+)]
#[post("/register")]
pub async fn register(
app_state: web::Data<AppState>,
@@ -57,6 +54,16 @@ pub async fn register(
}
}
+#[utoipa::path(
+ request_body = UserForm,
+ context_path = "/auth",
+ description = "Attempts to log in user. If successful, it returns an encoded token that grants access to protected routes in the api.",
+ responses(
+ (status = 200, description = "Returns encoded token", body = EncodeResponse),
+ (status = 400, description = "Errors found, unfulfilled request"),
+ (status = 401, description = "Unauthorized"),
+ ),
+)]
#[post("/login")]
pub async fn login(
app_state: web::Data<AppState>,
@@ -67,7 +74,7 @@ pub async fn login(
let result = match query {
Ok(res) => res,
Err(e) => {
- return HttpResponse::BadRequest().json(Response {
+ return HttpResponse::Unauthorized().json(Response {
message: format!("There was an issue in the request: {}", e).to_owned(),
})
}
@@ -113,6 +120,16 @@ async fn encode_token(id: usize, secret: &String) -> Result<String, HttpResponse
};
}
+#[utoipa::path(
+ request_body = UserForm,
+ context_path = "/auth",
+ description = "Attempts to delete user. Both username and password are required.",
+ responses(
+ (status = 200, description = "Delete user", body = Response),
+ (status = 400, description = "Errors found, unfulfilled request"),
+ (status = 401, description = "Unauthorized"),
+ ),
+)]
#[delete("/user")]
pub async fn delete_user(
app_state: web::Data<AppState>,
diff --git a/src/database/album.rs b/src/database/album.rs
index d7ffae0..6b2b46d 100644
--- a/src/database/album.rs
+++ b/src/database/album.rs
@@ -1,28 +1,34 @@
use crate::database::DatabaseWrapper;
use serde::{Deserialize, Serialize};
use sqlx::mysql::MySqlQueryResult;
+use utoipa::ToSchema;
-#[derive(Serialize)]
+#[derive(Serialize, ToSchema)]
pub struct Album {
+ #[schema(example = "album name")]
name: Option<String>,
+ #[schema(example = "1")]
id: Option<i32>,
- cover: Option<String>,
+ #[schema(example = "just ignore this one")]
artist_name: Option<String>,
+ #[schema(example = "1")]
artist_id: Option<i32>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Serialize, Deserialize, ToSchema)]
pub struct AlbumPost {
+ #[schema(example = "album name")]
name: Option<String>,
- cover: Option<String>,
+ #[schema(example = "just ignore this one")]
artist_id: Option<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Serialize, Deserialize, ToSchema)]
pub struct AlbumPut {
id: Option<String>,
+ #[schema(example = "album name")]
name: Option<String>,
- cover: Option<String>,
+ #[schema(example = "just ignore this one")]
artist_id: Option<String>,
}
@@ -30,7 +36,7 @@ impl DatabaseWrapper {
pub async fn select_albums(&self) -> Result<Vec<Album>, sqlx::Error> {
sqlx::query_as!(
Album,
- "SELECT album.name, album.id, album.cover,
+ "SELECT album.name, album.id,
artist.name as artist_name, artist.id as artist_id
FROM album
INNER JOIN artist ON album.artist_id = artist.id
@@ -43,7 +49,7 @@ impl DatabaseWrapper {
pub async fn select_album_by_id(&self, id: &str) -> Result<Option<Album>, sqlx::Error> {
sqlx::query_as!(
Album,
- "SELECT album.name, album.id, album.cover,
+ "SELECT album.name, album.id,
artist.name as artist_name, artist.id as artist_id
FROM album
INNER JOIN artist ON album.artist_id = artist.id
@@ -58,7 +64,7 @@ impl DatabaseWrapper {
let name: String = format!("{}{}{}", "%", name_raw, "%");
sqlx::query_as!(
Album,
- "SELECT album.name, album.id, album.cover,
+ "SELECT album.name, album.id,
artist.name as artist_name, artist.id as artist_id
FROM album
INNER JOIN artist ON album.artist_id = artist.id
@@ -76,7 +82,7 @@ impl DatabaseWrapper {
) -> Result<Vec<Album>, sqlx::Error> {
sqlx::query_as!(
Album,
- "SELECT album.name, album.id, album.cover,
+ "SELECT album.name, album.id,
artist.name as artist_name, artist.id as artist_id
FROM album
INNER JOIN artist ON album.artist_id = artist.id
@@ -103,10 +109,9 @@ impl DatabaseWrapper {
}
sqlx::query!(
- "INSERT INTO album (name, cover, artist_id)
- VALUE (?, ?, ?)",
+ "INSERT INTO album (name, artist_id)
+ VALUE (?, ?)",
data.name,
- data.cover.unwrap_or(String::default()),
data.artist_id,
)
.execute(&self.db_pool)
@@ -125,11 +130,9 @@ impl DatabaseWrapper {
Err(_) => return Err(sqlx::Error::RowNotFound),
};
sqlx::query!(
- "UPDATE album SET name=?, cover=? WHERE id=?",
+ "UPDATE album SET name=? WHERE id=?",
data.name
.unwrap_or(og_album.name.unwrap_or(String::default())),
- data.cover
- .unwrap_or(og_album.cover.unwrap_or(String::default())),
data.id,
)
.execute(&self.db_pool)
diff --git a/src/database/artist.rs b/src/database/artist.rs
index 1d4e1cd..0bdc7c1 100644
--- a/src/database/artist.rs
+++ b/src/database/artist.rs
@@ -5,20 +5,23 @@ use utoipa::ToSchema;
#[derive(Serialize, ToSchema)]
pub struct Artist {
- #[schema(example = "Attempt", required = true)]
+ #[schema(example = "artist name")]
name: Option<String>,
- #[schema(example = 3, required = true)]
+ #[schema(example = "1")]
id: Option<i32>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Serialize, Deserialize, ToSchema)]
pub struct ArtistPost {
+ #[schema(example = "artist name", required = true)]
name: Option<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Serialize, Deserialize, ToSchema)]
pub struct ArtistPut {
+ #[schema(example = "1", required = true)]
id: Option<String>,
+ #[schema(example = "new name", required = true)]
name: Option<String>,
}
diff --git a/src/database/mod.rs b/src/database/mod.rs
index 884a597..045cf23 100644
--- a/src/database/mod.rs
+++ b/src/database/mod.rs
@@ -6,10 +6,12 @@ pub mod user;
use serde::{Deserialize, Serialize};
use sqlx::mysql::{MySqlPool, MySqlPoolOptions};
+use utoipa::ToSchema;
use std::env;
-#[derive(Serialize, Deserialize)]
+#[derive(Serialize, Deserialize, ToSchema)]
pub struct Delete {
+ #[schema(example = "1", required = true)]
pub id: Option<String>,
}
diff --git a/src/database/song.rs b/src/database/song.rs
index 95b05ba..a4c860c 100644
--- a/src/database/song.rs
+++ b/src/database/song.rs
@@ -5,27 +5,41 @@ use utoipa::ToSchema;
#[derive(Serialize, ToSchema)]
pub struct Song {
+ #[schema(example = "song name")]
name: Option<String>,
+ #[schema(example = "1")]
id: Option<i32>,
+ #[schema(example = "song lyrics...")]
lyrics: Option<String>,
+ #[schema(example = "album name")]
album_name: Option<String>,
+ #[schema(example = "1")]
album_id: Option<i32>,
+ #[schema(example = "artist name")]
artist_name: Option<String>,
+ #[schema(example = "1")]
artist_id: Option<i32>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Serialize, Deserialize, ToSchema)]
pub struct SongPost {
+ #[schema(example = "song name")]
name: Option<String>,
+ #[schema(example = "song lyrics...")]
lyrics: Option<String>,
+ #[schema(example = "1")]
album_id: Option<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Serialize, Deserialize, ToSchema)]
pub struct SongPut {
+ #[schema(example = "1")]
id: Option<String>,
+ #[schema(example = "song name")]
name: Option<String>,
+ #[schema(example = "song lyrics...")]
lyrics: Option<String>,
+ #[schema(example = "1")]
album_id: Option<String>,
}
diff --git a/src/database/user.rs b/src/database/user.rs
index ff7b1ab..d3202ce 100644
--- a/src/database/user.rs
+++ b/src/database/user.rs
@@ -1,6 +1,7 @@
use crate::database::DatabaseWrapper;
use serde::{Deserialize, Serialize};
use sqlx::mysql::MySqlQueryResult;
+use utoipa::ToSchema;
#[derive(Deserialize, Serialize)]
pub struct User {
@@ -9,9 +10,11 @@ pub struct User {
password: Option<String>,
}
-#[derive(Deserialize, Serialize)]
+#[derive(Deserialize, Serialize, ToSchema)]
pub struct UserForm {
+ #[schema(example = "username")]
name: Option<String>,
+ #[schema(example = "password")]
password: Option<String>,
}
diff --git a/src/doc.rs b/src/doc.rs
new file mode 100644
index 0000000..2d5e168
--- /dev/null
+++ b/src/doc.rs
@@ -0,0 +1,56 @@
+use utoipa::{
+ openapi::security::{HttpAuthScheme, HttpBuilder, SecurityScheme},
+ Modify, OpenApi,
+};
+use crate::auth;
+
+use crate::api;
+
+pub fn get_openapi() -> utoipa::openapi::OpenApi {
+ /* utoipa setup */
+ #[derive(OpenApi)]
+ #[openapi(
+ paths(
+ api::album::post_album,
+ api::album::put_album,
+ api::album::get_album,
+ api::album::delete_album,
+ api::artist::post_artist,
+ api::artist::put_artist,
+ api::artist::get_artist,
+ api::artist::delete_artist,
+ api::song::post_song,
+ api::song::put_song,
+ api::song::get_song,
+ api::song::delete_song,
+ api::search_results::search_results,
+ auth::register,
+ auth::login,
+ auth::delete_user,
+ ),
+ components(
+ schemas(
+ )
+ ),
+ modifiers(&SecurityAddon)
+ )]
+ struct ApiDoc;
+
+ struct SecurityAddon;
+ impl Modify for SecurityAddon {
+ fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
+ let components = openapi.components.as_mut().unwrap();
+ components.add_security_scheme(
+ "bearer_auth",
+ SecurityScheme::Http(
+ HttpBuilder::new()
+ .scheme(HttpAuthScheme::Bearer)
+ .bearer_format("JWT")
+ .build(),
+ ),
+ );
+ }
+ }
+
+ return ApiDoc::openapi();
+}
diff --git a/src/extractors/auth_token.rs b/src/extractors/auth_token.rs
index c3f781c..9728146 100644
--- a/src/extractors/auth_token.rs
+++ b/src/extractors/auth_token.rs
@@ -26,26 +26,37 @@ impl FromRequest for AuthenticationToken {
None => return ready(Err(ErrorUnauthorized("No authorization token given"))),
};
- let auth_token: String = auth_header.to_str().unwrap_or("").to_string(); // check errors later
- // stop empty and weird (ascii, chinese...) auth_token strings:
- if auth_token.is_empty() {
+ /* Get value as &str */
+ let processed_header: &str = auth_header.to_str().unwrap_or("");
+ if processed_header.is_empty() {
return ready(Err(ErrorUnauthorized("Invalid auth token!")));
}
+
+ /* Accept both bearer tokens and raw tokens */
+ let splitted_header: Vec<&str> = processed_header.split_whitespace().collect();
+ let auth_token: String = match splitted_header[0] {
+ "Bearer" => splitted_header[1].to_string(),
+ _ => processed_header.to_string(),
+ };
+
+ /* Get application secret */
let secret: String = req
.app_data::<web::Data<AppState>>()
.unwrap()
.secret
.to_string();
- // decode token with secret
+ /* Decode token with secret */
let decode: Result<TokenData<Claims>, JwtError> = decode::<Claims>(
&auth_token,
&DecodingKey::from_secret(secret.as_str().as_ref()),
&Validation::new(Algorithm::HS256),
);
+ // for testing purposes
println!("{}", auth_token);
- // return authenticationtoken
+
+ /* Return authenticationtoken */
match decode {
Ok(token) => ready(Ok(AuthenticationToken {
id: token.claims.id,
diff --git a/src/main.rs b/src/main.rs
index 69eed5d..fab6dd5 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,19 +2,15 @@ mod api;
mod auth;
mod database;
mod extractors;
+mod doc;
-use actix_web::{web, App, HttpServer};
+use actix_web::web::Redirect;
+use actix_web::{web, App, HttpServer, Responder};
use dotenv::dotenv;
use std::env;
use std::sync::Arc;
-// use utoipa::{
-// openapi::security::{HttpAuthScheme, HttpBuilder, SecurityScheme},
-// Modify, OpenApi,
-// };
-//
-// use utoipa_swagger_ui::SwaggerUi;
-// use database::artist::Artist;
+use utoipa_swagger_ui::SwaggerUi;
#[derive(Clone)]
struct AppState {
@@ -47,56 +43,25 @@ async fn main() -> std::io::Result<()> {
secret: jwt_secret,
};
- // /* utoipa setup */
- // #[derive(OpenApi)]
- // #[openapi(
- // paths(
- // ),
- // components(
- // schemas(
- // Artist
- // )
- // ),
- // modifiers(&SecurityAddon)
- // )]
- // struct ApiDoc;
-
- // struct SecurityAddon;
- // impl Modify for SecurityAddon {
- // fn modify(&self, openapi : &mut utoipa::openapi::OpenApi) {
- // let components = openapi.components.as_mut().unwrap();
- // components.add_security_scheme(
- // "bearer_auth",
- // SecurityScheme::Http(
- // HttpBuilder::new()
- // .scheme(HttpAuthScheme::Bearer)
- // .bearer_format("JWT")
- // .build()
- // ),
- // );
- // }
- // }
-
- // let openapi = ApiDoc::openapi();
+ /* OpenApi */
+ let openapi = doc::get_openapi();
/* Server setup */
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(app_state.clone()))
- .route("/", web::get().to(root))
- // .service(SwaggerUi::new("/docs/{_:.*}").url(
- // "/docs/openapi.json",
- // openapi.clone(),
- // ))
.service(api::api_scope())
.service(auth::auth_scope())
+ .service(SwaggerUi::new("/doc/{_:.*}").url("/docs/openapi.json", openapi.clone()))
+ .route("/", web::get().to(redirect_to_docs))
+ .route("/doc", web::get().to(redirect_to_docs))
})
.bind(("127.0.0.1", 8000))?
.run()
.await
}
-/* Main page*/
-async fn root() -> String {
- String::from("Server is up and running")
+/* Redirection page */
+async fn redirect_to_docs() -> impl Responder {
+ Redirect::to("/doc/")
}