files_sdk/admin/
site.rs

1//! Site operations
2//!
3//! Site represents site-wide settings and configuration for your Files.com account.
4
5use crate::{FilesClient, Result};
6use serde::{Deserialize, Serialize};
7use serde_json::json;
8
9/// A Site entity (site-wide settings)
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct SiteEntity {
12    /// Site name
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub name: Option<String>,
15
16    /// Admin user ID
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub admin_user_id: Option<i64>,
19
20    /// Allowed IP addresses (whitelist)
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub allowed_ips: Option<String>,
23
24    /// Allowed countries
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub allowed_countries: Option<String>,
27
28    /// Disallowed countries
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub disallowed_countries: Option<String>,
31
32    /// Default time zone
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub default_time_zone: Option<String>,
35
36    /// Domain (custom domain)
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub domain: Option<String>,
39
40    /// Email (contact email)
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub email: Option<String>,
43
44    /// Session expiry (minutes)
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub session_expiry: Option<f64>,
47
48    /// SSL required
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub ssl_required: Option<bool>,
51
52    /// Subdomain
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub subdomain: Option<String>,
55
56    /// Welcome email enabled
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub welcome_email_enabled: Option<bool>,
59
60    /// User lockout enabled
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub user_lockout: Option<bool>,
63
64    /// User lockout tries
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub user_lockout_tries: Option<i64>,
67
68    /// User lockout within (seconds)
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub user_lockout_within: Option<i64>,
71
72    /// User lockout lock period (seconds)
73    #[serde(skip_serializing_if = "Option::is_none")]
74    pub user_lockout_lock_period: Option<i64>,
75
76    /// Require 2FA
77    #[serde(skip_serializing_if = "Option::is_none")]
78    pub require_2fa: Option<bool>,
79
80    /// Allowed 2FA methods
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub allowed_2fa_method_sms: Option<bool>,
83
84    #[serde(skip_serializing_if = "Option::is_none")]
85    pub allowed_2fa_method_totp: Option<bool>,
86
87    #[serde(skip_serializing_if = "Option::is_none")]
88    pub allowed_2fa_method_webauthn: Option<bool>,
89
90    #[serde(skip_serializing_if = "Option::is_none")]
91    pub allowed_2fa_method_yubi: Option<bool>,
92
93    /// Site currency
94    #[serde(skip_serializing_if = "Option::is_none")]
95    pub currency: Option<String>,
96
97    /// Session pinned by IP
98    #[serde(skip_serializing_if = "Option::is_none")]
99    pub session_pinned_by_ip: Option<bool>,
100
101    /// Bundle expiration (days)
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub bundle_expiration: Option<i64>,
104
105    /// Days to retain backups
106    #[serde(skip_serializing_if = "Option::is_none")]
107    pub days_to_retain_backups: Option<i64>,
108
109    /// Max prior passwords
110    #[serde(skip_serializing_if = "Option::is_none")]
111    pub max_prior_passwords: Option<i64>,
112
113    /// Password validity days
114    #[serde(skip_serializing_if = "Option::is_none")]
115    pub password_validity_days: Option<i64>,
116
117    /// Password min length
118    #[serde(skip_serializing_if = "Option::is_none")]
119    pub password_min_length: Option<i64>,
120
121    /// Password require letter
122    #[serde(skip_serializing_if = "Option::is_none")]
123    pub password_require_letter: Option<bool>,
124
125    /// Password require mixed case
126    #[serde(skip_serializing_if = "Option::is_none")]
127    pub password_require_mixed: Option<bool>,
128
129    /// Password require number
130    #[serde(skip_serializing_if = "Option::is_none")]
131    pub password_require_number: Option<bool>,
132
133    /// Password require special character
134    #[serde(skip_serializing_if = "Option::is_none")]
135    pub password_require_special: Option<bool>,
136
137    /// Password require unbreached
138    #[serde(skip_serializing_if = "Option::is_none")]
139    pub password_require_unbreached: Option<bool>,
140
141    /// SFTP user root enabled
142    #[serde(skip_serializing_if = "Option::is_none")]
143    pub sftp_user_root_enabled: Option<bool>,
144
145    /// Disable password reset
146    #[serde(skip_serializing_if = "Option::is_none")]
147    pub disable_password_reset: Option<bool>,
148
149    /// Site created at
150    #[serde(skip_serializing_if = "Option::is_none")]
151    pub created_at: Option<String>,
152
153    /// Custom namespace
154    #[serde(skip_serializing_if = "Option::is_none")]
155    pub custom_namespace: Option<bool>,
156}
157
158/// Site usage statistics
159#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct SiteUsageEntity {
161    /// Current storage usage (bytes)
162    #[serde(skip_serializing_if = "Option::is_none")]
163    pub current_storage: Option<i64>,
164
165    /// Deleted files count
166    #[serde(skip_serializing_if = "Option::is_none")]
167    pub deleted_files_counted_in_minimum: Option<i64>,
168
169    /// Deleted files storage (bytes)
170    #[serde(skip_serializing_if = "Option::is_none")]
171    pub deleted_files_storage: Option<i64>,
172
173    /// Root storage (bytes)
174    #[serde(skip_serializing_if = "Option::is_none")]
175    pub root_storage: Option<i64>,
176
177    /// Total billable transfer (bytes)
178    #[serde(skip_serializing_if = "Option::is_none")]
179    pub total_billable_transfer_used: Option<i64>,
180
181    /// Usage by top-level directory
182    #[serde(skip_serializing_if = "Option::is_none")]
183    pub usage_by_top_level_dir: Option<serde_json::Value>,
184}
185
186/// Handler for site operations
187pub struct SiteHandler {
188    client: FilesClient,
189}
190
191impl SiteHandler {
192    /// Create a new site handler
193    pub fn new(client: FilesClient) -> Self {
194        Self { client }
195    }
196
197    /// Get site settings
198    ///
199    /// # Example
200    /// ```no_run
201    /// use files_sdk::{FilesClient, SiteHandler};
202    ///
203    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
204    /// let client = FilesClient::builder().api_key("key").build()?;
205    /// let handler = SiteHandler::new(client);
206    /// let site = handler.get().await?;
207    /// println!("Site: {}", site.name.unwrap_or_default());
208    /// # Ok(())
209    /// # }
210    /// ```
211    pub async fn get(&self) -> Result<SiteEntity> {
212        let response = self.client.get_raw("/site").await?;
213        Ok(serde_json::from_value(response)?)
214    }
215
216    /// Get site usage statistics
217    ///
218    /// # Example
219    /// ```no_run
220    /// use files_sdk::{FilesClient, SiteHandler};
221    ///
222    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
223    /// let client = FilesClient::builder().api_key("key").build()?;
224    /// let handler = SiteHandler::new(client);
225    /// let usage = handler.get_usage().await?;
226    /// # Ok(())
227    /// # }
228    /// ```
229    pub async fn get_usage(&self) -> Result<SiteUsageEntity> {
230        let response = self.client.get_raw("/site/usage").await?;
231        Ok(serde_json::from_value(response)?)
232    }
233
234    /// Update site settings
235    ///
236    /// # Arguments
237    /// * `name` - Site name
238    /// * `subdomain` - Site subdomain
239    /// * `domain` - Custom domain
240    /// * `email` - Contact email
241    /// * `allowed_ips` - Allowed IP addresses (comma-separated)
242    /// * `require_2fa` - Require 2FA for all users
243    ///
244    /// # Returns
245    /// The updated site entity
246    #[allow(clippy::too_many_arguments)]
247    pub async fn update(
248        &self,
249        name: Option<&str>,
250        subdomain: Option<&str>,
251        domain: Option<&str>,
252        email: Option<&str>,
253        allowed_ips: Option<&str>,
254        require_2fa: Option<bool>,
255    ) -> Result<SiteEntity> {
256        let mut request_body = json!({});
257
258        if let Some(n) = name {
259            request_body["name"] = json!(n);
260        }
261        if let Some(s) = subdomain {
262            request_body["subdomain"] = json!(s);
263        }
264        if let Some(d) = domain {
265            request_body["domain"] = json!(d);
266        }
267        if let Some(e) = email {
268            request_body["email"] = json!(e);
269        }
270        if let Some(ips) = allowed_ips {
271            request_body["allowed_ips"] = json!(ips);
272        }
273        if let Some(r2fa) = require_2fa {
274            request_body["require_2fa"] = json!(r2fa);
275        }
276
277        let response = self.client.patch_raw("/site", request_body).await?;
278        Ok(serde_json::from_value(response)?)
279    }
280}
281
282#[cfg(test)]
283mod tests {
284    use super::*;
285
286    #[test]
287    fn test_handler_creation() {
288        let client = FilesClient::builder().api_key("test-key").build().unwrap();
289        let _handler = SiteHandler::new(client);
290    }
291}