From 8ea826e6adeea3ad3ae4f7eb785fd8db63f87d0e Mon Sep 17 00:00:00 2001 From: ZhenYi <434836402@qq.com> Date: Sun, 26 Apr 2026 14:05:52 +0800 Subject: [PATCH] chore(api): remove admin billing endpoint Admin Next.js app handles billing directly via database access now. --- libs/api/admin/billing.rs | 103 -------------------------------------- libs/api/admin/mod.rs | 11 ++-- libs/api/openapi.rs | 1 - 3 files changed, 3 insertions(+), 112 deletions(-) delete mode 100644 libs/api/admin/billing.rs diff --git a/libs/api/admin/billing.rs b/libs/api/admin/billing.rs deleted file mode 100644 index 830cb74..0000000 --- a/libs/api/admin/billing.rs +++ /dev/null @@ -1,103 +0,0 @@ -//! Workspace billing admin endpoints (add-credit without membership requirement). - -use actix_web::{HttpRequest, HttpResponse, Result, web}; -use chrono::Utc; -use models::*; -use service::workspace::billing::WorkspaceBillingAddCreditParams; -use service::AppService; -use uuid::Uuid; -use sea_orm::sea_query::prelude::rust_decimal::prelude::ToPrimitive; - -use crate::error::ApiError; -use crate::ApiResponse; -use service::error::AppError; - -/// Validate the `x-admin-api-key` header against ADMIN_API_SHARED_KEY env var. -fn validate_admin_key(req: &HttpRequest) -> Result<(), ApiError> { - let expected = std::env::var("ADMIN_API_SHARED_KEY").ok(); - let Some(expected) = expected else { - return Err(ApiError(AppError::InternalServerError( - "ADMIN_API_SHARED_KEY not configured".into(), - ))); - }; - let provided = req - .headers() - .get("x-admin-api-key") - .and_then(|v| v.to_str().ok()) - .ok_or(ApiError(AppError::Unauthorized))?; - if provided != expected { - return Err(ApiError(AppError::Unauthorized)); - } - Ok(()) -} - -#[utoipa::path( - post, - path = "/api/admin/workspaces/{slug}/add-credit", - params( - ("slug" = String, Path, description = "Workspace slug") - ), - request_body = WorkspaceBillingAddCreditParams, - responses( - (status = 200, description = "Credit added", body = ApiResponse), - (status = 401, description = "Invalid or missing admin API key"), - (status = 404, description = "Workspace not found"), - (status = 400, description = "Invalid amount"), - ), - tag = "Admin" -)] -pub async fn admin_workspace_add_credit( - req: HttpRequest, - service: web::Data, - path: web::Path, - body: web::Json, -) -> Result { - validate_admin_key(&req)?; - let slug = path.into_inner(); - - if body.amount <= 0.0 { - return Err(ApiError(AppError::BadRequest("Amount must be positive".to_string()))); - } - - let ws = service.utils_find_workspace_by_slug(slug.clone()).await?; - let billing = service.ensure_workspace_billing(ws.id, None).await?; - let now_utc = Utc::now(); - - let new_balance = rust_decimal::Decimal::from_f64_retain( - billing.balance.to_f64().unwrap_or_default() + body.amount, - ) - .unwrap_or(rust_decimal::Decimal::ZERO); - - // Update billing balance (pk is workspace_id, mark unchanged) - let _ = models::workspaces::workspace_billing::ActiveModel { - workspace_id: sea_orm::ActiveValue::Unchanged(ws.id), - balance: sea_orm::ActiveValue::Set(new_balance), - updated_at: sea_orm::ActiveValue::Set(now_utc), - ..Default::default() - } - .update(&service.db) - .await; - - // Insert history record with user_id = NULL (admin action) - let reason = body.reason.clone().unwrap_or_else(|| "admin_credit".to_string()); - let extra = serde_json::json!({ "description": format!("Admin 手动充值: {}", reason) }); - let _ = models::workspaces::workspace_billing_history::ActiveModel { - uid: sea_orm::ActiveValue::Set(Uuid::now_v7()), - workspace_id: sea_orm::ActiveValue::Set(ws.id), - user_id: sea_orm::ActiveValue::Set(None), - amount: sea_orm::ActiveValue::Set( - rust_decimal::Decimal::from_f64_retain(body.amount) - .unwrap_or(rust_decimal::Decimal::ZERO), - ), - currency: sea_orm::ActiveValue::Set(billing.currency.clone()), - reason: sea_orm::ActiveValue::Set(reason), - extra: sea_orm::ActiveValue::Set(Some(extra)), - created_at: sea_orm::ActiveValue::Set(now_utc), - } - .insert(&service.db) - .await; - - let session = session::Session::no_op(); - let resp = service.workspace_billing_current(&session, slug).await?; - Ok(ApiResponse::ok(resp).to_response()) -} diff --git a/libs/api/admin/mod.rs b/libs/api/admin/mod.rs index db4459f..d1a7b73 100644 --- a/libs/api/admin/mod.rs +++ b/libs/api/admin/mod.rs @@ -1,12 +1,11 @@ //! Admin API endpoints — protected by `x-admin-api-key` header. //! -//! Only platform-wide operations remain. AI model management is handled -//! directly by the admin Next.js app via database access. +//! Only platform-wide operations remain. AI model management and billing +//! are handled directly by the admin Next.js app via database access. use actix_web::web; pub mod alerts; -pub mod billing; pub mod sync; pub fn init_admin_routes(cfg: &mut web::ServiceConfig) { @@ -14,11 +13,7 @@ pub fn init_admin_routes(cfg: &mut web::ServiceConfig) { web::scope("/api/admin") .route("/health", web::get().to(health_check)) .route("/ai/sync", web::post().to(sync::admin_sync_models)) - .route("/alerts/check", web::post().to(alerts::admin_check_alerts)) - .route( - "/workspaces/{slug}/add-credit", - web::post().to(billing::admin_workspace_add_credit), - ), + .route("/alerts/check", web::post().to(alerts::admin_check_alerts)), ); } diff --git a/libs/api/openapi.rs b/libs/api/openapi.rs index 4f902f5..0c51ebc 100644 --- a/libs/api/openapi.rs +++ b/libs/api/openapi.rs @@ -32,7 +32,6 @@ use utoipa::OpenApi; crate::auth::email::api_email_verify, // Agent crate::admin::sync::admin_sync_models, - crate::admin::billing::admin_workspace_add_credit, crate::admin::alerts::admin_check_alerts, // Agent (CRUD) crate::agent::code_review::trigger_code_review,