diff --git a/crates/url_shortner/src/common/mod.rs b/crates/url_shortner/src/common/mod.rs index e044668..b15aec6 100644 --- a/crates/url_shortner/src/common/mod.rs +++ b/crates/url_shortner/src/common/mod.rs @@ -6,4 +6,4 @@ the GNU Affero General Public License along with this program. If not, see . */ pub mod types; -pub mod utils; \ No newline at end of file +pub mod utils; diff --git a/crates/url_shortner/src/common/types.rs b/crates/url_shortner/src/common/types.rs index d15470c..501c366 100644 --- a/crates/url_shortner/src/common/types.rs +++ b/crates/url_shortner/src/common/types.rs @@ -11,4 +11,4 @@ use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize, Clone, Copy, Debug, Eq, PartialEq, PartialOrd)] pub struct TimeStamp(pub DateTime); #[derive(Serialize, Deserialize, Clone, Debug, Eq, Hash, PartialEq)] -pub struct UrlShortCode (pub String); +pub struct UrlShortCode(pub String); diff --git a/crates/url_shortner/src/common/utils.rs b/crates/url_shortner/src/common/utils.rs index 1ea6aff..9d16762 100644 --- a/crates/url_shortner/src/common/utils.rs +++ b/crates/url_shortner/src/common/utils.rs @@ -11,4 +11,4 @@ pub fn from_maybe(maybe_value: Option, default: T) -> T { Some(value) => value, None => default, } -} \ No newline at end of file +} diff --git a/crates/url_shortner/src/domain/action/internal/crud.rs b/crates/url_shortner/src/domain/action/internal/crud.rs index fe4a292..4d6f906 100644 --- a/crates/url_shortner/src/domain/action/internal/crud.rs +++ b/crates/url_shortner/src/domain/action/internal/crud.rs @@ -8,16 +8,16 @@ the GNU Affero General Public License along with this program. If not, see , req: GenerateShortUrlRequest, ) -> Result { - println!("Generate short url req: {:?}", req); - let base_url = Url::parse(&req.base_url).map_err(|error| { - AppError::InvalidRequest(format!("URL parsing failed: {}", error)) - })?; + let base_url = Url::parse(&req.base_url) + .map_err(|error| AppError::InvalidRequest(format!("URL parsing failed: {}", error)))?; println!("Parsed URL: {:?}", base_url); - - let expiry_seconds: Option = req.expiry_in_hours.and_then(|hours| Some(3600 * Into::::into(hours))); + + let expiry_seconds: Option = req + .expiry_in_hours + .map(|hours| 3600 * Into::::into(hours)); let redis_expiry_in_s = from_maybe(expiry_seconds, app_state.redis_expiry); - let UrlShortCode(final_short_code) = - match req.custom_short_code { - Some(custom_short_code) => { - set_custom_code(&base_url, &custom_short_code, redis_expiry_in_s, &app_state.redis_pool).await?; - custom_short_code - }, - None => { - let final_short_code = set_base_url(&base_url, redis_expiry_in_s, &app_state).await?; - final_short_code.ok_or_else(|| { - AppError::InternalError(format!("Failed to generate unique short code after {} retries", app_state.max_retries_for_shortening)) - })? - } - }; + let UrlShortCode(final_short_code) = match req.custom_short_code { + Some(custom_short_code) => { + set_custom_code( + &base_url, + &custom_short_code, + redis_expiry_in_s, + &app_state.redis_pool, + ) + .await?; + custom_short_code + } + None => { + let final_short_code = set_base_url(&base_url, redis_expiry_in_s, &app_state).await?; + final_short_code.ok_or_else(|| { + AppError::InternalError(format!( + "Failed to generate unique short code after {} retries", + app_state.max_retries_for_shortening + )) + })? + } + }; let url_expiry = TimeStamp(Utc::now() + Duration::seconds(redis_expiry_in_s.into())); let short_url = format!("{}/{}", app_state.shortened_base_url, final_short_code); - println!("Generated short url: {} with expiry ts: {:?}", short_url, url_expiry.0); + println!( + "Generated short url: {} with expiry ts: {:?}", + short_url, url_expiry.0 + ); Ok(GenerateShortUrlResponse { short_url, @@ -65,29 +76,27 @@ pub async fn generate_url( }) } -async fn set_custom_code ( +async fn set_custom_code( base_url: &Url, short_code: &UrlShortCode, redis_expiry: u32, persistent_redis: &RedisConnectionPool, ) -> Result<(), AppError> { - let is_key_set = set_base_url_for_short_code( - base_url, - &short_code, - persistent_redis, - redis_expiry - ).await?; + let is_key_set = + set_base_url_for_short_code(base_url, short_code.clone(), persistent_redis, redis_expiry) + .await?; - if ! is_key_set { + if !is_key_set { let UrlShortCode(code) = short_code; - Err(AppError::InvalidRequest(format!("Short code: {code} already exists"))) - } - else { + Err(AppError::InvalidRequest(format!( + "Short code: {code} already exists" + ))) + } else { Ok(()) } } -async fn set_base_url ( +async fn set_base_url( base_url: &Url, redis_expiry: u32, app_state: &Data, @@ -100,19 +109,19 @@ async fn set_base_url ( let short_code = UrlShortCode(Alphanumeric.sample_string(&mut rng, short_code_len)); let is_key_set = set_base_url_for_short_code( base_url, - &short_code, + short_code.clone(), &app_state.redis_pool, redis_expiry, - ).await?; + ) + .await?; if is_key_set { final_short_code = Some(short_code); break; - } - else { + } else { retries_rem -= 1; } - }; + } Ok(final_short_code) } diff --git a/crates/url_shortner/src/domain/action/internal/mod.rs b/crates/url_shortner/src/domain/action/internal/mod.rs index 59956d1..9475ab5 100644 --- a/crates/url_shortner/src/domain/action/internal/mod.rs +++ b/crates/url_shortner/src/domain/action/internal/mod.rs @@ -5,4 +5,4 @@ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -pub mod crud; \ No newline at end of file +pub mod crud; diff --git a/crates/url_shortner/src/domain/action/mod.rs b/crates/url_shortner/src/domain/action/mod.rs index fe49aa4..0188b32 100644 --- a/crates/url_shortner/src/domain/action/mod.rs +++ b/crates/url_shortner/src/domain/action/mod.rs @@ -5,5 +5,5 @@ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -pub mod public_api; pub mod internal; +pub mod public_api; diff --git a/crates/url_shortner/src/domain/action/public_api.rs b/crates/url_shortner/src/domain/action/public_api.rs index 6569bf2..8dfb47b 100644 --- a/crates/url_shortner/src/domain/action/public_api.rs +++ b/crates/url_shortner/src/domain/action/public_api.rs @@ -5,28 +5,31 @@ is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; wit or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -use actix_web::web::{Data, Redirect}; use crate::{ - common::types::UrlShortCode, environment::AppState, - redis::commands::*, - tools::error::AppError + common::types::UrlShortCode, environment::AppState, redis::commands::*, tools::error::AppError, }; +use actix_web::web::{Data, Redirect}; pub async fn redirect_to_url( app_state: Data, url_short_code: UrlShortCode, ) -> Result { - println!("redirect request to url with short code: {:?}", url_short_code); + println!( + "redirect request to url with short code: {:?}", + url_short_code + ); - let mb_base_url = get_base_url_by_short_code(&url_short_code, &app_state.redis_pool).await?; + let mb_base_url = + get_base_url_by_short_code(url_short_code.clone(), &app_state.redis_pool).await?; match mb_base_url { Some(base_url) => { println!("redirecting to: {}", base_url); Ok(Redirect::to(base_url.to_string())) - }, - None => Err(AppError:: - InvalidRequest(format!("No URL found for short code: {}", url_short_code.0)) - ) + } + None => Err(AppError::InvalidRequest(format!( + "No URL found for short code: {}", + url_short_code.0 + ))), } } diff --git a/crates/url_shortner/src/domain/api/internal/crud.rs b/crates/url_shortner/src/domain/api/internal/crud.rs index 079ac78..4ae6d7b 100644 --- a/crates/url_shortner/src/domain/api/internal/crud.rs +++ b/crates/url_shortner/src/domain/api/internal/crud.rs @@ -5,18 +5,16 @@ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ +use crate::{ + domain::{action::internal::crud, types::internal::crud::*}, + environment::AppState, + tools::{auth::authenticate, error::AppError}, +}; use actix_web::{ post, web::{Data, Json}, HttpRequest, }; -use crate::{ - domain::{ - action::internal::crud, - types::internal::crud::* - }, - environment::AppState, tools::{auth::authenticate, error::AppError} -}; #[post("/internal/generateShortUrl")] async fn generate_url( @@ -24,10 +22,9 @@ async fn generate_url( req: HttpRequest, param_obj: Json, ) -> Result, AppError> { - authenticate(&data.internal_auth_api_key, req)?; let req_body = param_obj.into_inner(); - + Ok(Json(crud::generate_url(data, req_body).await?)) -} \ No newline at end of file +} diff --git a/crates/url_shortner/src/domain/api/internal/mod.rs b/crates/url_shortner/src/domain/api/internal/mod.rs index 59956d1..9475ab5 100644 --- a/crates/url_shortner/src/domain/api/internal/mod.rs +++ b/crates/url_shortner/src/domain/api/internal/mod.rs @@ -5,4 +5,4 @@ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -pub mod crud; \ No newline at end of file +pub mod crud; diff --git a/crates/url_shortner/src/domain/api/public_api.rs b/crates/url_shortner/src/domain/api/public_api.rs index a3a5676..2f4809e 100644 --- a/crates/url_shortner/src/domain/api/public_api.rs +++ b/crates/url_shortner/src/domain/api/public_api.rs @@ -11,10 +11,7 @@ use actix_web::{ }; use crate::{ - common::types::*, - domain::action::public_api, - environment::AppState, - tools::error::AppError, + common::types::*, domain::action::public_api, environment::AppState, tools::error::AppError, }; #[get("/{urlShortCode}")] @@ -23,5 +20,5 @@ async fn redirect_to_url( path: Path, ) -> Result { let url_short_code = UrlShortCode(path.into_inner()); - Ok(public_api::redirect_to_url(app_state, url_short_code).await?) -} \ No newline at end of file + public_api::redirect_to_url(app_state, url_short_code).await +} diff --git a/crates/url_shortner/src/domain/types/internal/crud.rs b/crates/url_shortner/src/domain/types/internal/crud.rs index f8c9e64..ccb666e 100644 --- a/crates/url_shortner/src/domain/types/internal/crud.rs +++ b/crates/url_shortner/src/domain/types/internal/crud.rs @@ -5,8 +5,8 @@ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -use serde::{Deserialize, Serialize}; use crate::common::types::*; +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] @@ -22,4 +22,4 @@ pub struct GenerateShortUrlRequest { pub struct GenerateShortUrlResponse { pub short_url: String, pub url_expiry: TimeStamp, -} \ No newline at end of file +} diff --git a/crates/url_shortner/src/domain/types/internal/mod.rs b/crates/url_shortner/src/domain/types/internal/mod.rs index 59956d1..9475ab5 100644 --- a/crates/url_shortner/src/domain/types/internal/mod.rs +++ b/crates/url_shortner/src/domain/types/internal/mod.rs @@ -5,4 +5,4 @@ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -pub mod crud; \ No newline at end of file +pub mod crud; diff --git a/crates/url_shortner/src/domain/types/mod.rs b/crates/url_shortner/src/domain/types/mod.rs index a775856..39c28e6 100644 --- a/crates/url_shortner/src/domain/types/mod.rs +++ b/crates/url_shortner/src/domain/types/mod.rs @@ -5,4 +5,4 @@ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -pub mod internal; \ No newline at end of file +pub mod internal; diff --git a/crates/url_shortner/src/environment.rs b/crates/url_shortner/src/environment.rs index 9c0b232..cfe181e 100644 --- a/crates/url_shortner/src/environment.rs +++ b/crates/url_shortner/src/environment.rs @@ -6,12 +6,11 @@ the GNU Affero General Public License along with this program. If not, see . */ +use crate::tools::logger::LoggerConfig; use serde::Deserialize; use shared::redis::types::{RedisConnectionPool, RedisSettings}; use std::sync::Arc; -use crate::tools::logger::LoggerConfig; - #[derive(Debug, Deserialize, Clone)] pub struct AppConfig { pub port: u16, diff --git a/crates/url_shortner/src/redis/commands.rs b/crates/url_shortner/src/redis/commands.rs index 990d8cc..aac81a4 100644 --- a/crates/url_shortner/src/redis/commands.rs +++ b/crates/url_shortner/src/redis/commands.rs @@ -5,50 +5,41 @@ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -use crate::{ - redis::keys::*, - tools::error::AppError, - common::types::*, -}; -use shared::redis::types::RedisConnectionPool; +use crate::{common::types::*, redis::keys::*, tools::error::AppError}; use reqwest::Url; +use shared::redis::types::RedisConnectionPool; pub async fn set_base_url_for_short_code( base_url: &Url, - url_short_code: &UrlShortCode, + url_short_code: UrlShortCode, persistent_redis_pool: &RedisConnectionPool, redis_expiry: u32, ) -> Result { - - let is_key_set = - persistent_redis_pool - .setnx_with_expiry( - &url_short_code_key(url_short_code), - base_url.as_str(), - redis_expiry.into(), - ) - .await - .map_err(|err| AppError::RedisError(err.to_string()))?; + let is_key_set = persistent_redis_pool + .setnx_with_expiry( + &url_short_code_key(url_short_code), + base_url.as_str(), + redis_expiry.into(), + ) + .await + .map_err(|err| AppError::RedisError(err.to_string()))?; Ok(is_key_set) } pub async fn get_base_url_by_short_code( - url_short_code: &UrlShortCode, + url_short_code: UrlShortCode, persistent_redis_pool: &RedisConnectionPool, ) -> Result, AppError> { - let base_url = - persistent_redis_pool - .get_key_as_str(&url_short_code_key(url_short_code)) - .await - .map_err(|err| AppError::RedisError(err.to_string()))?; + let base_url = persistent_redis_pool + .get_key_as_str(&url_short_code_key(url_short_code)) + .await + .map_err(|err| AppError::RedisError(err.to_string()))?; match base_url { - Some(base_url) => Ok( - Some(Url::parse(&base_url).map_err(|error| { - AppError::InternalError(format!("URL parsing failed: {}", error)) - })?) - ), - None => Ok(None) + Some(base_url) => Ok(Some(Url::parse(&base_url).map_err(|error| { + AppError::InternalError(format!("URL parsing failed: {}", error)) + })?)), + None => Ok(None), } -} \ No newline at end of file +} diff --git a/crates/url_shortner/src/redis/keys.rs b/crates/url_shortner/src/redis/keys.rs index 07fdd9c..6503073 100644 --- a/crates/url_shortner/src/redis/keys.rs +++ b/crates/url_shortner/src/redis/keys.rs @@ -9,6 +9,6 @@ use crate::common::types::*; const REDIS_KEY_PREFIX: &str = "url-shortner"; -pub fn url_short_code_key(UrlShortCode(url_short_code): &UrlShortCode) -> String { +pub fn url_short_code_key(UrlShortCode(url_short_code): UrlShortCode) -> String { format!("{REDIS_KEY_PREFIX}:url_short_code:{url_short_code}") -} \ No newline at end of file +} diff --git a/crates/url_shortner/src/redis/mod.rs b/crates/url_shortner/src/redis/mod.rs index 56cedf9..be66e7a 100644 --- a/crates/url_shortner/src/redis/mod.rs +++ b/crates/url_shortner/src/redis/mod.rs @@ -6,4 +6,4 @@ the GNU Affero General Public License along with this program. If not, see . */ pub mod commands; -pub mod keys; \ No newline at end of file +pub mod keys; diff --git a/crates/url_shortner/src/tools/auth.rs b/crates/url_shortner/src/tools/auth.rs index 5fcafb6..adf4379 100644 --- a/crates/url_shortner/src/tools/auth.rs +++ b/crates/url_shortner/src/tools/auth.rs @@ -10,10 +10,7 @@ use actix_web::HttpRequest; use super::error::AppError; -pub fn authenticate( - internal_api_key: &str, - req: HttpRequest, -) -> Result<(), AppError> { +pub fn authenticate(internal_api_key: &str, req: HttpRequest) -> Result<(), AppError> { let token = req .headers() .get("x-api-key") @@ -22,13 +19,13 @@ pub fn authenticate( .ok_or(AppError::InvalidRequest( "x-api-key (Header) is missing".to_string(), ))?; - + if token != internal_api_key { - Err( - AppError::AuthFailed(format!("Invalid x-api-key: {}", token)) - ) - } - else { + Err(AppError::AuthFailed(format!( + "Invalid x-api-key: {}", + token + ))) + } else { Ok(()) } -} \ No newline at end of file +} diff --git a/flake.nix b/flake.nix index 83a2a33..ab504c1 100644 --- a/flake.nix +++ b/flake.nix @@ -65,6 +65,7 @@ ''; nativeBuildInputs = with pkgs; [ just + cargo ]; };