Switch to Trolley for Modrinth Payments (#727)

* most of trolley

* Switch to trolley for payments

* run prepare

* fix clippy

* fix more

* Fix most tests + bitflags

* Update src/auth/flows.rs

Co-authored-by: Jackson Kruger <jak.kruger@gmail.com>

* Finish trolley

* run prep for merge

* Update src/queue/payouts.rs

Co-authored-by: Jackson Kruger <jak.kruger@gmail.com>

---------

Co-authored-by: Jackson Kruger <jak.kruger@gmail.com>
This commit is contained in:
Geometrically
2023-10-11 15:55:01 -07:00
committed by GitHub
parent f1ff88f452
commit 07ecd13554
41 changed files with 1719 additions and 1461 deletions

View File

@@ -8,7 +8,8 @@ use crate::file_hosting::FileHost;
use crate::models::ids::base62_impl::{parse_base62, to_base62};
use crate::models::ids::random_base62_rng;
use crate::models::pats::Scopes;
use crate::models::users::{Badges, Role};
use crate::models::users::{Badges, RecipientStatus, Role, UserPayoutData};
use crate::queue::payouts::{AccountUser, PayoutsQueue};
use crate::queue::session::AuthQueue;
use crate::queue::socket::ActiveSockets;
use crate::routes::ApiError;
@@ -30,7 +31,7 @@ use serde::{Deserialize, Serialize};
use sqlx::postgres::PgPool;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use tokio::sync::{Mutex, RwLock};
use validator::Validate;
pub fn config(cfg: &mut ServiceConfig) {
@@ -51,7 +52,8 @@ pub fn config(cfg: &mut ServiceConfig) {
.service(resend_verify_email)
.service(set_email)
.service(verify_email)
.service(subscribe_newsletter),
.service(subscribe_newsletter)
.service(link_trolley),
);
}
@@ -225,9 +227,8 @@ impl TempUser {
role: Role::Developer.to_string(),
badges: Badges::default(),
balance: Decimal::ZERO,
payout_wallet: None,
payout_wallet_type: None,
payout_address: None,
trolley_id: None,
trolley_account_status: None,
}
.insert(transaction)
.await?;
@@ -1013,7 +1014,7 @@ pub async fn auth_callback(
let sockets = active_sockets.clone();
let state = state_string.clone();
let res: Result<HttpResponse, AuthenticationError> = (|| async move {
let res: Result<HttpResponse, AuthenticationError> = async move {
let flow = Flow::get(&state, &redis).await?;
@@ -1175,7 +1176,7 @@ pub async fn auth_callback(
} else {
Err::<HttpResponse, AuthenticationError>(AuthenticationError::InvalidCredentials)
}
})().await;
}.await;
// Because this is callback route, if we have an error, we need to ensure we close the original socket if it exists
if let Err(ref e) = res {
@@ -1385,9 +1386,8 @@ pub async fn create_account_with_password(
role: Role::Developer.to_string(),
badges: Badges::default(),
balance: Decimal::ZERO,
payout_wallet: None,
payout_wallet_type: None,
payout_address: None,
trolley_id: None,
trolley_account_status: None,
}
.insert(&mut transaction)
.await?;
@@ -2011,6 +2011,7 @@ pub async fn set_email(
redis: Data<RedisPool>,
email: web::Json<SetEmail>,
session_queue: Data<AuthQueue>,
payouts_queue: Data<Mutex<PayoutsQueue>>,
) -> Result<HttpResponse, ApiError> {
email
.0
@@ -2064,6 +2065,17 @@ pub async fn set_email(
"We need to verify your email address.",
)?;
if let Some(UserPayoutData {
trolley_id: Some(trolley_id),
..
}) = user.payout_data
{
let queue = payouts_queue.lock().await;
queue
.update_recipient_email(&trolley_id, &email.email)
.await?;
}
crate::database::models::User::clear_caches(&[(user.id.into(), None)], &redis).await?;
transaction.commit().await?;
@@ -2206,3 +2218,59 @@ fn send_email_verify(
Some(("Verify email", &format!("{}/{}?flow={}", dotenvy::var("SITE_URL")?, dotenvy::var("SITE_VERIFY_EMAIL_PATH")?, flow))),
)
}
#[post("trolley/link")]
pub async fn link_trolley(
req: HttpRequest,
pool: Data<PgPool>,
redis: Data<RedisPool>,
session_queue: Data<AuthQueue>,
payouts_queue: Data<Mutex<PayoutsQueue>>,
body: web::Json<AccountUser>,
) -> Result<HttpResponse, ApiError> {
let user = get_user_from_headers(
&req,
&**pool,
&redis,
&session_queue,
Some(&[Scopes::PAYOUTS_WRITE]),
)
.await?
.1;
if let Some(payout_data) = user.payout_data {
if payout_data.trolley_id.is_some() {
return Err(ApiError::InvalidInput(
"User already has a trolley account.".to_string(),
));
}
}
if let Some(email) = user.email {
let id = payouts_queue.lock().await.register_recipient(&email, body.0).await?;
let mut transaction = pool.begin().await?;
sqlx::query!(
"
UPDATE users
SET trolley_id = $1, trolley_account_status = $2
WHERE id = $3
",
id,
RecipientStatus::Incomplete.as_str(),
user.id.0 as i64,
)
.execute(&mut transaction)
.await?;
transaction.commit().await?;
crate::database::models::User::clear_caches(&[(user.id.into(), None)], &redis).await?;
Ok(HttpResponse::NoContent().finish())
} else {
Err(ApiError::InvalidInput(
"User needs to have an email set on account.".to_string(),
))
}
}

View File

@@ -56,16 +56,15 @@ where
created: db_user.created,
role: Role::from_string(&db_user.role),
badges: db_user.badges,
payout_data: Some(UserPayoutData {
balance: db_user.balance,
payout_wallet: db_user.payout_wallet,
payout_wallet_type: db_user.payout_wallet_type,
payout_address: db_user.payout_address,
}),
auth_providers: Some(auth_providers),
has_password: Some(db_user.password.is_some()),
has_totp: Some(db_user.totp_secret.is_some()),
github_id: None,
payout_data: Some(UserPayoutData {
balance: db_user.balance,
trolley_id: db_user.trolley_id,
trolley_status: db_user.trolley_account_status,
}),
};
if let Some(required_scopes) = required_scopes {