start on runs routes

This commit is contained in:
aecsocket
2026-08-17 17:33:55 +09:00
parent 9cb02f3abf
commit 369cd7aa91
8 changed files with 68 additions and 10 deletions
+1
View File
@@ -160,6 +160,7 @@ FLAME_ANVIL_URL=none
STRIPE_API_KEY=none
STRIPE_WEBHOOK_SECRET=none
ADITUDE_BASE_URL=https://cloud.aditude.io/api
ADITUDE_API_KEY=none
PYRO_API_KEY=none
+1
View File
@@ -186,6 +186,7 @@ FLAME_ANVIL_URL=none
STRIPE_API_KEY=none
STRIPE_WEBHOOK_SECRET=none
ADITUDE_BASE_URL=https://cloud.aditude.io/api
ADITUDE_API_KEY=none
PYRO_API_KEY=none
+1
View File
@@ -335,6 +335,7 @@ vars! {
STRIPE_API_KEY: String = "none";
STRIPE_WEBHOOK_SECRET: String = "none";
ADITUDE_BASE_URL: String = "https://cloud.aditude.io/api";
ADITUDE_API_KEY: String = "none";
PYRO_API_KEY: String = "none";
+7
View File
@@ -70,6 +70,7 @@ pub struct LabrinthConfig {
pub rate_limiter: web::Data<AsyncRateLimiter>,
pub stripe_client: stripe::Client,
pub anrok_client: anrok::Client,
pub aditude_client: web::Data<aditude::Client>,
pub email_queue: web::Data<EmailQueue>,
pub archon_client: web::Data<ArchonClient>,
pub gotenberg_client: GotenbergClient,
@@ -99,6 +100,10 @@ pub fn app_setup(
let scheduler = scheduler::Scheduler::new();
let http_client = web::Data::new(HttpClient::new());
let aditude_client = web::Data::new(aditude::Client::new(
ENV.ADITUDE_BASE_URL.clone(),
ENV.ADITUDE_API_KEY.clone(),
));
let tiltify_client =
web::Data::new(TiltifyClient::new(http_client.get_ref().clone()));
let search_state = web::Data::new(search::SearchState {
@@ -302,6 +307,7 @@ pub fn app_setup(
rate_limiter: limiter,
stripe_client,
anrok_client,
aditude_client,
gotenberg_client,
http_client,
tiltify_client,
@@ -359,6 +365,7 @@ pub fn app_data_config(
.app_data(labrinth_config.archon_client.clone())
.app_data(web::Data::new(labrinth_config.stripe_client.clone()))
.app_data(web::Data::new(labrinth_config.anrok_client.clone()))
.app_data(labrinth_config.aditude_client.clone())
.app_data(labrinth_config.rate_limiter.clone())
.app_data(labrinth_config.kafka_client.clone())
.app_data(labrinth_config.search_state.clone())
@@ -36,7 +36,7 @@ pub async fn estimate(
periods: &[YearMonth],
) -> Result<Vec<PeriodEstimate>> {
redis
.get_cached_keys(REDIS_KEY, periods, |periods| async {
.get_cached_keys(REDIS_KEY, periods, |periods| async move {
fetch_estimates(aditude, redis, &periods)
.await
.map_err(ApiError::Internal)
+19 -9
View File
@@ -26,7 +26,7 @@
//!
//! How revenue is distributed:
//! - While a month is still open/in review:
//! - `raw_estimated_revenue_usd`: how much our ad provider estimates we'll
//! - `estimated.raw_revenue_usd`: how much our ad provider estimates we'll
//! earn for a specific period
//! - We also get a value for this per day
//! - `fees_usd`: how much we pay in fees to Clean.io
@@ -36,23 +36,23 @@
//! - e.g. if variance is 10%, and we estimate that we'll earn $100k, then
//! our ad provider will probably give us closer to $90k - the variance
//! lets us express this difference
//! - `net_estimated_revenue_usd`: raw estimated - fees - variance
//! - `platform_net_estimated_revenue_usd`: net estimated revenue x Modrinth's cut
//! - `creator_net_estimated_revenue_usd`: net estimated revenue x (1 - Modrinth's cut)
//! - `net_revenue_usd`: raw estimated - fees - variance
//! - `platform_net_revenue_usd`: net estimated revenue x Modrinth's cut
//! - `creator_net_revenue_usd`: net estimated revenue x (1 - Modrinth's cut)
//! - After a payout run has been executed:
//! - We save the per-day raw estimated revenue and impressions in the
//! database
//! - `raw_actual_revenue_usd`: how much we got from Aditude, input by an admin
//! - `actual.raw_revenue_usd`: how much we got from Aditude, input by an admin
//! - We compute this per-day by:
//! ```text
//! let factor = raw_actual_revenue_usd / raw_estimated_revenue_usd
//! raw_actual_revenue_usd_for_today = raw_estimated_revenue_usd_for_today * factor
//! let factor = actual.raw_revenue_usd / raw_estimated_revenue_usd
//! raw_revenue_usd_for_today = raw_estimated_revenue_usd_for_today * factor
//! ```
//! - (fees stay the same, since they're based on impressions, not revenue)
//! - (variance is ignored, since that's purely an estimation value)
//! - `adjustments_usd`: sum of all manual adjustments input by the admin
//! - `net_actual_revenue_usd`: raw actual revenue - fees + adjustments
//! - `(platform|creator)_net_estimated_revenue_usd`: same logic as estimated,
//! - `actual.net_revenue_usd`: raw actual revenue - fees + adjustments
//! - `actual.(platform|creator)_net_revenue_usd`: same logic as estimated,
//! but using the net actual revenue
//!
//! ## Variance
@@ -76,12 +76,22 @@ const PLATFORM_REVENUE_SPLIT: Decimal = dec!(0.25);
/// This may refer to either estimated or actual revenue.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DayDistribution {
/// Amount of revenue input into the algorithm.
pub raw_revenue_usd: Decimal,
/// Operational fees to subtract.
pub fees_usd: Decimal,
/// Estimation variance to subtract.
///
/// For non-estimates (actual revenue values), this is zero.
pub variance_usd: Decimal,
/// Manually-input adjustments on top of raw revenue.
pub sum_adjustments_usd: Decimal,
/// Total net revenue that we earned;
/// `raw_revenue - fees - variance + sum_adjustments`.
pub net_revenue_usd: Decimal,
/// How much of the net revenue goes to the platform.
pub platform_net_revenue_usd: Decimal,
/// How much of the net revenue goes to creators.
pub creator_net_revenue_usd: Decimal,
}
+1
View File
@@ -15,6 +15,7 @@ pub mod medal;
pub mod moderation;
pub mod mural;
pub mod pats;
pub mod payout_runs;
pub mod search;
pub mod server_ping;
pub mod session;
@@ -0,0 +1,37 @@
use actix_web::{get, web};
use crate::{queue::payout_run::DayDistribution, routes::ApiError};
#[derive(Debug, Serialize, Deserialize)]
pub struct PayoutRuns {
pub periods: Vec<PayoutRunPeriod>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PayoutRunPeriod {
pub period: YearMonth,
pub status: PayoutRunStatus,
pub days: Vec<PayoutRunDay>,
pub adjustments: Vec<PayoutRunAdjustment>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PayoutRunDay {
pub date: NaiveDate,
pub estimated: DayDistribution,
pub actual: Option<DayDistribution>,
}
/// Manual admin-input adjustment to a [`PayoutRunPeriod`].
#[derive(Debug, Serialize, Deserialize)]
pub struct PayoutRunAdjustment {
/// Total value of the adjustment.
pub amount_usd: Decimal,
/// Why this adjustment was applied.
///
/// Only visible to admins.
pub description: Option<String>,
}
#[get("/")]
pub async fn get_runs() -> Result<web::Json<PayoutRuns>, ApiError> {}