Skip to main content

cbilling/
models.rs

1// Copyright 2025 OpenObserve Inc.
2// SPDX-License-Identifier: AGPL-3.0
3
4//! Data models for multi-cloud billing management
5
6use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8
9/// Cloud Account
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct Account {
12    pub id: i64,
13    pub org_id: String,
14    pub name: String,
15    pub provider: String, // aliyun, tencent, aws, azure, volcengine
16    pub account_id: String,
17    pub access_key: String,
18    pub secret_key: String,
19    pub region: Option<String>,
20    pub enabled: bool,
21    pub created_at: DateTime<Utc>,
22    pub updated_at: DateTime<Utc>,
23}
24
25/// Bill Record
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct Bill {
28    pub id: i64,
29    pub account_id: i64,
30    pub org_id: String,
31    pub billing_cycle: String,
32    pub amount: f64,
33    pub currency: String,
34    pub product_code: String,
35    pub product_name: Option<String>,
36    pub provider: Option<String>,
37    pub region: Option<String>,
38    pub instance_id: Option<String>,
39    pub project_id: Option<String>,
40    pub usage_amount: f64,
41    pub usage_unit: String,
42    pub deduction_amount: Option<f64>,
43    pub created_at: DateTime<Utc>,
44}
45
46/// Cost Overview for a billing cycle
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct CostOverview {
49    pub billing_cycle: String,
50    pub total_cost: f64,
51    pub account_count: i32,
52    pub accounts: Vec<AccountCost>,
53}
54
55/// Cost per account
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct AccountCost {
58    pub account_id: i64,
59    pub account_name: String,
60    pub provider: String,
61    pub cost: f64,
62    pub percentage: f64,
63}
64
65/// Billing Statistics
66#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct BillingStats {
68    pub month: String,
69    pub total_cost: f64,
70    pub service_cost: f64,
71    pub discount: f64,
72    pub monthly_recurring: f64,
73}
74
75/// Provider Configuration
76#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct ProviderConfig {
78    pub name: String,
79    pub display_name: String,
80    pub enabled: bool,
81    pub fields: Vec<ConfigField>,
82}
83
84/// Configuration field for a provider
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct ConfigField {
87    pub name: String,
88    pub label: String,
89    pub field_type: String, // text, password, select
90    pub required: bool,
91    pub options: Option<Vec<String>>,
92}
93
94/// Cloud Provider type
95#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
96#[serde(rename_all = "lowercase")]
97pub enum Provider {
98    Aliyun,
99    TencentCloud,
100    AWS,
101    Azure,
102    Volcengine,
103    UCloud,
104    Gcp,
105    Cloudflare,
106}
107
108impl Provider {
109    pub fn as_str(&self) -> &'static str {
110        match self {
111            Provider::Aliyun => "aliyun",
112            Provider::TencentCloud => "tencentcloud",
113            Provider::AWS => "aws",
114            Provider::Azure => "azure",
115            Provider::Volcengine => "volcengine",
116            Provider::UCloud => "ucloud",
117            Provider::Gcp => "gcp",
118            Provider::Cloudflare => "cloudflare",
119        }
120    }
121
122}
123
124impl std::str::FromStr for Provider {
125    type Err = String;
126
127    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
128        match s.to_lowercase().as_str() {
129            "aliyun" => Ok(Provider::Aliyun),
130            "tencentcloud" | "tencent" => Ok(Provider::TencentCloud),
131            "aws" => Ok(Provider::AWS),
132            "azure" => Ok(Provider::Azure),
133            "volcengine" => Ok(Provider::Volcengine),
134            "ucloud" => Ok(Provider::UCloud),
135            "gcp" | "google" | "googlecloud" => Ok(Provider::Gcp),
136            "cloudflare" | "cf" => Ok(Provider::Cloudflare),
137            _ => Err(format!("unknown provider: {}", s)),
138        }
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    #[test]
147    fn test_account_creation() {
148        let account = Account {
149            id: 1,
150            org_id: "org_123".to_string(),
151            name: "Production Account".to_string(),
152            provider: "aliyun".to_string(),
153            account_id: "1234567890".to_string(),
154            access_key: "test_key".to_string(),
155            secret_key: "test_secret".to_string(),
156            region: Some("cn-beijing".to_string()),
157            enabled: true,
158            created_at: Utc::now(),
159            updated_at: Utc::now(),
160        };
161
162        assert_eq!(account.id, 1);
163        assert_eq!(account.provider, "aliyun");
164        assert!(account.enabled);
165    }
166
167    #[test]
168    fn test_provider_enum() {
169        assert_eq!(Provider::Aliyun.as_str(), "aliyun");
170        assert_eq!("aws".parse::<Provider>().unwrap(), Provider::AWS);
171        assert_eq!("tencent".parse::<Provider>().unwrap(), Provider::TencentCloud);
172        assert!("unknown".parse::<Provider>().is_err());
173    }
174}