diff --git a/build.rs b/build.rs index 2bf0bd2..45de5ff 100644 --- a/build.rs +++ b/build.rs @@ -8,7 +8,7 @@ fn main() { .output() .ok() .and_then(|o| String::from_utf8(o.stdout).ok()) - .map(|s| s.trim().to_string()) // Trim newline + .map(|s| s.trim().to_string()) // Trim newline .unwrap_or_else(|| "UNKNOWN".to_string()); // Tell Cargo to set `GIT_SHORT_HASH` for the main compilation diff --git a/src/api/mod.rs b/src/api/mod.rs index 4cad2fa..6d83e02 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -7,5 +7,7 @@ mod v1; mod versions; pub fn web(path: &str) -> Scope { - web::scope(path.trim_end_matches('/')).service(v1::web()).service(versions::get) + web::scope(path.trim_end_matches('/')) + .service(v1::web()) + .service(versions::get) } diff --git a/src/api/v1/auth/login.rs b/src/api/v1/auth/login.rs index 254b913..5229425 100644 --- a/src/api/v1/auth/login.rs +++ b/src/api/v1/auth/login.rs @@ -11,8 +11,8 @@ use crate::{ error::Error, schema::*, utils::{ - PASSWORD_REGEX, generate_access_token, generate_refresh_token, - refresh_token_cookie, user_uuid_from_identifier + PASSWORD_REGEX, generate_access_token, generate_refresh_token, refresh_token_cookie, + user_uuid_from_identifier, }, }; diff --git a/src/api/v1/auth/mod.rs b/src/api/v1/auth/mod.rs index cabe114..d627a59 100644 --- a/src/api/v1/auth/mod.rs +++ b/src/api/v1/auth/mod.rs @@ -11,9 +11,9 @@ use crate::{Conn, error::Error, schema::access_tokens::dsl}; mod login; mod refresh; mod register; +mod reset_password; mod revoke; mod verify_email; -mod reset_password; #[derive(Serialize)] struct Response { diff --git a/src/api/v1/auth/refresh.rs b/src/api/v1/auth/refresh.rs index fceabf5..b64b10e 100644 --- a/src/api/v1/auth/refresh.rs +++ b/src/api/v1/auth/refresh.rs @@ -61,8 +61,6 @@ pub async fn res(req: HttpRequest, data: web::Data) -> Result 1987200 { let new_refresh_token = generate_refresh_token()?; - let new_refresh_token = new_refresh_token; - match update(refresh_tokens::table) .filter(rdsl::token.eq(&refresh_token)) .set(( diff --git a/src/api/v1/auth/reset_password.rs b/src/api/v1/auth/reset_password.rs index 6c6dee7..8240fbd 100644 --- a/src/api/v1/auth/reset_password.rs +++ b/src/api/v1/auth/reset_password.rs @@ -4,9 +4,7 @@ use actix_web::{HttpResponse, get, post, web}; use chrono::{Duration, Utc}; use serde::Deserialize; -use crate::{ - error::Error, structs::PasswordResetToken, Data -}; +use crate::{Data, error::Error, structs::PasswordResetToken}; #[derive(Deserialize)] struct Query { @@ -14,30 +12,31 @@ struct Query { } /// `GET /api/v1/auth/reset-password` Sends password reset email to user -/// +/// /// requires auth? no -/// +/// /// ### Query Parameters /// identifier: Email or username -/// +/// /// ### Responses /// 200 Email sent /// 429 Too Many Requests /// 404 Not found /// 400 Bad request -/// +/// #[get("/reset-password")] -pub async fn get( - query: web::Query, - data: web::Data, -) -> Result { +pub async fn get(query: web::Query, data: web::Data) -> Result { let mut conn = data.pool.get().await?; - if let Ok(password_reset_token) = PasswordResetToken::get_with_identifier(&mut conn, query.identifier.clone()).await { + if let Ok(password_reset_token) = + PasswordResetToken::get_with_identifier(&mut conn, query.identifier.clone()).await + { if Utc::now().signed_duration_since(password_reset_token.created_at) > Duration::hours(1) { password_reset_token.delete(&mut conn).await?; } else { - return Err(Error::TooManyRequests("Please allow 1 hour before sending a new email".to_string())) + return Err(Error::TooManyRequests( + "Please allow 1 hour before sending a new email".to_string(), + )); } } @@ -53,9 +52,9 @@ struct ResetPassword { } /// `POST /api/v1/auth/reset-password` Resets user password -/// +/// /// requires auth? no -/// +/// /// ### Request Example: /// ``` /// json!({ @@ -63,13 +62,13 @@ struct ResetPassword { /// "token": "a3f7e29c1b8d0456e2c9f83b7a1d6e4f5028c3b9a7e1f2d5c6b8a0d3e7f4a2b" /// }); /// ``` -/// +/// /// ### Responses /// 200 Success /// 410 Token Expired /// 404 Not Found /// 400 Bad Request -/// +/// #[post("/reset-password")] pub async fn post( reset_password: web::Json, @@ -77,14 +76,17 @@ pub async fn post( ) -> Result { let mut conn = data.pool.get().await?; - let password_reset_token = PasswordResetToken::get(&mut conn, reset_password.token.clone()).await?; + let password_reset_token = + PasswordResetToken::get(&mut conn, reset_password.token.clone()).await?; if Utc::now().signed_duration_since(password_reset_token.created_at) > Duration::hours(24) { password_reset_token.delete(&mut conn).await?; return Ok(HttpResponse::Gone().finish()); } - password_reset_token.set_password(&data, reset_password.password.clone()).await?; + password_reset_token + .set_password(&data, reset_password.password.clone()) + .await?; Ok(HttpResponse::Ok().finish()) } diff --git a/src/api/v1/auth/verify_email.rs b/src/api/v1/auth/verify_email.rs index d8df8c3..c5c9097 100644 --- a/src/api/v1/auth/verify_email.rs +++ b/src/api/v1/auth/verify_email.rs @@ -5,7 +5,11 @@ use chrono::{Duration, Utc}; use serde::Deserialize; use crate::{ - api::v1::auth::check_access_token, error::Error, structs::{EmailToken, Me}, utils::get_auth_header, Data + Data, + api::v1::auth::check_access_token, + error::Error, + structs::{EmailToken, Me}, + utils::get_auth_header, }; #[derive(Deserialize)] @@ -14,18 +18,18 @@ struct Query { } /// `GET /api/v1/auth/verify-email` Verifies user email address -/// +/// /// requires auth? yes -/// +/// /// ### Query Parameters /// token -/// +/// /// ### Responses /// 200 Success /// 410 Token Expired /// 404 Not Found /// 401 Unauthorized -/// +/// #[get("/verify-email")] pub async fn get( req: HttpRequest, @@ -61,20 +65,17 @@ pub async fn get( } /// `POST /api/v1/auth/verify-email` Sends user verification email -/// +/// /// requires auth? yes -/// +/// /// ### Responses /// 200 Email sent /// 204 Already verified /// 429 Too Many Requests /// 401 Unauthorized -/// +/// #[post("/verify-email")] -pub async fn post( - req: HttpRequest, - data: web::Data, -) -> Result { +pub async fn post(req: HttpRequest, data: web::Data) -> Result { let headers = req.headers(); let auth_header = get_auth_header(headers)?; @@ -86,14 +87,16 @@ pub async fn post( let me = Me::get(&mut conn, uuid).await?; if me.email_verified { - return Ok(HttpResponse::NoContent().finish()) + return Ok(HttpResponse::NoContent().finish()); } if let Ok(email_token) = EmailToken::get(&mut conn, me.uuid).await { if Utc::now().signed_duration_since(email_token.created_at) > Duration::hours(1) { email_token.delete(&mut conn).await?; } else { - return Err(Error::TooManyRequests("Please allow 1 hour before sending a new email".to_string())) + return Err(Error::TooManyRequests( + "Please allow 1 hour before sending a new email".to_string(), + )); } } diff --git a/src/api/v1/channels/mod.rs b/src/api/v1/channels/mod.rs index d3d5d23..999bb23 100644 --- a/src/api/v1/channels/mod.rs +++ b/src/api/v1/channels/mod.rs @@ -1,4 +1,4 @@ -use actix_web::{web, Scope}; +use actix_web::{Scope, web}; mod uuid; diff --git a/src/api/v1/channels/uuid/messages.rs b/src/api/v1/channels/uuid/messages.rs index ea5377e..ddcc800 100644 --- a/src/api/v1/channels/uuid/messages.rs +++ b/src/api/v1/channels/uuid/messages.rs @@ -1,7 +1,11 @@ //! `/api/v1/channels/{uuid}/messages` Endpoints related to channel messages use crate::{ - api::v1::auth::check_access_token, error::Error, structs::{Channel, Member}, utils::{get_auth_header, global_checks}, Data + Data, + api::v1::auth::check_access_token, + error::Error, + structs::{Channel, Member}, + utils::{get_auth_header, global_checks}, }; use ::uuid::Uuid; use actix_web::{HttpRequest, HttpResponse, get, web}; @@ -14,11 +18,11 @@ struct MessageRequest { } /// `GET /api/v1/channels/{uuid}/messages` Returns user with the given UUID -/// +/// /// requires auth: yes -/// +/// /// requires relation: yes -/// +/// /// ### Request Example /// ``` /// json!({ @@ -26,7 +30,7 @@ struct MessageRequest { /// "offset": 0 /// }) /// ``` -/// +/// /// ### Response Example /// ``` /// json!({ @@ -42,7 +46,7 @@ struct MessageRequest { /// } /// }); /// ``` -/// +/// #[get("/{uuid}/messages")] pub async fn get( req: HttpRequest, diff --git a/src/api/v1/channels/uuid/mod.rs b/src/api/v1/channels/uuid/mod.rs index 4e81142..f429159 100644 --- a/src/api/v1/channels/uuid/mod.rs +++ b/src/api/v1/channels/uuid/mod.rs @@ -2,7 +2,11 @@ pub mod messages; pub mod socket; use crate::{ - api::v1::auth::check_access_token, error::Error, structs::{Channel, Member}, utils::{get_auth_header, global_checks}, Data + Data, + api::v1::auth::check_access_token, + error::Error, + structs::{Channel, Member}, + utils::{get_auth_header, global_checks}, }; use actix_web::{HttpRequest, HttpResponse, delete, get, web}; use uuid::Uuid; diff --git a/src/api/v1/channels/uuid/socket.rs b/src/api/v1/channels/uuid/socket.rs index 34e0fdc..556dca3 100644 --- a/src/api/v1/channels/uuid/socket.rs +++ b/src/api/v1/channels/uuid/socket.rs @@ -8,7 +8,10 @@ use futures_util::StreamExt as _; use uuid::Uuid; use crate::{ - api::v1::auth::check_access_token, structs::{Channel, Member}, utils::{get_ws_protocol_header, global_checks}, Data + Data, + api::v1::auth::check_access_token, + structs::{Channel, Member}, + utils::{get_ws_protocol_header, global_checks}, }; #[get("/{uuid}/socket")] @@ -71,9 +74,7 @@ pub async fn ws( Ok(AggregatedMessage::Text(text)) => { let mut conn = data.cache_pool.get_multiplexed_tokio_connection().await?; - let message = channel - .new_message(&data, uuid, text.to_string()) - .await?; + let message = channel.new_message(&data, uuid, text.to_string()).await?; redis::cmd("PUBLISH") .arg(&[channel_uuid.to_string(), serde_json::to_string(&message)?]) diff --git a/src/api/v1/guilds/mod.rs b/src/api/v1/guilds/mod.rs index 4c2dbf1..b7a7a7c 100644 --- a/src/api/v1/guilds/mod.rs +++ b/src/api/v1/guilds/mod.rs @@ -6,7 +6,11 @@ use serde::Deserialize; mod uuid; use crate::{ - api::v1::auth::check_access_token, error::Error, structs::{Guild, StartAmountQuery}, utils::{get_auth_header, global_checks}, Data + Data, + api::v1::auth::check_access_token, + error::Error, + structs::{Guild, StartAmountQuery}, + utils::{get_auth_header, global_checks}, }; #[derive(Deserialize)] @@ -22,16 +26,16 @@ pub fn web() -> Scope { } /// `POST /api/v1/guilds` Creates a new guild -/// +/// /// requires auth: yes -/// +/// /// ### Request Example /// ``` /// json!({ /// "name": "My new server!" /// }); /// ``` -/// +/// /// ### Response Example /// ``` /// json!({ @@ -59,22 +63,17 @@ pub async fn post( let uuid = check_access_token(auth_header, &mut conn).await?; - let guild = Guild::new( - &mut conn, - guild_info.name.clone(), - uuid, - ) - .await?; + let guild = Guild::new(&mut conn, guild_info.name.clone(), uuid).await?; Ok(HttpResponse::Ok().json(guild)) } /// `GET /api/v1/servers` Fetches all guilds -/// +/// /// requires auth: yes -/// +/// /// requires admin: yes -/// +/// /// ### Response Example /// ``` /// json!([ diff --git a/src/api/v1/guilds/uuid/channels.rs b/src/api/v1/guilds/uuid/channels.rs index 2d01e2b..0dd4566 100644 --- a/src/api/v1/guilds/uuid/channels.rs +++ b/src/api/v1/guilds/uuid/channels.rs @@ -1,5 +1,9 @@ use crate::{ - api::v1::auth::check_access_token, error::Error, structs::{Channel, Member}, utils::{get_auth_header, global_checks, order_by_is_above}, Data + Data, + api::v1::auth::check_access_token, + error::Error, + structs::{Channel, Member}, + utils::{get_auth_header, global_checks, order_by_is_above}, }; use ::uuid::Uuid; use actix_web::{HttpRequest, HttpResponse, get, post, web}; diff --git a/src/api/v1/guilds/uuid/icon.rs b/src/api/v1/guilds/uuid/icon.rs index ae71321..f2e15b6 100644 --- a/src/api/v1/guilds/uuid/icon.rs +++ b/src/api/v1/guilds/uuid/icon.rs @@ -5,13 +5,17 @@ use futures_util::StreamExt as _; use uuid::Uuid; use crate::{ - api::v1::auth::check_access_token, error::Error, structs::{Guild, Member}, utils::{get_auth_header, global_checks}, Data + Data, + api::v1::auth::check_access_token, + error::Error, + structs::{Guild, Member}, + utils::{get_auth_header, global_checks}, }; /// `PUT /api/v1/guilds/{uuid}/icon` Icon upload -/// +/// /// requires auth: no -/// +/// /// put request expects a file and nothing else #[put("{uuid}/icon")] pub async fn upload( diff --git a/src/api/v1/guilds/uuid/invites/mod.rs b/src/api/v1/guilds/uuid/invites/mod.rs index e985625..ea04529 100644 --- a/src/api/v1/guilds/uuid/invites/mod.rs +++ b/src/api/v1/guilds/uuid/invites/mod.rs @@ -3,7 +3,11 @@ use serde::Deserialize; use uuid::Uuid; use crate::{ - api::v1::auth::check_access_token, error::Error, structs::{Guild, Member}, utils::{get_auth_header, global_checks}, Data + Data, + api::v1::auth::check_access_token, + error::Error, + structs::{Guild, Member}, + utils::{get_auth_header, global_checks}, }; #[derive(Deserialize)] diff --git a/src/api/v1/guilds/uuid/members.rs b/src/api/v1/guilds/uuid/members.rs index 2cc416a..d7ed0a5 100644 --- a/src/api/v1/guilds/uuid/members.rs +++ b/src/api/v1/guilds/uuid/members.rs @@ -1,5 +1,9 @@ use crate::{ - api::v1::auth::check_access_token, error::Error, structs::Member, utils::{get_auth_header, global_checks}, Data + Data, + api::v1::auth::check_access_token, + error::Error, + structs::Member, + utils::{get_auth_header, global_checks}, }; use ::uuid::Uuid; use actix_web::{HttpRequest, HttpResponse, get, web}; diff --git a/src/api/v1/guilds/uuid/mod.rs b/src/api/v1/guilds/uuid/mod.rs index 69c3f31..c24e957 100644 --- a/src/api/v1/guilds/uuid/mod.rs +++ b/src/api/v1/guilds/uuid/mod.rs @@ -6,11 +6,15 @@ use uuid::Uuid; mod channels; mod icon; mod invites; -mod roles; mod members; +mod roles; use crate::{ - api::v1::auth::check_access_token, error::Error, structs::{Guild, Member}, utils::{get_auth_header, global_checks}, Data + Data, + api::v1::auth::check_access_token, + error::Error, + structs::{Guild, Member}, + utils::{get_auth_header, global_checks}, }; pub fn web() -> Scope { @@ -34,9 +38,9 @@ pub fn web() -> Scope { } /// `GET /api/v1/guilds/{uuid}` DESCRIPTION -/// +/// /// requires auth: yes -/// +/// /// ### Response Example /// ``` /// json!({ @@ -65,7 +69,7 @@ pub fn web() -> Scope { /// ], /// "member_count": 20 /// }); -/// ``` +/// ``` #[get("/{uuid}")] pub async fn get( req: HttpRequest, diff --git a/src/api/v1/guilds/uuid/roles/mod.rs b/src/api/v1/guilds/uuid/roles/mod.rs index 9eb6349..8015384 100644 --- a/src/api/v1/guilds/uuid/roles/mod.rs +++ b/src/api/v1/guilds/uuid/roles/mod.rs @@ -3,7 +3,11 @@ use actix_web::{HttpRequest, HttpResponse, get, post, web}; use serde::Deserialize; use crate::{ - api::v1::auth::check_access_token, error::Error, structs::{Member, Role}, utils::{get_auth_header, global_checks, order_by_is_above}, Data + Data, + api::v1::auth::check_access_token, + error::Error, + structs::{Member, Role}, + utils::{get_auth_header, global_checks, order_by_is_above}, }; pub mod uuid; diff --git a/src/api/v1/guilds/uuid/roles/uuid.rs b/src/api/v1/guilds/uuid/roles/uuid.rs index 9e7853f..0e7f306 100644 --- a/src/api/v1/guilds/uuid/roles/uuid.rs +++ b/src/api/v1/guilds/uuid/roles/uuid.rs @@ -1,5 +1,9 @@ use crate::{ - api::v1::auth::check_access_token, error::Error, structs::{Member, Role}, utils::{get_auth_header, global_checks}, Data + Data, + api::v1::auth::check_access_token, + error::Error, + structs::{Member, Role}, + utils::{get_auth_header, global_checks}, }; use ::uuid::Uuid; use actix_web::{HttpRequest, HttpResponse, get, web}; diff --git a/src/api/v1/invites/id.rs b/src/api/v1/invites/id.rs index 6ddc53f..687b825 100644 --- a/src/api/v1/invites/id.rs +++ b/src/api/v1/invites/id.rs @@ -1,14 +1,15 @@ use actix_web::{HttpRequest, HttpResponse, get, post, web}; use crate::{ - api::v1::auth::check_access_token, error::Error, structs::{Guild, Invite, Member}, utils::{get_auth_header, global_checks}, Data + Data, + api::v1::auth::check_access_token, + error::Error, + structs::{Guild, Invite, Member}, + utils::{get_auth_header, global_checks}, }; #[get("{id}")] -pub async fn get( - path: web::Path<(String,)>, - data: web::Data, -) -> Result { +pub async fn get(path: web::Path<(String,)>, data: web::Data) -> Result { let mut conn = data.pool.get().await?; let invite_id = path.into_inner().0; diff --git a/src/api/v1/me/guilds.rs b/src/api/v1/me/guilds.rs index 516fc35..06c3328 100644 --- a/src/api/v1/me/guilds.rs +++ b/src/api/v1/me/guilds.rs @@ -1,14 +1,19 @@ //! `/api/v1/me/guilds` Contains endpoint related to guild memberships -use actix_web::{get, web, HttpRequest, HttpResponse}; - -use crate::{api::v1::auth::check_access_token, error::Error, structs::Me, utils::{get_auth_header, global_checks}, Data}; +use actix_web::{HttpRequest, HttpResponse, get, web}; +use crate::{ + Data, + api::v1::auth::check_access_token, + error::Error, + structs::Me, + utils::{get_auth_header, global_checks}, +}; /// `GET /api/v1/me/guilds` Returns all guild memberships in a list -/// +/// /// requires auth: yes -/// +/// /// ### Example Response /// ``` /// json!([ @@ -44,4 +49,4 @@ pub async fn get(req: HttpRequest, data: web::Data) -> Result Scope { web::scope("/v1") diff --git a/src/api/v1/stats.rs b/src/api/v1/stats.rs index 30888aa..5877c00 100644 --- a/src/api/v1/stats.rs +++ b/src/api/v1/stats.rs @@ -25,9 +25,9 @@ struct Response { } /// `GET /api/v1/` Returns stats about the server -/// +/// /// requires auth: no -/// +/// /// ### Response Example /// ``` /// json!({ diff --git a/src/api/v1/users/mod.rs b/src/api/v1/users/mod.rs index b3d853b..fd3980d 100644 --- a/src/api/v1/users/mod.rs +++ b/src/api/v1/users/mod.rs @@ -3,23 +3,25 @@ use actix_web::{HttpRequest, HttpResponse, Scope, get, web}; use crate::{ - api::v1::auth::check_access_token, error::Error, structs::{StartAmountQuery, User}, utils::{get_auth_header, global_checks}, Data + Data, + api::v1::auth::check_access_token, + error::Error, + structs::{StartAmountQuery, User}, + utils::{get_auth_header, global_checks}, }; mod uuid; pub fn web() -> Scope { - web::scope("/users") - .service(get) - .service(uuid::get) + web::scope("/users").service(get).service(uuid::get) } /// `GET /api/v1/users` Returns all users on this instance -/// +/// /// requires auth: yes -/// +/// /// requires admin: yes -/// +/// /// ### Response Example /// ``` /// json!([ diff --git a/src/api/v1/users/uuid.rs b/src/api/v1/users/uuid.rs index 337019b..213afe5 100644 --- a/src/api/v1/users/uuid.rs +++ b/src/api/v1/users/uuid.rs @@ -4,15 +4,19 @@ use actix_web::{HttpRequest, HttpResponse, get, web}; use uuid::Uuid; use crate::{ - api::v1::auth::check_access_token, error::Error, structs::User, utils::{get_auth_header, global_checks}, Data + Data, + api::v1::auth::check_access_token, + error::Error, + structs::User, + utils::{get_auth_header, global_checks}, }; /// `GET /api/v1/users/{uuid}` Returns user with the given UUID -/// +/// /// requires auth: yes -/// +/// /// requires relation: yes -/// +/// /// ### Response Example /// ``` /// json!({ diff --git a/src/api/versions.rs b/src/api/versions.rs index 809d6ed..0c3e106 100644 --- a/src/api/versions.rs +++ b/src/api/versions.rs @@ -12,9 +12,9 @@ struct Response { struct UnstableFeatures; /// `GET /api/versions` Returns info about api versions. -/// +/// /// requires auth: no -/// +/// /// ### Response Example /// ``` /// json!({ diff --git a/src/error.rs b/src/error.rs index 984f57e..1b1bfba 100644 --- a/src/error.rs +++ b/src/error.rs @@ -12,6 +12,9 @@ use bunny_api_tokio::error::Error as BunnyError; use deadpool::managed::{BuildError, PoolError}; use diesel::{ConnectionError, result::Error as DieselError}; use diesel_async::pooled_connection::PoolError as DieselPoolError; +use lettre::{ + address::AddressError, error::Error as EmailError, transport::smtp::Error as SmtpError, +}; use log::{debug, error}; use redis::RedisError; use serde::Serialize; @@ -19,7 +22,6 @@ use serde_json::Error as JsonError; use thiserror::Error; use tokio::task::JoinError; use toml::de::Error as TomlError; -use lettre::{error::Error as EmailError, address::AddressError, transport::smtp::Error as SmtpError}; #[derive(Debug, Error)] pub enum Error { diff --git a/src/main.rs b/src/main.rs index 5670e7b..892c79d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,8 +6,8 @@ use diesel_async::pooled_connection::AsyncDieselConnectionManager; use diesel_async::pooled_connection::deadpool::Pool; use error::Error; use simple_logger::SimpleLogger; -use structs::MailClient; use std::time::SystemTime; +use structs::MailClient; mod config; use config::{Config, ConfigBuilder}; use diesel_migrations::{EmbeddedMigrations, MigrationHarness, embed_migrations}; @@ -76,7 +76,12 @@ async fn main() -> Result<(), Error> { let mail = config.mail.clone(); - let mail_client = MailClient::new(mail.smtp.credentials(), mail.smtp.server, mail.address, mail.tls)?; + let mail_client = MailClient::new( + mail.smtp.credentials(), + mail.smtp.server, + mail.address, + mail.tls, + )?; let database_url = config.database.url(); diff --git a/src/structs.rs b/src/structs.rs index bf49a30..e76aa2a 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -1,22 +1,36 @@ use actix_web::web::BytesMut; +use argon2::{ + PasswordHasher, + password_hash::{SaltString, rand_core::OsRng}, +}; use chrono::Utc; use diesel::{ - delete, dsl::now, insert_into, prelude::{Insertable, Queryable}, update, ExpressionMethods, QueryDsl, Selectable, SelectableHelper + ExpressionMethods, QueryDsl, Selectable, SelectableHelper, delete, + dsl::now, + insert_into, + prelude::{Insertable, Queryable}, + update, }; use diesel_async::{RunQueryDsl, pooled_connection::AsyncDieselConnectionManager}; -use lettre::{message::{Mailbox, MessageBuilder as EmailBuilder, MultiPart}, transport::smtp::authentication::Credentials, AsyncSmtpTransport, AsyncTransport, Message as Email, Tokio1Executor}; +use lettre::{ + AsyncSmtpTransport, AsyncTransport, Message as Email, Tokio1Executor, + message::{Mailbox, MessageBuilder as EmailBuilder, MultiPart}, + transport::smtp::authentication::Credentials, +}; use log::debug; use serde::{Deserialize, Serialize}; use tokio::task; use url::Url; use uuid::Uuid; -use argon2::{ - PasswordHasher, - password_hash::{SaltString, rand_core::OsRng}, -}; use crate::{ - error::Error, schema::*, utils::{generate_refresh_token, global_checks, image_check, order_by_is_above, user_uuid_from_identifier, EMAIL_REGEX, PASSWORD_REGEX, USERNAME_REGEX}, Conn, Data + Conn, Data, + error::Error, + schema::*, + utils::{ + EMAIL_REGEX, PASSWORD_REGEX, USERNAME_REGEX, generate_refresh_token, global_checks, + image_check, order_by_is_above, user_uuid_from_identifier, + }, }; pub trait HasUuid { @@ -61,7 +75,12 @@ pub struct MailClient { } impl MailClient { - pub fn new>(creds: Credentials, smtp_server: String, mbox: String, tls: T) -> Result { + pub fn new>( + creds: Credentials, + smtp_server: String, + mbox: String, + tls: T, + ) -> Result { Ok(Self { creds, smtp_server, @@ -71,15 +90,16 @@ impl MailClient { } pub fn message_builder(&self) -> EmailBuilder { - Email::builder() - .from(self.mbox.clone()) + Email::builder().from(self.mbox.clone()) } pub async fn send_mail(&self, email: Email) -> Result<(), Error> { let mailer: AsyncSmtpTransport = match self.tls { - MailTls::StartTls => AsyncSmtpTransport::::starttls_relay(&self.smtp_server)? - .credentials(self.creds.clone()) - .build(), + MailTls::StartTls => { + AsyncSmtpTransport::::starttls_relay(&self.smtp_server)? + .credentials(self.creds.clone()) + .build() + } MailTls::Tls => AsyncSmtpTransport::::relay(&self.smtp_server)? .credentials(self.creds.clone()) .build(), @@ -256,7 +276,11 @@ impl Channel { data.set_cache_key(channel_uuid.to_string(), channel.clone(), 1800) .await?; - if let Ok(_) = data.get_cache_key(format!("{}_channels", guild_uuid)).await { + if data + .get_cache_key(format!("{}_channels", guild_uuid)) + .await + .is_ok() + { data.del_cache_key(format!("{}_channels", guild_uuid)) .await?; } @@ -273,7 +297,7 @@ impl Channel { .execute(&mut conn) .await?; - if let Ok(_) = data.get_cache_key(self.uuid.to_string()).await { + if data.get_cache_key(self.uuid.to_string()).await.is_ok() { data.del_cache_key(self.uuid.to_string()).await?; } @@ -300,9 +324,7 @@ impl Channel { .await, )?; - let message_futures = messages.iter().map(async move |b| { - b.build(data).await - }); + let message_futures = messages.iter().map(async move |b| b.build(data).await); futures::future::try_join_all(message_futures).await } @@ -708,7 +730,11 @@ impl Member { Ok(count) } - pub async fn check_membership(conn: &mut Conn, user_uuid: Uuid, guild_uuid: Uuid) -> Result<(), Error> { + pub async fn check_membership( + conn: &mut Conn, + user_uuid: Uuid, + guild_uuid: Uuid, + ) -> Result<(), Error> { use guild_members::dsl; dsl::guild_members .filter(dsl::user_uuid.eq(user_uuid)) @@ -720,11 +746,7 @@ impl Member { Ok(()) } - pub async fn fetch_one( - data: &Data, - user_uuid: Uuid, - guild_uuid: Uuid, - ) -> Result { + pub async fn fetch_one(data: &Data, user_uuid: Uuid, guild_uuid: Uuid) -> Result { let mut conn = data.pool.get().await?; use guild_members::dsl; @@ -747,12 +769,12 @@ impl Member { .filter(dsl::guild_uuid.eq(guild_uuid)) .select(MemberBuilder::as_select()) .load(&mut conn) - .await + .await, )?; - let member_futures = member_builders.iter().map(async move |m| { - m.build(data).await - }); + let member_futures = member_builders + .iter() + .map(async move |m| m.build(data).await); futures::future::try_join_all(member_futures).await } @@ -921,15 +943,16 @@ impl Me { let user = User::fetch_one(data, self.uuid).await?; - let memberships = member_builders.iter().map(|m| { - Member { + let memberships = member_builders + .iter() + .map(|m| Member { uuid: m.uuid, nickname: m.nickname.clone(), user_uuid: m.user_uuid, guild_uuid: m.guild_uuid, user: user.clone(), - } - }).collect(); + }) + .collect(); Ok(memberships) } @@ -1070,6 +1093,7 @@ impl EmailToken { Ok(email_token) } + #[allow(clippy::new_ret_no_self)] pub async fn new(data: &Data, me: Me) -> Result<(), Error> { let token = generate_refresh_token()?; @@ -1077,7 +1101,11 @@ impl EmailToken { use email_tokens::dsl; insert_into(email_tokens::table) - .values((dsl::user_uuid.eq(me.uuid), dsl::token.eq(&token), dsl::created_at.eq(now))) + .values(( + dsl::user_uuid.eq(me.uuid), + dsl::token.eq(&token), + dsl::created_at.eq(now), + )) .execute(&mut conn) .await?; @@ -1095,10 +1123,7 @@ impl EmailToken { format!(r#"

Verify your {} Account

Hello, {}!

Thanks for creating a new account on Gorb.

The final step to create your account is to verify your email address by clicking the button below, within 24 hours.

VERIFY ACCOUNT

If you didn't ask to verify this address, you can safely ignore this email.

"#, data.config.instance.name, me.username, verify_endpoint) ))?; - data - .mail_client - .send_mail(email) - .await?; + data.mail_client.send_mail(email).await?; Ok(()) } @@ -1136,7 +1161,10 @@ impl PasswordResetToken { Ok(password_reset_token) } - pub async fn get_with_identifier(conn: &mut Conn, identifier: String) -> Result { + pub async fn get_with_identifier( + conn: &mut Conn, + identifier: String, + ) -> Result { let user_uuid = user_uuid_from_identifier(conn, &identifier).await?; use password_reset_tokens::dsl; @@ -1149,6 +1177,7 @@ impl PasswordResetToken { Ok(password_reset_token) } + #[allow(clippy::new_ret_no_self)] pub async fn new(data: &Data, identifier: String) -> Result<(), Error> { let token = generate_refresh_token()?; @@ -1156,7 +1185,7 @@ impl PasswordResetToken { let user_uuid = user_uuid_from_identifier(&mut conn, &identifier).await?; - global_checks(&data, user_uuid).await?; + global_checks(data, user_uuid).await?; use users::dsl as udsl; let (username, email_address): (String, String) = udsl::users @@ -1167,7 +1196,11 @@ impl PasswordResetToken { use password_reset_tokens::dsl; insert_into(password_reset_tokens::table) - .values((dsl::user_uuid.eq(user_uuid), dsl::token.eq(&token), dsl::created_at.eq(now))) + .values(( + dsl::user_uuid.eq(user_uuid), + dsl::token.eq(&token), + dsl::created_at.eq(now), + )) .execute(&mut conn) .await?; @@ -1185,17 +1218,16 @@ impl PasswordResetToken { format!(r#"

{} Password Reset

Hello, {}!

Someone requested a password reset for your Gorb account.

Click the button below within 24 hours to reset your password.

RESET PASSWORD

If you didn't request a password reset, don't worry, your account is safe and you can safely ignore this email.

"#, data.config.instance.name, username, reset_endpoint) ))?; - data - .mail_client - .send_mail(email) - .await?; + data.mail_client.send_mail(email).await?; Ok(()) } pub async fn set_password(&self, data: &Data, password: String) -> Result<(), Error> { if !PASSWORD_REGEX.is_match(&password) { - return Err(Error::BadRequest("Please provide a valid password".to_string())) + return Err(Error::BadRequest( + "Please provide a valid password".to_string(), + )); } let salt = SaltString::generate(&mut OsRng); @@ -1204,7 +1236,7 @@ impl PasswordResetToken { .argon2 .hash_password(password.as_bytes(), &salt) .map_err(|e| Error::PasswordHashError(e.to_string()))?; - + let mut conn = data.pool.get().await?; use users::dsl; @@ -1232,10 +1264,7 @@ impl PasswordResetToken { format!(r#"

{} Password Reset Confirmation

Hello, {}!

Your password has been successfully reset for your Gorb account.

If you did not initiate this change, please click the button below to reset your password immediately.

RESET PASSWORD
"#, data.config.instance.name, username, login_page) ))?; - data - .mail_client - .send_mail(email) - .await?; + data.mail_client.send_mail(email).await?; self.delete(&mut conn).await } @@ -1251,4 +1280,3 @@ impl PasswordResetToken { Ok(()) } } - diff --git a/src/utils.rs b/src/utils.rs index d5d4480..5ef2187 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -16,7 +16,10 @@ use serde::Serialize; use uuid::Uuid; use crate::{ - error::Error, schema::users, structs::{HasIsAbove, HasUuid}, Conn, Data + Conn, Data, + error::Error, + schema::users, + structs::{HasIsAbove, HasUuid}, }; pub static EMAIL_REGEX: LazyLock = LazyLock::new(|| { @@ -137,27 +140,32 @@ pub fn image_check(icon: BytesMut) -> Result { )) } -pub async fn user_uuid_from_identifier(conn: &mut Conn, identifier: &String) -> Result { +pub async fn user_uuid_from_identifier( + conn: &mut Conn, + identifier: &String, +) -> Result { if EMAIL_REGEX.is_match(identifier) { - use users::dsl; - let user_uuid = dsl::users - .filter(dsl::email.eq(identifier)) - .select(dsl::uuid) - .get_result(conn) - .await?; + use users::dsl; + let user_uuid = dsl::users + .filter(dsl::email.eq(identifier)) + .select(dsl::uuid) + .get_result(conn) + .await?; - Ok(user_uuid) + Ok(user_uuid) } else if USERNAME_REGEX.is_match(identifier) { - use users::dsl; - let user_uuid = dsl::users - .filter(dsl::username.eq(identifier)) - .select(dsl::uuid) - .get_result(conn) - .await?; + use users::dsl; + let user_uuid = dsl::users + .filter(dsl::username.eq(identifier)) + .select(dsl::uuid) + .get_result(conn) + .await?; - Ok(user_uuid) + Ok(user_uuid) } else { - Err(Error::BadRequest("Please provide a valid username or email".to_string())) + Err(Error::BadRequest( + "Please provide a valid username or email".to_string(), + )) } } @@ -173,11 +181,12 @@ pub async fn global_checks(data: &Data, user_uuid: Uuid) -> Result<(), Error> { .await?; if !email_verified { - return Err(Error::Forbidden("server requires email verification".to_string())) + return Err(Error::Forbidden( + "server requires email verification".to_string(), + )); } } - Ok(()) }