admin/credit: don't credit unprovisioned subscriptions (#4594)

* Remove pointless sorting

* Filter subscriptions by labrinth's provisioned state
This commit is contained in:
François-Xavier Talbot
2025-10-20 21:31:20 +01:00
committed by GitHub
parent eeed4e572d
commit c379e4b173
2 changed files with 35 additions and 28 deletions

View File

@@ -1,6 +1,7 @@
use self::payments::*;
use crate::auth::get_user_from_headers;
use crate::database::models::charge_item::DBCharge;
use crate::database::models::ids::DBUserSubscriptionId;
use crate::database::models::notification_item::NotificationBuilder;
use crate::database::models::products_tax_identifier_item::product_info_by_product_price_id;
use crate::database::models::users_subscriptions_credits::DBUserSubscriptionCredit;
@@ -2189,10 +2190,8 @@ pub async fn stripe_webhook(
Ok(HttpResponse::Ok().finish())
}
pub mod payments;
#[allow(clippy::too_many_arguments)]
async fn apply_credit_many_in_txn(
async fn apply_credit_many(
transaction: &mut Transaction<'_, Postgres>,
redis: &RedisPool,
current_user_id: crate::database::models::ids::DBUserId,
@@ -2201,20 +2200,6 @@ async fn apply_credit_many_in_txn(
send_email: bool,
message: String,
) -> Result<(), ApiError> {
use crate::database::models::ids::DBUserSubscriptionId;
let mut credit_sub_ids: Vec<DBUserSubscriptionId> =
Vec::with_capacity(subscription_ids.len());
let mut credit_user_ids: Vec<crate::database::models::ids::DBUserId> =
Vec::with_capacity(subscription_ids.len());
let mut credit_creditor_ids: Vec<crate::database::models::ids::DBUserId> =
Vec::with_capacity(subscription_ids.len());
let mut credit_days: Vec<i32> = Vec::with_capacity(subscription_ids.len());
let mut credit_prev_dues: Vec<chrono::DateTime<chrono::Utc>> =
Vec::with_capacity(subscription_ids.len());
let mut credit_next_dues: Vec<chrono::DateTime<chrono::Utc>> =
Vec::with_capacity(subscription_ids.len());
let subs_ids: Vec<DBUserSubscriptionId> = subscription_ids
.iter()
.map(|id| DBUserSubscriptionId(id.0 as i64))
@@ -2225,7 +2210,28 @@ async fn apply_credit_many_in_txn(
)
.await?;
let provisioned_count = subs
.iter()
.filter(|s| s.status == SubscriptionStatus::Provisioned)
.count();
let mut credit_sub_ids: Vec<DBUserSubscriptionId> =
Vec::with_capacity(provisioned_count);
let mut credit_user_ids: Vec<crate::database::models::ids::DBUserId> =
Vec::with_capacity(provisioned_count);
let mut credit_creditor_ids: Vec<crate::database::models::ids::DBUserId> =
Vec::with_capacity(provisioned_count);
let mut credit_days: Vec<i32> = Vec::with_capacity(provisioned_count);
let mut credit_prev_dues: Vec<chrono::DateTime<chrono::Utc>> =
Vec::with_capacity(provisioned_count);
let mut credit_next_dues: Vec<chrono::DateTime<chrono::Utc>> =
Vec::with_capacity(provisioned_count);
for subscription in subs {
if subscription.status != SubscriptionStatus::Provisioned {
continue;
}
let mut open_charge = charge_item::DBCharge::get_open_subscription(
subscription.id,
&mut **transaction,
@@ -2349,7 +2355,7 @@ pub async fn credit(
"You must specify at least one subscription id".to_string(),
));
}
apply_credit_many_in_txn(
apply_credit_many(
&mut transaction,
&redis,
crate::database::models::ids::DBUserId(user.id.0 as i64),
@@ -2370,9 +2376,8 @@ pub async fn credit(
for hostname in nodes {
let ids =
archon_client.get_servers_by_hostname(&hostname).await?;
server_ids.extend(ids);
server_ids.extend(ids.into_iter().map(|id| id.to_string()));
}
server_ids.sort();
server_ids.dedup();
let subs = user_subscription_item::DBUserSubscription::get_many_by_server_ids(
&server_ids,
@@ -2384,7 +2389,7 @@ pub async fn credit(
"No subscriptions found for provided nodes".to_string(),
));
}
apply_credit_many_in_txn(
apply_credit_many(
&mut transaction,
&redis,
crate::database::models::ids::DBUserId(user.id.0 as i64),
@@ -2396,10 +2401,10 @@ pub async fn credit(
.await?;
}
CreditTarget::Region { region } => {
let parsed_active =
let servers =
archon_client.get_active_servers_by_region(&region).await?;
let subs = user_subscription_item::DBUserSubscription::get_many_by_server_ids(
&parsed_active,
&servers.into_iter().map(|id| id.to_string()).collect::<Vec<String>>(),
&mut *transaction,
)
.await?;
@@ -2408,7 +2413,7 @@ pub async fn credit(
"No subscriptions found for provided region".to_string(),
));
}
apply_credit_many_in_txn(
apply_credit_many(
&mut transaction,
&redis,
crate::database::models::ids::DBUserId(user.id.0 as i64),
@@ -2425,3 +2430,5 @@ pub async fn credit(
Ok(HttpResponse::NoContent().finish())
}
pub mod payments;

View File

@@ -76,14 +76,14 @@ impl ArchonClient {
pub async fn get_servers_by_hostname(
&self,
hostname: &str,
) -> Result<Vec<String>, reqwest::Error> {
) -> Result<Vec<Uuid>, reqwest::Error> {
#[derive(Deserialize)]
struct NodeByHostnameResponse {
servers: Vec<NodeServerEntry>,
}
#[derive(Deserialize)]
struct NodeServerEntry {
id: String,
id: Uuid,
#[allow(dead_code)]
available: Option<bool>,
}
@@ -106,10 +106,10 @@ impl ArchonClient {
pub async fn get_active_servers_by_region(
&self,
region: &str,
) -> Result<Vec<String>, reqwest::Error> {
) -> Result<Vec<Uuid>, reqwest::Error> {
#[derive(Deserialize)]
struct RegionResponse {
active_servers: Vec<String>,
active_servers: Vec<Uuid>,
}
let res = self