refactor: rewrite entire codebase in axum instead of actix
Replaces actix with axum for web, allows us to use socket.io and gives us access to the tower ecosystem of middleware breaks compatibility with our current websocket implementation, needs to be reimplemented for socket.io
This commit is contained in:
parent
3647086adb
commit
324137ce8b
47 changed files with 1381 additions and 1129 deletions
|
@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
|
|||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
Conn, Data,
|
||||
AppState, Conn,
|
||||
error::Error,
|
||||
schema::{channel_permissions, channels, messages},
|
||||
utils::{CHANNEL_REGEX, order_by_is_above},
|
||||
|
@ -105,12 +105,12 @@ impl Channel {
|
|||
futures::future::try_join_all(channel_futures).await
|
||||
}
|
||||
|
||||
pub async fn fetch_one(data: &Data, channel_uuid: Uuid) -> Result<Self, Error> {
|
||||
if let Ok(cache_hit) = data.get_cache_key(channel_uuid.to_string()).await {
|
||||
pub async fn fetch_one(app_state: &AppState, channel_uuid: Uuid) -> Result<Self, Error> {
|
||||
if let Ok(cache_hit) = app_state.get_cache_key(channel_uuid.to_string()).await {
|
||||
return Ok(serde_json::from_str(&cache_hit)?);
|
||||
}
|
||||
|
||||
let mut conn = data.pool.get().await?;
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
use channels::dsl;
|
||||
let channel_builder: ChannelBuilder = dsl::channels
|
||||
|
@ -121,14 +121,15 @@ impl Channel {
|
|||
|
||||
let channel = channel_builder.build(&mut conn).await?;
|
||||
|
||||
data.set_cache_key(channel_uuid.to_string(), channel.clone(), 60)
|
||||
app_state
|
||||
.set_cache_key(channel_uuid.to_string(), channel.clone(), 60)
|
||||
.await?;
|
||||
|
||||
Ok(channel)
|
||||
}
|
||||
|
||||
pub async fn new(
|
||||
data: actix_web::web::Data<Data>,
|
||||
app_state: &AppState,
|
||||
guild_uuid: Uuid,
|
||||
name: String,
|
||||
description: Option<String>,
|
||||
|
@ -137,11 +138,11 @@ impl Channel {
|
|||
return Err(Error::BadRequest("Channel name is invalid".to_string()));
|
||||
}
|
||||
|
||||
let mut conn = data.pool.get().await?;
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
let channel_uuid = Uuid::now_v7();
|
||||
|
||||
let channels = Self::fetch_all(&data.pool, guild_uuid).await?;
|
||||
let channels = Self::fetch_all(&app_state.pool, guild_uuid).await?;
|
||||
|
||||
let channels_ordered = order_by_is_above(channels).await?;
|
||||
|
||||
|
@ -179,22 +180,25 @@ impl Channel {
|
|||
permissions: vec![],
|
||||
};
|
||||
|
||||
data.set_cache_key(channel_uuid.to_string(), channel.clone(), 1800)
|
||||
app_state
|
||||
.set_cache_key(channel_uuid.to_string(), channel.clone(), 1800)
|
||||
.await?;
|
||||
|
||||
if data
|
||||
if app_state
|
||||
.get_cache_key(format!("{guild_uuid}_channels"))
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
data.del_cache_key(format!("{guild_uuid}_channels")).await?;
|
||||
app_state
|
||||
.del_cache_key(format!("{guild_uuid}_channels"))
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(channel)
|
||||
}
|
||||
|
||||
pub async fn delete(self, data: &Data) -> Result<(), Error> {
|
||||
let mut conn = data.pool.get().await?;
|
||||
pub async fn delete(self, app_state: &AppState) -> Result<(), Error> {
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
use channels::dsl;
|
||||
match update(channels::table)
|
||||
|
@ -224,16 +228,17 @@ impl Channel {
|
|||
Err(e) => Err(e),
|
||||
}?;
|
||||
|
||||
if data.get_cache_key(self.uuid.to_string()).await.is_ok() {
|
||||
data.del_cache_key(self.uuid.to_string()).await?;
|
||||
if app_state.get_cache_key(self.uuid.to_string()).await.is_ok() {
|
||||
app_state.del_cache_key(self.uuid.to_string()).await?;
|
||||
}
|
||||
|
||||
if data
|
||||
if app_state
|
||||
.get_cache_key(format!("{}_channels", self.guild_uuid))
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
data.del_cache_key(format!("{}_channels", self.guild_uuid))
|
||||
app_state
|
||||
.del_cache_key(format!("{}_channels", self.guild_uuid))
|
||||
.await?;
|
||||
}
|
||||
|
||||
|
@ -242,11 +247,11 @@ impl Channel {
|
|||
|
||||
pub async fn fetch_messages(
|
||||
&self,
|
||||
data: &Data,
|
||||
app_state: &AppState,
|
||||
amount: i64,
|
||||
offset: i64,
|
||||
) -> Result<Vec<Message>, Error> {
|
||||
let mut conn = data.pool.get().await?;
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
use messages::dsl;
|
||||
let messages: Vec<MessageBuilder> = load_or_empty(
|
||||
|
@ -260,14 +265,14 @@ 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(app_state).await);
|
||||
|
||||
futures::future::try_join_all(message_futures).await
|
||||
}
|
||||
|
||||
pub async fn new_message(
|
||||
&self,
|
||||
data: &Data,
|
||||
app_state: &AppState,
|
||||
user_uuid: Uuid,
|
||||
message: String,
|
||||
reply_to: Option<Uuid>,
|
||||
|
@ -282,22 +287,22 @@ impl Channel {
|
|||
reply_to,
|
||||
};
|
||||
|
||||
let mut conn = data.pool.get().await?;
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
insert_into(messages::table)
|
||||
.values(message.clone())
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
message.build(data).await
|
||||
message.build(app_state).await
|
||||
}
|
||||
|
||||
pub async fn set_name(&mut self, data: &Data, new_name: String) -> Result<(), Error> {
|
||||
pub async fn set_name(&mut self, app_state: &AppState, new_name: String) -> Result<(), Error> {
|
||||
if !CHANNEL_REGEX.is_match(&new_name) {
|
||||
return Err(Error::BadRequest("Channel name is invalid".to_string()));
|
||||
}
|
||||
|
||||
let mut conn = data.pool.get().await?;
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
use channels::dsl;
|
||||
update(channels::table)
|
||||
|
@ -313,10 +318,10 @@ impl Channel {
|
|||
|
||||
pub async fn set_description(
|
||||
&mut self,
|
||||
data: &Data,
|
||||
app_state: &AppState,
|
||||
new_description: String,
|
||||
) -> Result<(), Error> {
|
||||
let mut conn = data.pool.get().await?;
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
use channels::dsl;
|
||||
update(channels::table)
|
||||
|
@ -330,8 +335,12 @@ impl Channel {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn move_channel(&mut self, data: &Data, new_is_above: Uuid) -> Result<(), Error> {
|
||||
let mut conn = data.pool.get().await?;
|
||||
pub async fn move_channel(
|
||||
&mut self,
|
||||
app_state: &AppState,
|
||||
new_is_above: Uuid,
|
||||
) -> Result<(), Error> {
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
use channels::dsl;
|
||||
let old_above_uuid: Option<Uuid> = match dsl::channels
|
||||
|
|
|
@ -3,7 +3,7 @@ use lettre::message::MultiPart;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{Data, error::Error, utils::generate_token};
|
||||
use crate::{AppState, error::Error, utils::generate_token};
|
||||
|
||||
use super::Me;
|
||||
|
||||
|
@ -15,9 +15,9 @@ pub struct EmailToken {
|
|||
}
|
||||
|
||||
impl EmailToken {
|
||||
pub async fn get(data: &Data, user_uuid: Uuid) -> Result<EmailToken, Error> {
|
||||
pub async fn get(app_state: &AppState, user_uuid: Uuid) -> Result<EmailToken, Error> {
|
||||
let email_token = serde_json::from_str(
|
||||
&data
|
||||
&app_state
|
||||
.get_cache_key(format!("{user_uuid}_email_verify"))
|
||||
.await?,
|
||||
)?;
|
||||
|
@ -26,7 +26,7 @@ impl EmailToken {
|
|||
}
|
||||
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub async fn new(data: &Data, me: Me) -> Result<(), Error> {
|
||||
pub async fn new(app_state: &AppState, me: Me) -> Result<(), Error> {
|
||||
let token = generate_token::<32>()?;
|
||||
|
||||
let email_token = EmailToken {
|
||||
|
@ -36,30 +36,32 @@ impl EmailToken {
|
|||
created_at: Utc::now(),
|
||||
};
|
||||
|
||||
data.set_cache_key(format!("{}_email_verify", me.uuid), email_token, 86400)
|
||||
app_state
|
||||
.set_cache_key(format!("{}_email_verify", me.uuid), email_token, 86400)
|
||||
.await?;
|
||||
|
||||
let mut verify_endpoint = data.config.web.frontend_url.join("verify-email")?;
|
||||
let mut verify_endpoint = app_state.config.web.frontend_url.join("verify-email")?;
|
||||
|
||||
verify_endpoint.set_query(Some(&format!("token={token}")));
|
||||
|
||||
let email = data
|
||||
let email = app_state
|
||||
.mail_client
|
||||
.message_builder()
|
||||
.to(me.email.parse()?)
|
||||
.subject(format!("{} E-mail Verification", data.config.instance.name))
|
||||
.subject(format!("{} E-mail Verification", app_state.config.instance.name))
|
||||
.multipart(MultiPart::alternative_plain_html(
|
||||
format!("Verify your {} account\n\nHello, {}!\nThanks for creating a new account on Gorb.\nThe final step to create your account is to verify your email address by visiting the page, within 24 hours.\n\n{}\n\nIf you didn't ask to verify this address, you can safely ignore this email\n\nThanks, The gorb team.", data.config.instance.name, me.username, verify_endpoint),
|
||||
format!(r#"<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><style>:root{{--header-text-colour: #ffffff;--footer-text-colour: #7f7f7f;--button-text-colour: #170e08;--text-colour: #170e08;--background-colour: #fbf6f2;--primary-colour: #df5f0b;--secondary-colour: #e8ac84;--accent-colour: #e68b4e;}}@media (prefers-color-scheme: dark){{:root{{--header-text-colour: #ffffff;--footer-text-colour: #585858;--button-text-colour: #ffffff;--text-colour: #f7eee8;--background-colour: #0c0704;--primary-colour: #f4741f;--secondary-colour: #7c4018;--accent-colour: #b35719;}}}}@media (max-width: 600px){{.container{{width: 100%;}}}}body{{font-family: Arial, sans-serif;align-content: center;text-align: center;margin: 0;padding: 0;background-color: var(--background-colour);color: var(--text-colour);width: 100%;max-width: 600px;margin: 0 auto;border-radius: 5px;}}.header{{background-color: var(--primary-colour);color: var(--header-text-colour);padding: 20px;}}.verify-button{{background-color: var(--accent-colour);color: var(--button-text-colour);padding: 12px 30px;margin: 16px;font-size: 20px;transition: background-color 0.3s;cursor: pointer;border: none;border-radius: 14px;text-decoration: none;display: inline-block;}}.verify-button:hover{{background-color: var(--secondary-colour);}}.content{{padding: 20px 30px;}}.footer{{padding: 10px;font-size: 12px;color: var(--footer-text-colour);}}</style></head><body><div class="container"><div class="header"><h1>Verify your {} Account</h1></div><div class="content"><h2>Hello, {}!</h2><p>Thanks for creating a new account on Gorb.</p><p>The final step to create your account is to verify your email address by clicking the button below, within 24 hours.</p><a href="{}" class="verify-button">VERIFY ACCOUNT</a><p>If you didn't ask to verify this address, you can safely ignore this email.</p><div class="footer"><p>Thanks<br>The gorb team.</p></div></div></div></body></html>"#, data.config.instance.name, me.username, verify_endpoint)
|
||||
format!("Verify your {} account\n\nHello, {}!\nThanks for creating a new account on Gorb.\nThe final step to create your account is to verify your email address by visiting the page, within 24 hours.\n\n{}\n\nIf you didn't ask to verify this address, you can safely ignore this email\n\nThanks, The gorb team.", app_state.config.instance.name, me.username, verify_endpoint),
|
||||
format!(r#"<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><style>:root{{--header-text-colour: #ffffff;--footer-text-colour: #7f7f7f;--button-text-colour: #170e08;--text-colour: #170e08;--background-colour: #fbf6f2;--primary-colour: #df5f0b;--secondary-colour: #e8ac84;--accent-colour: #e68b4e;}}@media (prefers-color-scheme: dark){{:root{{--header-text-colour: #ffffff;--footer-text-colour: #585858;--button-text-colour: #ffffff;--text-colour: #f7eee8;--background-colour: #0c0704;--primary-colour: #f4741f;--secondary-colour: #7c4018;--accent-colour: #b35719;}}}}@media (max-width: 600px){{.container{{width: 100%;}}}}body{{font-family: Arial, sans-serif;align-content: center;text-align: center;margin: 0;padding: 0;background-color: var(--background-colour);color: var(--text-colour);width: 100%;max-width: 600px;margin: 0 auto;border-radius: 5px;}}.header{{background-color: var(--primary-colour);color: var(--header-text-colour);padding: 20px;}}.verify-button{{background-color: var(--accent-colour);color: var(--button-text-colour);padding: 12px 30px;margin: 16px;font-size: 20px;transition: background-color 0.3s;cursor: pointer;border: none;border-radius: 14px;text-decoration: none;display: inline-block;}}.verify-button:hover{{background-color: var(--secondary-colour);}}.content{{padding: 20px 30px;}}.footer{{padding: 10px;font-size: 12px;color: var(--footer-text-colour);}}</style></head><body><div class="container"><div class="header"><h1>Verify your {} Account</h1></div><div class="content"><h2>Hello, {}!</h2><p>Thanks for creating a new account on Gorb.</p><p>The final step to create your account is to verify your email address by clicking the button below, within 24 hours.</p><a href="{}" class="verify-button">VERIFY ACCOUNT</a><p>If you didn't ask to verify this address, you can safely ignore this email.</p><div class="footer"><p>Thanks<br>The gorb team.</p></div></div></div></body></html>"#, app_state.config.instance.name, me.username, verify_endpoint)
|
||||
))?;
|
||||
|
||||
data.mail_client.send_mail(email).await?;
|
||||
app_state.mail_client.send_mail(email).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete(&self, data: &Data) -> Result<(), Error> {
|
||||
data.del_cache_key(format!("{}_email_verify", self.user_uuid))
|
||||
pub async fn delete(&self, app_state: &AppState) -> Result<(), Error> {
|
||||
app_state
|
||||
.del_cache_key(format!("{}_email_verify", self.user_uuid))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use actix_web::web::BytesMut;
|
||||
use axum::body::Bytes;
|
||||
use diesel::{
|
||||
ExpressionMethods, Insertable, QueryDsl, Queryable, Selectable, SelectableHelper, insert_into,
|
||||
update,
|
||||
|
@ -191,7 +191,7 @@ impl Guild {
|
|||
bunny_storage: &bunny_api_tokio::EdgeStorageClient,
|
||||
conn: &mut Conn,
|
||||
cdn_url: Url,
|
||||
icon: BytesMut,
|
||||
icon: Bytes,
|
||||
) -> Result<(), Error> {
|
||||
let icon_clone = icon.clone();
|
||||
let image_type = task::spawn_blocking(move || image_check(icon_clone)).await??;
|
||||
|
@ -204,7 +204,7 @@ impl Guild {
|
|||
|
||||
let path = format!("icons/{}/{}.{}", self.uuid, Uuid::now_v7(), image_type);
|
||||
|
||||
bunny_storage.upload(path.clone(), icon.into()).await?;
|
||||
bunny_storage.upload(path.clone(), icon).await?;
|
||||
|
||||
let icon_url = cdn_url.join(&path)?;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use actix_web::web::BytesMut;
|
||||
use axum::body::Bytes;
|
||||
use diesel::{
|
||||
ExpressionMethods, QueryDsl, Queryable, Selectable, SelectableHelper, delete, insert_into,
|
||||
update,
|
||||
|
@ -10,7 +10,7 @@ use url::Url;
|
|||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
Conn, Data,
|
||||
AppState, Conn,
|
||||
error::Error,
|
||||
objects::{Friend, FriendRequest, User},
|
||||
schema::{friend_requests, friends, guild_members, guilds, users},
|
||||
|
@ -75,28 +75,26 @@ impl Me {
|
|||
|
||||
pub async fn set_avatar(
|
||||
&mut self,
|
||||
data: &Data,
|
||||
app_state: &AppState,
|
||||
cdn_url: Url,
|
||||
avatar: BytesMut,
|
||||
avatar: Bytes,
|
||||
) -> Result<(), Error> {
|
||||
let avatar_clone = avatar.clone();
|
||||
let image_type = task::spawn_blocking(move || image_check(avatar_clone)).await??;
|
||||
|
||||
let mut conn = data.pool.get().await?;
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
if let Some(avatar) = &self.avatar {
|
||||
let avatar_url: Url = avatar.parse()?;
|
||||
|
||||
let relative_url = avatar_url.path().trim_start_matches('/');
|
||||
|
||||
data.bunny_storage.delete(relative_url).await?;
|
||||
app_state.bunny_storage.delete(relative_url).await?;
|
||||
}
|
||||
|
||||
let path = format!("avatar/{}/{}.{}", self.uuid, Uuid::now_v7(), image_type);
|
||||
|
||||
data.bunny_storage
|
||||
.upload(path.clone(), avatar.into())
|
||||
.await?;
|
||||
app_state.bunny_storage.upload(path.clone(), avatar).await?;
|
||||
|
||||
let avatar_url = cdn_url.join(&path)?;
|
||||
|
||||
|
@ -107,8 +105,8 @@ impl Me {
|
|||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
if data.get_cache_key(self.uuid.to_string()).await.is_ok() {
|
||||
data.del_cache_key(self.uuid.to_string()).await?
|
||||
if app_state.get_cache_key(self.uuid.to_string()).await.is_ok() {
|
||||
app_state.del_cache_key(self.uuid.to_string()).await?
|
||||
}
|
||||
|
||||
self.avatar = Some(avatar_url.to_string());
|
||||
|
@ -127,7 +125,11 @@ impl Me {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn set_username(&mut self, data: &Data, new_username: String) -> Result<(), Error> {
|
||||
pub async fn set_username(
|
||||
&mut self,
|
||||
app_state: &AppState,
|
||||
new_username: String,
|
||||
) -> Result<(), Error> {
|
||||
if !USERNAME_REGEX.is_match(&new_username)
|
||||
|| new_username.len() < 3
|
||||
|| new_username.len() > 32
|
||||
|
@ -135,7 +137,7 @@ impl Me {
|
|||
return Err(Error::BadRequest("Invalid username".to_string()));
|
||||
}
|
||||
|
||||
let mut conn = data.pool.get().await?;
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
use users::dsl;
|
||||
update(users::table)
|
||||
|
@ -144,8 +146,8 @@ impl Me {
|
|||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
if data.get_cache_key(self.uuid.to_string()).await.is_ok() {
|
||||
data.del_cache_key(self.uuid.to_string()).await?
|
||||
if app_state.get_cache_key(self.uuid.to_string()).await.is_ok() {
|
||||
app_state.del_cache_key(self.uuid.to_string()).await?
|
||||
}
|
||||
|
||||
self.username = new_username;
|
||||
|
@ -155,10 +157,10 @@ impl Me {
|
|||
|
||||
pub async fn set_display_name(
|
||||
&mut self,
|
||||
data: &Data,
|
||||
app_state: &AppState,
|
||||
new_display_name: String,
|
||||
) -> Result<(), Error> {
|
||||
let mut conn = data.pool.get().await?;
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
let new_display_name_option = if new_display_name.is_empty() {
|
||||
None
|
||||
|
@ -173,8 +175,8 @@ impl Me {
|
|||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
if data.get_cache_key(self.uuid.to_string()).await.is_ok() {
|
||||
data.del_cache_key(self.uuid.to_string()).await?
|
||||
if app_state.get_cache_key(self.uuid.to_string()).await.is_ok() {
|
||||
app_state.del_cache_key(self.uuid.to_string()).await?
|
||||
}
|
||||
|
||||
self.display_name = new_display_name_option;
|
||||
|
@ -182,12 +184,16 @@ impl Me {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn set_email(&mut self, data: &Data, new_email: String) -> Result<(), Error> {
|
||||
pub async fn set_email(
|
||||
&mut self,
|
||||
app_state: &AppState,
|
||||
new_email: String,
|
||||
) -> Result<(), Error> {
|
||||
if !EMAIL_REGEX.is_match(&new_email) {
|
||||
return Err(Error::BadRequest("Invalid username".to_string()));
|
||||
}
|
||||
|
||||
let mut conn = data.pool.get().await?;
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
use users::dsl;
|
||||
update(users::table)
|
||||
|
@ -199,8 +205,8 @@ impl Me {
|
|||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
if data.get_cache_key(self.uuid.to_string()).await.is_ok() {
|
||||
data.del_cache_key(self.uuid.to_string()).await?
|
||||
if app_state.get_cache_key(self.uuid.to_string()).await.is_ok() {
|
||||
app_state.del_cache_key(self.uuid.to_string()).await?
|
||||
}
|
||||
|
||||
self.email = new_email;
|
||||
|
@ -208,8 +214,12 @@ impl Me {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn set_pronouns(&mut self, data: &Data, new_pronouns: String) -> Result<(), Error> {
|
||||
let mut conn = data.pool.get().await?;
|
||||
pub async fn set_pronouns(
|
||||
&mut self,
|
||||
app_state: &AppState,
|
||||
new_pronouns: String,
|
||||
) -> Result<(), Error> {
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
use users::dsl;
|
||||
update(users::table)
|
||||
|
@ -218,15 +228,19 @@ impl Me {
|
|||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
if data.get_cache_key(self.uuid.to_string()).await.is_ok() {
|
||||
data.del_cache_key(self.uuid.to_string()).await?
|
||||
if app_state.get_cache_key(self.uuid.to_string()).await.is_ok() {
|
||||
app_state.del_cache_key(self.uuid.to_string()).await?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn set_about(&mut self, data: &Data, new_about: String) -> Result<(), Error> {
|
||||
let mut conn = data.pool.get().await?;
|
||||
pub async fn set_about(
|
||||
&mut self,
|
||||
app_state: &AppState,
|
||||
new_about: String,
|
||||
) -> Result<(), Error> {
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
use users::dsl;
|
||||
update(users::table)
|
||||
|
@ -235,8 +249,8 @@ impl Me {
|
|||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
if data.get_cache_key(self.uuid.to_string()).await.is_ok() {
|
||||
data.del_cache_key(self.uuid.to_string()).await?
|
||||
if app_state.get_cache_key(self.uuid.to_string()).await.is_ok() {
|
||||
app_state.del_cache_key(self.uuid.to_string()).await?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -352,10 +366,10 @@ impl Me {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_friends(&self, data: &Data) -> Result<Vec<User>, Error> {
|
||||
pub async fn get_friends(&self, app_state: &AppState) -> Result<Vec<User>, Error> {
|
||||
use friends::dsl;
|
||||
|
||||
let mut conn = data.pool.get().await?;
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
let friends1 = load_or_empty(
|
||||
dsl::friends
|
||||
|
@ -374,13 +388,13 @@ impl Me {
|
|||
)?;
|
||||
|
||||
let friend_futures = friends1.iter().map(async move |friend| {
|
||||
User::fetch_one_with_friendship(data, self, friend.uuid2).await
|
||||
User::fetch_one_with_friendship(app_state, self, friend.uuid2).await
|
||||
});
|
||||
|
||||
let mut friends = futures::future::try_join_all(friend_futures).await?;
|
||||
|
||||
let friend_futures = friends2.iter().map(async move |friend| {
|
||||
User::fetch_one_with_friendship(data, self, friend.uuid1).await
|
||||
User::fetch_one_with_friendship(app_state, self, friend.uuid1).await
|
||||
});
|
||||
|
||||
friends.append(&mut futures::future::try_join_all(friend_futures).await?);
|
||||
|
|
|
@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
|
|||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
Conn, Data,
|
||||
AppState, Conn,
|
||||
error::Error,
|
||||
objects::{Me, Permissions, Role},
|
||||
schema::guild_members,
|
||||
|
@ -26,13 +26,13 @@ pub struct MemberBuilder {
|
|||
}
|
||||
|
||||
impl MemberBuilder {
|
||||
pub async fn build(&self, data: &Data, me: Option<&Me>) -> Result<Member, Error> {
|
||||
pub async fn build(&self, app_state: &AppState, me: Option<&Me>) -> Result<Member, Error> {
|
||||
let user;
|
||||
|
||||
if let Some(me) = me {
|
||||
user = User::fetch_one_with_friendship(data, me, self.user_uuid).await?;
|
||||
user = User::fetch_one_with_friendship(app_state, me, self.user_uuid).await?;
|
||||
} else {
|
||||
user = User::fetch_one(data, self.user_uuid).await?;
|
||||
user = User::fetch_one(app_state, self.user_uuid).await?;
|
||||
}
|
||||
|
||||
Ok(Member {
|
||||
|
@ -47,11 +47,11 @@ impl MemberBuilder {
|
|||
|
||||
pub async fn check_permission(
|
||||
&self,
|
||||
data: &Data,
|
||||
app_state: &AppState,
|
||||
permission: Permissions,
|
||||
) -> Result<(), Error> {
|
||||
if !self.is_owner {
|
||||
let roles = Role::fetch_from_member(data, self.uuid).await?;
|
||||
let roles = Role::fetch_from_member(app_state, self.uuid).await?;
|
||||
let allowed = roles.iter().any(|r| r.permissions & permission as i64 != 0);
|
||||
if !allowed {
|
||||
return Err(Error::Forbidden("Not allowed".to_string()));
|
||||
|
@ -101,12 +101,12 @@ impl Member {
|
|||
}
|
||||
|
||||
pub async fn fetch_one(
|
||||
data: &Data,
|
||||
app_state: &AppState,
|
||||
me: &Me,
|
||||
user_uuid: Uuid,
|
||||
guild_uuid: Uuid,
|
||||
) -> Result<Self, Error> {
|
||||
let mut conn = data.pool.get().await?;
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
use guild_members::dsl;
|
||||
let member: MemberBuilder = dsl::guild_members
|
||||
|
@ -116,11 +116,15 @@ impl Member {
|
|||
.get_result(&mut conn)
|
||||
.await?;
|
||||
|
||||
member.build(data, Some(me)).await
|
||||
member.build(app_state, Some(me)).await
|
||||
}
|
||||
|
||||
pub async fn fetch_all(data: &Data, me: &Me, guild_uuid: Uuid) -> Result<Vec<Self>, Error> {
|
||||
let mut conn = data.pool.get().await?;
|
||||
pub async fn fetch_all(
|
||||
app_state: &AppState,
|
||||
me: &Me,
|
||||
guild_uuid: Uuid,
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
use guild_members::dsl;
|
||||
let member_builders: Vec<MemberBuilder> = load_or_empty(
|
||||
|
@ -134,14 +138,18 @@ impl Member {
|
|||
let mut members = vec![];
|
||||
|
||||
for builder in member_builders {
|
||||
members.push(builder.build(&data, Some(me)).await?);
|
||||
members.push(builder.build(app_state, Some(me)).await?);
|
||||
}
|
||||
|
||||
Ok(members)
|
||||
}
|
||||
|
||||
pub async fn new(data: &Data, user_uuid: Uuid, guild_uuid: Uuid) -> Result<Self, Error> {
|
||||
let mut conn = data.pool.get().await?;
|
||||
pub async fn new(
|
||||
app_state: &AppState,
|
||||
user_uuid: Uuid,
|
||||
guild_uuid: Uuid,
|
||||
) -> Result<Self, Error> {
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
let member_uuid = Uuid::now_v7();
|
||||
|
||||
|
@ -158,6 +166,6 @@ impl Member {
|
|||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
member.build(data, None).await
|
||||
member.build(app_state, None).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use diesel::{Insertable, Queryable, Selectable};
|
|||
use serde::Serialize;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{Data, error::Error, schema::messages};
|
||||
use crate::{AppState, error::Error, schema::messages};
|
||||
|
||||
use super::User;
|
||||
|
||||
|
@ -18,8 +18,8 @@ pub struct MessageBuilder {
|
|||
}
|
||||
|
||||
impl MessageBuilder {
|
||||
pub async fn build(&self, data: &Data) -> Result<Message, Error> {
|
||||
let user = User::fetch_one(data, self.user_uuid).await?;
|
||||
pub async fn build(&self, app_state: &AppState) -> Result<Message, Error> {
|
||||
let user = User::fetch_one(app_state, self.user_uuid).await?;
|
||||
|
||||
Ok(Message {
|
||||
uuid: self.uuid,
|
||||
|
|
|
@ -42,6 +42,37 @@ pub trait HasUuid {
|
|||
pub trait HasIsAbove {
|
||||
fn is_above(&self) -> Option<&Uuid>;
|
||||
}
|
||||
/*
|
||||
pub trait Cookies {
|
||||
fn cookies(&self) -> CookieJar;
|
||||
fn cookie<T: AsRef<str>>(&self, cookie: T) -> Option<Cookie>;
|
||||
}
|
||||
|
||||
impl Cookies for Request<Body> {
|
||||
fn cookies(&self) -> CookieJar {
|
||||
let cookies = self.headers()
|
||||
.get(axum::http::header::COOKIE)
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.map(|s| Cookie::split_parse(s.to_string()))
|
||||
.and_then(|c| c.collect::<Result<Vec<Cookie>, cookie::ParseError>>().ok())
|
||||
.unwrap_or(vec![]);
|
||||
|
||||
let mut cookie_jar = CookieJar::new();
|
||||
|
||||
for cookie in cookies {
|
||||
cookie_jar.add(cookie)
|
||||
}
|
||||
|
||||
cookie_jar
|
||||
}
|
||||
|
||||
fn cookie<T: AsRef<str>>(&self, cookie: T) -> Option<Cookie> {
|
||||
self.cookies()
|
||||
.get(cookie.as_ref())
|
||||
.and_then(|c| Some(c.to_owned()))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
fn load_or_empty<T>(
|
||||
query_result: Result<Vec<T>, diesel::result::Error>,
|
||||
|
|
|
@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize};
|
|||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
Data,
|
||||
AppState,
|
||||
error::Error,
|
||||
schema::users,
|
||||
utils::{PASSWORD_REGEX, generate_token, global_checks, user_uuid_from_identifier},
|
||||
|
@ -24,10 +24,11 @@ pub struct PasswordResetToken {
|
|||
}
|
||||
|
||||
impl PasswordResetToken {
|
||||
pub async fn get(data: &Data, token: String) -> Result<PasswordResetToken, Error> {
|
||||
let user_uuid: Uuid = serde_json::from_str(&data.get_cache_key(token.to_string()).await?)?;
|
||||
pub async fn get(app_state: &AppState, token: String) -> Result<PasswordResetToken, Error> {
|
||||
let user_uuid: Uuid =
|
||||
serde_json::from_str(&app_state.get_cache_key(token.to_string()).await?)?;
|
||||
let password_reset_token = serde_json::from_str(
|
||||
&data
|
||||
&app_state
|
||||
.get_cache_key(format!("{user_uuid}_password_reset"))
|
||||
.await?,
|
||||
)?;
|
||||
|
@ -36,15 +37,15 @@ impl PasswordResetToken {
|
|||
}
|
||||
|
||||
pub async fn get_with_identifier(
|
||||
data: &Data,
|
||||
app_state: &AppState,
|
||||
identifier: String,
|
||||
) -> Result<PasswordResetToken, Error> {
|
||||
let mut conn = data.pool.get().await?;
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
let user_uuid = user_uuid_from_identifier(&mut conn, &identifier).await?;
|
||||
|
||||
let password_reset_token = serde_json::from_str(
|
||||
&data
|
||||
&app_state
|
||||
.get_cache_key(format!("{user_uuid}_password_reset"))
|
||||
.await?,
|
||||
)?;
|
||||
|
@ -53,14 +54,14 @@ impl PasswordResetToken {
|
|||
}
|
||||
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub async fn new(data: &Data, identifier: String) -> Result<(), Error> {
|
||||
pub async fn new(app_state: &AppState, identifier: String) -> Result<(), Error> {
|
||||
let token = generate_token::<32>()?;
|
||||
|
||||
let mut conn = data.pool.get().await?;
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
let user_uuid = user_uuid_from_identifier(&mut conn, &identifier).await?;
|
||||
|
||||
global_checks(data, user_uuid).await?;
|
||||
global_checks(app_state, user_uuid).await?;
|
||||
|
||||
use users::dsl as udsl;
|
||||
let (username, email_address): (String, String) = udsl::users
|
||||
|
@ -75,34 +76,37 @@ impl PasswordResetToken {
|
|||
created_at: Utc::now(),
|
||||
};
|
||||
|
||||
data.set_cache_key(
|
||||
format!("{user_uuid}_password_reset"),
|
||||
password_reset_token,
|
||||
86400,
|
||||
)
|
||||
.await?;
|
||||
data.set_cache_key(token.clone(), user_uuid, 86400).await?;
|
||||
app_state
|
||||
.set_cache_key(
|
||||
format!("{user_uuid}_password_reset"),
|
||||
password_reset_token,
|
||||
86400,
|
||||
)
|
||||
.await?;
|
||||
app_state
|
||||
.set_cache_key(token.clone(), user_uuid, 86400)
|
||||
.await?;
|
||||
|
||||
let mut reset_endpoint = data.config.web.frontend_url.join("reset-password")?;
|
||||
let mut reset_endpoint = app_state.config.web.frontend_url.join("reset-password")?;
|
||||
|
||||
reset_endpoint.set_query(Some(&format!("token={token}")));
|
||||
|
||||
let email = data
|
||||
let email = app_state
|
||||
.mail_client
|
||||
.message_builder()
|
||||
.to(email_address.parse()?)
|
||||
.subject(format!("{} Password Reset", data.config.instance.name))
|
||||
.subject(format!("{} Password Reset", app_state.config.instance.name))
|
||||
.multipart(MultiPart::alternative_plain_html(
|
||||
format!("{} Password Reset\n\nHello, {}!\nSomeone requested a password reset for your Gorb account.\nClick the button below within 24 hours to reset your password.\n\n{}\n\nIf you didn't request a password reset, don't worry, your account is safe and you can safely ignore this email.\n\nThanks, The gorb team.", data.config.instance.name, username, reset_endpoint),
|
||||
format!(r#"<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><style>:root {{--header-text-colour: #ffffff;--footer-text-colour: #7f7f7f;--button-text-colour: #170e08;--text-colour: #170e08;--background-colour: #fbf6f2;--primary-colour: #df5f0b;--secondary-colour: #e8ac84;--accent-colour: #e68b4e;}}@media (prefers-color-scheme: dark) {{:root {{--header-text-colour: #ffffff;--footer-text-colour: #585858;--button-text-colour: #ffffff;--text-colour: #f7eee8;--background-colour: #0c0704;--primary-colour: #f4741f;--secondary-colour: #7c4018;--accent-colour: #b35719;}}}}@media (max-width: 600px) {{.container {{width: 100%;}}}}body {{font-family: Arial, sans-serif;align-content: center;text-align: center;margin: 0;padding: 0;background-color: var(--background-colour);color: var(--text-colour);width: 100%;max-width: 600px;margin: 0 auto;border-radius: 5px;}}.header {{background-color: var(--primary-colour);color: var(--header-text-colour);padding: 20px;}}.verify-button {{background-color: var(--accent-colour);color: var(--button-text-colour);padding: 12px 30px;margin: 16px;font-size: 20px;transition: background-color 0.3s;cursor: pointer;border: none;border-radius: 14px;text-decoration: none;display: inline-block;}}.verify-button:hover {{background-color: var(--secondary-colour);}}.content {{padding: 20px 30px;}}.footer {{padding: 10px;font-size: 12px;color: var(--footer-text-colour);}}</style></head><body><div class="container"><div class="header"><h1>{} Password Reset</h1></div><div class="content"><h2>Hello, {}!</h2><p>Someone requested a password reset for your Gorb account.</p><p>Click the button below within 24 hours to reset your password.</p><a href="{}" class="verify-button">RESET PASSWORD</a><p>If you didn't request a password reset, don't worry, your account is safe and you can safely ignore this email.</p><div class="footer"><p>Thanks<br>The gorb team.</p></div></div></div></body></html>"#, data.config.instance.name, username, reset_endpoint)
|
||||
format!("{} Password Reset\n\nHello, {}!\nSomeone requested a password reset for your Gorb account.\nClick the button below within 24 hours to reset your password.\n\n{}\n\nIf you didn't request a password reset, don't worry, your account is safe and you can safely ignore this email.\n\nThanks, The gorb team.", app_state.config.instance.name, username, reset_endpoint),
|
||||
format!(r#"<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><style>:root {{--header-text-colour: #ffffff;--footer-text-colour: #7f7f7f;--button-text-colour: #170e08;--text-colour: #170e08;--background-colour: #fbf6f2;--primary-colour: #df5f0b;--secondary-colour: #e8ac84;--accent-colour: #e68b4e;}}@media (prefers-color-scheme: dark) {{:root {{--header-text-colour: #ffffff;--footer-text-colour: #585858;--button-text-colour: #ffffff;--text-colour: #f7eee8;--background-colour: #0c0704;--primary-colour: #f4741f;--secondary-colour: #7c4018;--accent-colour: #b35719;}}}}@media (max-width: 600px) {{.container {{width: 100%;}}}}body {{font-family: Arial, sans-serif;align-content: center;text-align: center;margin: 0;padding: 0;background-color: var(--background-colour);color: var(--text-colour);width: 100%;max-width: 600px;margin: 0 auto;border-radius: 5px;}}.header {{background-color: var(--primary-colour);color: var(--header-text-colour);padding: 20px;}}.verify-button {{background-color: var(--accent-colour);color: var(--button-text-colour);padding: 12px 30px;margin: 16px;font-size: 20px;transition: background-color 0.3s;cursor: pointer;border: none;border-radius: 14px;text-decoration: none;display: inline-block;}}.verify-button:hover {{background-color: var(--secondary-colour);}}.content {{padding: 20px 30px;}}.footer {{padding: 10px;font-size: 12px;color: var(--footer-text-colour);}}</style></head><body><div class="container"><div class="header"><h1>{} Password Reset</h1></div><div class="content"><h2>Hello, {}!</h2><p>Someone requested a password reset for your Gorb account.</p><p>Click the button below within 24 hours to reset your password.</p><a href="{}" class="verify-button">RESET PASSWORD</a><p>If you didn't request a password reset, don't worry, your account is safe and you can safely ignore this email.</p><div class="footer"><p>Thanks<br>The gorb team.</p></div></div></div></body></html>"#, app_state.config.instance.name, username, reset_endpoint)
|
||||
))?;
|
||||
|
||||
data.mail_client.send_mail(email).await?;
|
||||
app_state.mail_client.send_mail(email).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn set_password(&self, data: &Data, password: String) -> Result<(), Error> {
|
||||
pub async fn set_password(&self, app_state: &AppState, password: String) -> Result<(), Error> {
|
||||
if !PASSWORD_REGEX.is_match(&password) {
|
||||
return Err(Error::BadRequest(
|
||||
"Please provide a valid password".to_string(),
|
||||
|
@ -111,12 +115,12 @@ impl PasswordResetToken {
|
|||
|
||||
let salt = SaltString::generate(&mut OsRng);
|
||||
|
||||
let hashed_password = data
|
||||
let hashed_password = app_state
|
||||
.argon2
|
||||
.hash_password(password.as_bytes(), &salt)
|
||||
.map_err(|e| Error::PasswordHashError(e.to_string()))?;
|
||||
|
||||
let mut conn = data.pool.get().await?;
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
use users::dsl;
|
||||
update(users::table)
|
||||
|
@ -131,27 +135,28 @@ impl PasswordResetToken {
|
|||
.get_result(&mut conn)
|
||||
.await?;
|
||||
|
||||
let login_page = data.config.web.frontend_url.join("login")?;
|
||||
let login_page = app_state.config.web.frontend_url.join("login")?;
|
||||
|
||||
let email = data
|
||||
let email = app_state
|
||||
.mail_client
|
||||
.message_builder()
|
||||
.to(email_address.parse()?)
|
||||
.subject(format!("Your {} Password has been Reset", data.config.instance.name))
|
||||
.subject(format!("Your {} Password has been Reset", app_state.config.instance.name))
|
||||
.multipart(MultiPart::alternative_plain_html(
|
||||
format!("{} Password Reset Confirmation\n\nHello, {}!\nYour password has been successfully reset for your Gorb account.\nIf you did not initiate this change, please click the link below to reset your password <strong>immediately</strong>.\n\n{}\n\nThanks, The gorb team.", data.config.instance.name, username, login_page),
|
||||
format!(r#"<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><style>:root {{--header-text-colour: #ffffff;--footer-text-colour: #7f7f7f;--button-text-colour: #170e08;--text-colour: #170e08;--background-colour: #fbf6f2;--primary-colour: #df5f0b;--secondary-colour: #e8ac84;--accent-colour: #e68b4e;}}@media (prefers-color-scheme: dark) {{:root {{--header-text-colour: #ffffff;--footer-text-colour: #585858;--button-text-colour: #ffffff;--text-colour: #f7eee8;--background-colour: #0c0704;--primary-colour: #f4741f;--secondary-colour: #7c4018;--accent-colour: #b35719;}}}}@media (max-width: 600px) {{.container {{width: 100%;}}}}body {{font-family: Arial, sans-serif;align-content: center;text-align: center;margin: 0;padding: 0;background-color: var(--background-colour);color: var(--text-colour);width: 100%;max-width: 600px;margin: 0 auto;border-radius: 5px;}}.header {{background-color: var(--primary-colour);color: var(--header-text-colour);padding: 20px;}}.verify-button {{background-color: var(--accent-colour);color: var(--button-text-colour);padding: 12px 30px;margin: 16px;font-size: 20px;transition: background-color 0.3s;cursor: pointer;border: none;border-radius: 14px;text-decoration: none;display: inline-block;}}.verify-button:hover {{background-color: var(--secondary-colour);}}.content {{padding: 20px 30px;}}.footer {{padding: 10px;font-size: 12px;color: var(--footer-text-colour);}}</style></head><body><div class="container"><div class="header"><h1>{} Password Reset Confirmation</h1></div><div class="content"><h2>Hello, {}!</h2><p>Your password has been successfully reset for your Gorb account.</p><p>If you did not initiate this change, please click the button below to reset your password <strong>immediately</strong>.</p><a href="{}" class="verify-button">RESET PASSWORD</a><div class="footer"><p>Thanks<br>The gorb team.</p></div></div></div></body></html>"#, data.config.instance.name, username, login_page)
|
||||
format!("{} Password Reset Confirmation\n\nHello, {}!\nYour password has been successfully reset for your Gorb account.\nIf you did not initiate this change, please click the link below to reset your password <strong>immediately</strong>.\n\n{}\n\nThanks, The gorb team.", app_state.config.instance.name, username, login_page),
|
||||
format!(r#"<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><style>:root {{--header-text-colour: #ffffff;--footer-text-colour: #7f7f7f;--button-text-colour: #170e08;--text-colour: #170e08;--background-colour: #fbf6f2;--primary-colour: #df5f0b;--secondary-colour: #e8ac84;--accent-colour: #e68b4e;}}@media (prefers-color-scheme: dark) {{:root {{--header-text-colour: #ffffff;--footer-text-colour: #585858;--button-text-colour: #ffffff;--text-colour: #f7eee8;--background-colour: #0c0704;--primary-colour: #f4741f;--secondary-colour: #7c4018;--accent-colour: #b35719;}}}}@media (max-width: 600px) {{.container {{width: 100%;}}}}body {{font-family: Arial, sans-serif;align-content: center;text-align: center;margin: 0;padding: 0;background-color: var(--background-colour);color: var(--text-colour);width: 100%;max-width: 600px;margin: 0 auto;border-radius: 5px;}}.header {{background-color: var(--primary-colour);color: var(--header-text-colour);padding: 20px;}}.verify-button {{background-color: var(--accent-colour);color: var(--button-text-colour);padding: 12px 30px;margin: 16px;font-size: 20px;transition: background-color 0.3s;cursor: pointer;border: none;border-radius: 14px;text-decoration: none;display: inline-block;}}.verify-button:hover {{background-color: var(--secondary-colour);}}.content {{padding: 20px 30px;}}.footer {{padding: 10px;font-size: 12px;color: var(--footer-text-colour);}}</style></head><body><div class="container"><div class="header"><h1>{} Password Reset Confirmation</h1></div><div class="content"><h2>Hello, {}!</h2><p>Your password has been successfully reset for your Gorb account.</p><p>If you did not initiate this change, please click the button below to reset your password <strong>immediately</strong>.</p><a href="{}" class="verify-button">RESET PASSWORD</a><div class="footer"><p>Thanks<br>The gorb team.</p></div></div></div></body></html>"#, app_state.config.instance.name, username, login_page)
|
||||
))?;
|
||||
|
||||
data.mail_client.send_mail(email).await?;
|
||||
app_state.mail_client.send_mail(email).await?;
|
||||
|
||||
self.delete(data).await
|
||||
self.delete(app_state).await
|
||||
}
|
||||
|
||||
pub async fn delete(&self, data: &Data) -> Result<(), Error> {
|
||||
data.del_cache_key(format!("{}_password_reset", &self.user_uuid))
|
||||
pub async fn delete(&self, app_state: &AppState) -> Result<(), Error> {
|
||||
app_state
|
||||
.del_cache_key(format!("{}_password_reset", &self.user_uuid))
|
||||
.await?;
|
||||
data.del_cache_key(self.token.to_string()).await?;
|
||||
app_state.del_cache_key(self.token.to_string()).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
|
|||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
Conn, Data,
|
||||
AppState, Conn,
|
||||
error::Error,
|
||||
schema::{role_members, roles},
|
||||
utils::order_by_is_above,
|
||||
|
@ -74,12 +74,18 @@ impl Role {
|
|||
Ok(roles)
|
||||
}
|
||||
|
||||
pub async fn fetch_from_member(data: &Data, member_uuid: Uuid) -> Result<Vec<Self>, Error> {
|
||||
if let Ok(roles) = data.get_cache_key(format!("{member_uuid}_roles")).await {
|
||||
pub async fn fetch_from_member(
|
||||
app_state: &AppState,
|
||||
member_uuid: Uuid,
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
if let Ok(roles) = app_state
|
||||
.get_cache_key(format!("{member_uuid}_roles"))
|
||||
.await
|
||||
{
|
||||
return Ok(serde_json::from_str(&roles)?);
|
||||
}
|
||||
|
||||
let mut conn = data.pool.get().await?;
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
use role_members::dsl;
|
||||
let role_memberships: Vec<RoleMember> = load_or_empty(
|
||||
|
@ -96,7 +102,8 @@ impl Role {
|
|||
roles.push(membership.fetch_role(&mut conn).await?);
|
||||
}
|
||||
|
||||
data.set_cache_key(format!("{member_uuid}_roles"), roles.clone(), 300)
|
||||
app_state
|
||||
.set_cache_key(format!("{member_uuid}_roles"), roles.clone(), 300)
|
||||
.await?;
|
||||
|
||||
Ok(roles)
|
||||
|
|
|
@ -4,7 +4,7 @@ use diesel_async::RunQueryDsl;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{Conn, Data, error::Error, objects::Me, schema::users};
|
||||
use crate::{AppState, Conn, error::Error, objects::Me, schema::users};
|
||||
|
||||
use super::load_or_empty;
|
||||
|
||||
|
@ -46,10 +46,10 @@ pub struct User {
|
|||
}
|
||||
|
||||
impl User {
|
||||
pub async fn fetch_one(data: &Data, user_uuid: Uuid) -> Result<Self, Error> {
|
||||
let mut conn = data.pool.get().await?;
|
||||
pub async fn fetch_one(app_state: &AppState, user_uuid: Uuid) -> Result<Self, Error> {
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
if let Ok(cache_hit) = data.get_cache_key(user_uuid.to_string()).await {
|
||||
if let Ok(cache_hit) = app_state.get_cache_key(user_uuid.to_string()).await {
|
||||
return Ok(serde_json::from_str(&cache_hit)?);
|
||||
}
|
||||
|
||||
|
@ -62,20 +62,21 @@ impl User {
|
|||
|
||||
let user = user_builder.build();
|
||||
|
||||
data.set_cache_key(user_uuid.to_string(), user.clone(), 1800)
|
||||
app_state
|
||||
.set_cache_key(user_uuid.to_string(), user.clone(), 1800)
|
||||
.await?;
|
||||
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub async fn fetch_one_with_friendship(
|
||||
data: &Data,
|
||||
app_state: &AppState,
|
||||
me: &Me,
|
||||
user_uuid: Uuid,
|
||||
) -> Result<Self, Error> {
|
||||
let mut conn = data.pool.get().await?;
|
||||
let mut conn = app_state.pool.get().await?;
|
||||
|
||||
let mut user = Self::fetch_one(data, user_uuid).await?;
|
||||
let mut user = Self::fetch_one(app_state, user_uuid).await?;
|
||||
|
||||
if let Some(friend) = me.friends_with(&mut conn, user_uuid).await? {
|
||||
user.friends_since = Some(friend.accepted_at);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue