feat(billing): add billing_mode to providers (postpaid support) & UI/migration
Some checks failed
CI / Check (push) Has been cancelled
CI / Clippy (push) Has been cancelled
CI / Formatting (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Release Build (push) Has been cancelled

This commit is contained in:
2026-03-03 15:37:19 -05:00
parent 75b68521c8
commit 1453e64d4b
4 changed files with 77 additions and 35 deletions

View File

@@ -17,6 +17,7 @@ pub(super) struct UpdateProviderRequest {
pub(super) api_key: Option<String>,
pub(super) credit_balance: Option<f64>,
pub(super) low_credit_threshold: Option<f64>,
pub(super) billing_mode: Option<String>,
}
pub(super) async fn handle_get_providers(State(state): State<DashboardState>) -> Json<ApiResponse<serde_json::Value>> {
@@ -24,11 +25,12 @@ pub(super) async fn handle_get_providers(State(state): State<DashboardState>) ->
let config = &state.app_state.config;
let pool = &state.app_state.db_pool;
// Load all overrides from database
let db_configs_result =
sqlx::query("SELECT id, enabled, base_url, credit_balance, low_credit_threshold FROM provider_configs")
.fetch_all(pool)
.await;
// Load all overrides from database (including billing_mode)
let db_configs_result = sqlx::query(
"SELECT id, enabled, base_url, credit_balance, low_credit_threshold, billing_mode FROM provider_configs",
)
.fetch_all(pool)
.await;
let mut db_configs = HashMap::new();
if let Ok(rows) = db_configs_result {
@@ -38,7 +40,8 @@ pub(super) async fn handle_get_providers(State(state): State<DashboardState>) ->
let base_url: Option<String> = row.get("base_url");
let balance: f64 = row.get("credit_balance");
let threshold: f64 = row.get("low_credit_threshold");
db_configs.insert(id, (enabled, base_url, balance, threshold));
let billing_mode: Option<String> = row.get("billing_mode");
db_configs.insert(id, (enabled, base_url, balance, threshold, billing_mode));
}
}
@@ -80,15 +83,17 @@ pub(super) async fn handle_get_providers(State(state): State<DashboardState>) ->
let mut balance = 0.0;
let mut threshold = 5.0;
let mut billing_mode: Option<String> = None;
// Apply database overrides
if let Some((db_enabled, db_url, db_balance, db_threshold)) = db_configs.get(id) {
if let Some((db_enabled, db_url, db_balance, db_threshold, db_billing)) = db_configs.get(id) {
enabled = *db_enabled;
if let Some(url) = db_url {
base_url = url.clone();
}
balance = *db_balance;
threshold = *db_threshold;
billing_mode = db_billing.clone();
}
// Find models for this provider in registry
@@ -138,6 +143,7 @@ pub(super) async fn handle_get_providers(State(state): State<DashboardState>) ->
"base_url": base_url,
"credit_balance": balance,
"low_credit_threshold": threshold,
"billing_mode": billing_mode,
"last_used": None::<String>,
}));
}
@@ -185,10 +191,11 @@ pub(super) async fn handle_get_provider(
let mut balance = 0.0;
let mut threshold = 5.0;
let mut billing_mode: Option<String> = None;
// Apply database overrides
let db_config = sqlx::query(
"SELECT enabled, base_url, credit_balance, low_credit_threshold FROM provider_configs WHERE id = ?",
"SELECT enabled, base_url, credit_balance, low_credit_threshold, billing_mode FROM provider_configs WHERE id = ?",
)
.bind(&name)
.fetch_optional(pool)
@@ -201,6 +208,7 @@ pub(super) async fn handle_get_provider(
}
balance = row.get::<f64, _>("credit_balance");
threshold = row.get::<f64, _>("low_credit_threshold");
billing_mode = row.get::<Option<String>, _>("billing_mode");
}
// Find models for this provider
@@ -246,6 +254,7 @@ pub(super) async fn handle_get_provider(
"base_url": base_url,
"credit_balance": balance,
"low_credit_threshold": threshold,
"billing_mode": billing_mode,
"last_used": None::<String>,
})))
}
@@ -262,19 +271,20 @@ pub(super) async fn handle_update_provider(
let pool = &state.app_state.db_pool;
// Update or insert into database
// Update or insert into database (include billing_mode)
let result = sqlx::query(
r#"
INSERT INTO provider_configs (id, display_name, enabled, base_url, api_key, credit_balance, low_credit_threshold)
VALUES (?, ?, ?, ?, ?, ?, ?)
INSERT INTO provider_configs (id, display_name, enabled, base_url, api_key, credit_balance, low_credit_threshold, billing_mode)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET
enabled = excluded.enabled,
base_url = excluded.base_url,
api_key = COALESCE(excluded.api_key, provider_configs.api_key),
credit_balance = COALESCE(excluded.credit_balance, provider_configs.credit_balance),
low_credit_threshold = COALESCE(excluded.low_credit_threshold, provider_configs.low_credit_threshold),
billing_mode = COALESCE(excluded.billing_mode, provider_configs.billing_mode),
updated_at = CURRENT_TIMESTAMP
"#
"#,
)
.bind(&name)
.bind(name.to_uppercase())
@@ -283,6 +293,7 @@ pub(super) async fn handle_update_provider(
.bind(&payload.api_key)
.bind(payload.credit_balance)
.bind(payload.low_credit_threshold)
.bind(payload.billing_mode)
.execute(pool)
.await;