threatflux_core/
models.rs

1use std::{collections::HashMap, str::FromStr};
2
3use chrono::{DateTime, Duration, Utc};
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use utoipa::{IntoParams, ToSchema};
7use uuid::Uuid;
8use validator::{Validate, ValidationError};
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
11#[serde(rename_all = "snake_case")]
12pub enum AuthProvider {
13    Local,
14    OAuth { provider: String },
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
18pub struct CreateUserRequest {
19    #[validate(length(min = 3, max = 32))]
20    pub username: String,
21    #[validate(email)]
22    pub email: String,
23    #[validate(length(min = 12, max = 128))]
24    pub password: String,
25    #[validate(length(max = 64))]
26    pub display_name: Option<String>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
30pub struct LoginRequest {
31    #[validate(length(min = 1, max = 320))]
32    pub username_or_email: String,
33    #[validate(length(min = 1, max = 128))]
34    pub password: String,
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
38pub struct RefreshTokenRequest {
39    #[validate(length(min = 32, max = 128))]
40    pub refresh_token: String,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
44pub struct UpdateUserRequest {
45    #[validate(length(min = 1, max = 64))]
46    pub display_name: Option<String>,
47    #[validate(length(min = 1, max = 512))]
48    pub bio: Option<String>,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
52pub struct User {
53    pub id: Uuid,
54    pub username: String,
55    pub email: String,
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub display_name: Option<String>,
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub bio: Option<String>,
60    #[serde(default, skip_serializing)]
61    #[schema(ignore)]
62    pub roles: Vec<String>,
63    #[serde(default)]
64    pub groups: Vec<String>,
65    pub created_at: DateTime<Utc>,
66    pub updated_at: DateTime<Utc>,
67    #[serde(skip_serializing_if = "Option::is_none")]
68    pub last_login: Option<DateTime<Utc>>,
69    pub is_active: bool,
70    pub is_verified: bool,
71    pub provider: AuthProvider,
72}
73
74impl User {
75    #[must_use]
76    pub fn effective_roles(&self) -> &[String] {
77        if self.roles.is_empty() {
78            &self.groups
79        } else {
80            &self.roles
81        }
82    }
83
84    #[must_use]
85    pub fn has_role(&self, role: &str) -> bool {
86        self.effective_roles()
87            .iter()
88            .any(|value| value.eq_ignore_ascii_case(role))
89    }
90
91    #[must_use]
92    pub fn has_any_role(&self, roles: &[&str]) -> bool {
93        self.effective_roles()
94            .iter()
95            .any(|value| roles.iter().any(|role| value.eq_ignore_ascii_case(role)))
96    }
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
100#[allow(clippy::struct_excessive_bools)]
101pub struct AdminUserSummary {
102    pub id: Uuid,
103    pub username: String,
104    pub email: String,
105    #[serde(skip_serializing_if = "Option::is_none")]
106    pub display_name: Option<String>,
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub bio: Option<String>,
109    #[serde(default, skip_serializing)]
110    #[schema(ignore)]
111    pub roles: Vec<String>,
112    #[serde(default)]
113    pub groups: Vec<String>,
114    pub created_at: DateTime<Utc>,
115    pub updated_at: DateTime<Utc>,
116    #[serde(skip_serializing_if = "Option::is_none")]
117    pub last_login: Option<DateTime<Utc>>,
118    pub is_active: bool,
119    pub is_verified: bool,
120    pub provider: AuthProvider,
121    pub is_admin: bool,
122    pub can_manage_api_keys: bool,
123}
124
125impl AdminUserSummary {
126    #[must_use]
127    pub fn from_user(user: &User) -> Self {
128        let is_admin = user.has_role("admin");
129        let can_manage_api_keys = user.has_role("api_admin");
130
131        Self {
132            id: user.id,
133            username: user.username.clone(),
134            email: user.email.clone(),
135            display_name: user.display_name.clone(),
136            bio: user.bio.clone(),
137            roles: user.effective_roles().to_vec(),
138            groups: user.effective_roles().to_vec(),
139            created_at: user.created_at,
140            updated_at: user.updated_at,
141            last_login: user.last_login,
142            is_active: user.is_active,
143            is_verified: user.is_verified,
144            provider: user.provider.clone(),
145            is_admin,
146            can_manage_api_keys,
147        }
148    }
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
152pub struct AdminUserListResponse {
153    pub users: Vec<AdminUserSummary>,
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
157pub struct UpdateUserAccessRequest {
158    pub is_admin: Option<bool>,
159    pub can_manage_api_keys: Option<bool>,
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
163pub struct TokenResponse {
164    pub access_token: String,
165    pub refresh_token: String,
166    pub token_type: String,
167    pub expires_in: u64,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
171pub struct ProviderTokenResponse {
172    pub access_token: String,
173    pub token_type: String,
174    pub expires_in: u64,
175    #[serde(skip_serializing_if = "Option::is_none")]
176    pub refresh_token: Option<String>,
177    #[serde(skip_serializing_if = "Option::is_none")]
178    pub id_token: Option<String>,
179    #[serde(skip_serializing_if = "Option::is_none")]
180    pub scope: Option<String>,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
184#[serde(rename_all = "snake_case")]
185pub enum AgentStatus {
186    Online,
187    Offline,
188    Draining,
189}
190
191#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
192pub struct AgentInputCapability {
193    #[serde(default)]
194    pub keyboard: bool,
195    #[serde(default)]
196    pub mouse: bool,
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
200pub struct ScreenCaptureCapability {
201    pub supported: bool,
202    #[serde(default, skip_serializing_if = "Vec::is_empty")]
203    pub formats: Vec<String>,
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
207pub struct Capabilities {
208    #[serde(skip_serializing_if = "Option::is_none")]
209    pub os: Option<String>,
210    #[serde(default, skip_serializing_if = "Vec::is_empty")]
211    pub tasks: Vec<String>,
212    #[serde(skip_serializing_if = "Option::is_none")]
213    pub input: Option<AgentInputCapability>,
214    #[serde(skip_serializing_if = "Option::is_none")]
215    pub screen_capture: Option<ScreenCaptureCapability>,
216    #[serde(skip_serializing_if = "Option::is_none")]
217    pub max_concurrency: Option<u32>,
218    #[serde(skip_serializing_if = "Option::is_none")]
219    pub version: Option<String>,
220}
221
222#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
223pub struct CpuInfo {
224    #[serde(skip_serializing_if = "Option::is_none")]
225    pub model: Option<String>,
226    pub logical_cores: usize,
227    #[serde(skip_serializing_if = "Option::is_none")]
228    pub physical_cores: Option<usize>,
229    #[serde(skip_serializing_if = "Option::is_none")]
230    pub usage_percent: Option<f64>,
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
234pub struct MemoryInfo {
235    pub total_bytes: u64,
236    pub available_bytes: u64,
237}
238
239#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
240pub struct GpuInfo {
241    pub name: String,
242    #[serde(skip_serializing_if = "Option::is_none")]
243    pub memory_total_bytes: Option<u64>,
244    #[serde(skip_serializing_if = "Option::is_none")]
245    pub memory_used_bytes: Option<u64>,
246}
247
248#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
249pub struct DiskInfo {
250    pub name: String,
251    #[serde(skip_serializing_if = "Option::is_none")]
252    pub mount_point: Option<String>,
253    pub total_bytes: u64,
254    pub available_bytes: u64,
255    #[serde(skip_serializing_if = "Option::is_none")]
256    pub filesystem: Option<String>,
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
260pub struct AgentSystemInfo {
261    pub cpu: CpuInfo,
262    pub memory: MemoryInfo,
263    #[serde(default, skip_serializing_if = "Vec::is_empty")]
264    pub gpus: Vec<GpuInfo>,
265    #[serde(default, skip_serializing_if = "Vec::is_empty")]
266    pub disks: Vec<DiskInfo>,
267}
268
269#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
270pub struct AgentSystemSnapshot {
271    pub captured_at: DateTime<Utc>,
272    pub system: AgentSystemInfo,
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
276pub struct VncTarget {
277    /// Stable identifier used when requesting sessions (e.g., dc01, win10)
278    pub id: String,
279    /// Human-friendly label for display purposes.
280    pub name: String,
281    /// Base URL for the noVNC web UI (e.g., `http://node:30060`).
282    pub url: String,
283    #[serde(skip_serializing_if = "Option::is_none")]
284    pub notes: Option<String>,
285}
286
287#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
288#[serde(rename_all = "snake_case")]
289pub enum VncSessionState {
290    Pending,
291    Active,
292    Revoked,
293    Expired,
294}
295
296#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
297pub struct VncSession {
298    pub id: Uuid,
299    pub target_id: String,
300    pub state: VncSessionState,
301    pub created_at: DateTime<Utc>,
302    pub expires_at: DateTime<Utc>,
303    /// Direct URL to launch noVNC for this target (host-managed, not agent-routed).
304    /// Always includes an http(s) scheme and points at `vnc.html` with autoconnect params.
305    #[serde(skip_serializing_if = "Option::is_none")]
306    pub connect_url: Option<String>,
307    /// Optional per-session viewer password (if the upstream viewer honors it).
308    #[serde(skip_serializing_if = "Option::is_none")]
309    pub password: Option<String>,
310    #[serde(skip_serializing_if = "Option::is_none")]
311    pub token: Option<String>,
312    #[serde(skip_serializing_if = "Option::is_none")]
313    pub created_by: Option<Uuid>,
314}
315
316#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
317pub struct CreateVncSessionRequest {
318    #[validate(length(min = 1, max = 128))]
319    pub target_id: String,
320    /// Optional explicit duration in minutes (default provided by API config).
321    #[serde(default)]
322    pub duration_minutes: Option<u64>,
323    #[serde(skip_serializing_if = "Option::is_none", default)]
324    #[validate(length(max = 64))]
325    pub password: Option<String>,
326}
327
328#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
329pub struct Agent {
330    pub id: Uuid,
331    pub name: String,
332    pub status: AgentStatus,
333    pub last_seen: DateTime<Utc>,
334    #[serde(skip_serializing_if = "Option::is_none")]
335    pub load: Option<f64>,
336    #[serde(skip_serializing_if = "Option::is_none")]
337    pub queue_depth: Option<u32>,
338    pub capabilities: Capabilities,
339    #[serde(default)]
340    pub metadata: HashMap<String, Value>,
341    #[serde(skip_serializing_if = "Option::is_none")]
342    pub system: Option<AgentSystemInfo>,
343}
344
345#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
346pub struct RegisterAgentRequest {
347    #[validate(length(min = 1, max = 128))]
348    pub name: String,
349    pub capabilities: Capabilities,
350    #[serde(default)]
351    pub metadata: HashMap<String, Value>,
352}
353
354#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
355pub struct AgentWithToken {
356    #[serde(flatten)]
357    pub agent: Agent,
358    pub token: String,
359}
360
361#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
362pub struct UpdateAgentRequest {
363    #[validate(length(min = 1, max = 128))]
364    #[serde(skip_serializing_if = "Option::is_none")]
365    pub name: Option<String>,
366    #[serde(skip_serializing_if = "Option::is_none")]
367    pub status: Option<AgentStatus>,
368    #[serde(skip_serializing_if = "Option::is_none")]
369    pub metadata: Option<HashMap<String, Value>>,
370}
371
372#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
373pub struct Heartbeat {
374    #[serde(skip_serializing_if = "Option::is_none")]
375    pub status: Option<AgentStatus>,
376    #[serde(skip_serializing_if = "Option::is_none")]
377    pub load: Option<f64>,
378    #[serde(skip_serializing_if = "Option::is_none")]
379    pub queue_depth: Option<u32>,
380    #[serde(skip_serializing_if = "Option::is_none")]
381    pub system: Option<AgentSystemInfo>,
382}
383
384#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
385#[serde(rename_all = "snake_case")]
386pub enum AgentInstallChannel {
387    Ssh,
388    Wmi,
389}
390
391#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
392#[serde(rename_all = "snake_case")]
393pub enum DeploymentStatus {
394    Pending,
395    Running,
396    Succeeded,
397    Failed,
398}
399
400#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
401pub struct DeploymentLog {
402    pub ts: DateTime<Utc>,
403    pub level: String,
404    pub message: String,
405}
406
407#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, Validate)]
408pub struct DeploymentAgentConfig {
409    #[validate(length(min = 1, max = 128))]
410    #[serde(skip_serializing_if = "Option::is_none")]
411    pub name: Option<String>,
412    pub capabilities: Capabilities,
413    #[serde(default)]
414    pub metadata: HashMap<String, Value>,
415}
416
417#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, Default)]
418pub struct DeploymentInstallOptions {
419    #[serde(skip_serializing_if = "Option::is_none")]
420    pub download_url: Option<String>,
421    #[serde(skip_serializing_if = "Option::is_none")]
422    pub install_path: Option<String>,
423    #[serde(skip_serializing_if = "Option::is_none")]
424    pub bootstrap_script: Option<String>,
425}
426
427#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
428pub struct SshDeployment {
429    #[validate(length(min = 1, max = 255))]
430    pub host: String,
431    #[serde(default = "SshDeployment::default_port")]
432    pub port: u16,
433    #[validate(length(min = 1, max = 64))]
434    pub username: String,
435    #[serde(skip_serializing_if = "Option::is_none")]
436    pub private_key: Option<String>,
437    #[serde(skip_serializing_if = "Option::is_none")]
438    pub password: Option<String>,
439    #[serde(skip_serializing_if = "Option::is_none")]
440    pub install_path: Option<String>,
441}
442
443impl SshDeployment {
444    #[must_use]
445    const fn default_port() -> u16 {
446        22
447    }
448}
449
450#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
451pub struct WmiDeployment {
452    #[validate(length(min = 1, max = 255))]
453    pub host: String,
454    #[validate(length(min = 1, max = 128))]
455    pub username: String,
456    #[validate(length(min = 1, max = 256))]
457    pub password: String,
458    #[serde(skip_serializing_if = "Option::is_none")]
459    pub namespace: Option<String>,
460}
461
462#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
463pub struct CreateAgentDeploymentRequest {
464    pub channel: AgentInstallChannel,
465    #[validate(nested)]
466    #[serde(skip_serializing_if = "Option::is_none")]
467    pub ssh: Option<SshDeployment>,
468    #[validate(nested)]
469    #[serde(skip_serializing_if = "Option::is_none")]
470    pub wmi: Option<WmiDeployment>,
471    #[validate(nested)]
472    pub agent: DeploymentAgentConfig,
473    #[serde(skip_serializing_if = "Option::is_none")]
474    pub install: Option<DeploymentInstallOptions>,
475}
476
477#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
478pub struct AgentDeploymentTarget {
479    pub host: String,
480    #[serde(skip_serializing_if = "Option::is_none")]
481    pub port: Option<u16>,
482    #[serde(skip_serializing_if = "Option::is_none")]
483    pub username: Option<String>,
484    #[serde(skip_serializing_if = "Option::is_none")]
485    pub transport: Option<String>,
486}
487
488#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
489pub struct AgentDeployment {
490    pub id: Uuid,
491    pub channel: AgentInstallChannel,
492    pub target: AgentDeploymentTarget,
493    pub status: DeploymentStatus,
494    #[serde(skip_serializing_if = "Option::is_none")]
495    pub status_message: Option<String>,
496    pub created_at: DateTime<Utc>,
497    pub updated_at: DateTime<Utc>,
498    #[serde(skip_serializing_if = "Option::is_none")]
499    pub agent_id: Option<Uuid>,
500    #[serde(skip_serializing_if = "Option::is_none")]
501    pub agent_token: Option<String>,
502    #[serde(skip_serializing_if = "Option::is_none")]
503    pub install_path: Option<String>,
504    #[serde(default, skip_serializing_if = "Vec::is_empty")]
505    pub logs: Vec<DeploymentLog>,
506}
507
508#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, ToSchema)]
509#[serde(rename_all = "snake_case")]
510pub enum OAuthGrantType {
511    AuthorizationCode,
512    RefreshToken,
513    ClientCredentials,
514}
515
516impl OAuthGrantType {
517    #[must_use]
518    pub const fn as_str(&self) -> &'static str {
519        match self {
520            Self::AuthorizationCode => "authorization_code",
521            Self::RefreshToken => "refresh_token",
522            Self::ClientCredentials => "client_credentials",
523        }
524    }
525}
526
527impl FromStr for OAuthGrantType {
528    type Err = ();
529
530    fn from_str(value: &str) -> Result<Self, Self::Err> {
531        match value {
532            "authorization_code" | "code" => Ok(Self::AuthorizationCode),
533            "refresh_token" => Ok(Self::RefreshToken),
534            "client_credentials" => Ok(Self::ClientCredentials),
535            _ => Err(()),
536        }
537    }
538}
539
540#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
541#[serde(rename_all = "lowercase")]
542pub enum CodeChallengeMethod {
543    Plain,
544    S256,
545}
546
547impl CodeChallengeMethod {
548    #[must_use]
549    pub const fn as_str(&self) -> &'static str {
550        match self {
551            Self::Plain => "plain",
552            Self::S256 => "S256",
553        }
554    }
555}
556
557impl FromStr for CodeChallengeMethod {
558    type Err = ();
559
560    fn from_str(value: &str) -> Result<Self, Self::Err> {
561        match value {
562            "plain" => Ok(Self::Plain),
563            "S256" | "s256" => Ok(Self::S256),
564            _ => Err(()),
565        }
566    }
567}
568
569#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
570pub struct OAuthClient {
571    pub client_id: String,
572    #[serde(skip_serializing_if = "Option::is_none")]
573    pub name: Option<String>,
574    pub redirect_uris: Vec<String>,
575    pub allowed_scopes: Vec<String>,
576    pub allowed_grant_types: Vec<OAuthGrantType>,
577    pub is_confidential: bool,
578    pub is_active: bool,
579    #[serde(skip_serializing_if = "Option::is_none")]
580    pub secret_preview: Option<String>,
581    pub created_at: DateTime<Utc>,
582    pub updated_at: DateTime<Utc>,
583}
584
585#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
586pub struct OAuthClientWithSecret {
587    pub client: OAuthClient,
588    #[serde(skip_serializing_if = "Option::is_none")]
589    pub client_secret: Option<String>,
590}
591
592#[derive(Debug, Clone)]
593pub struct StoredOAuthClient {
594    pub client: OAuthClient,
595    pub secret_hash: Option<String>,
596}
597
598#[derive(Debug, Clone)]
599pub struct NewOAuthClient {
600    pub client_id: String,
601    pub name: Option<String>,
602    pub secret_hash: Option<String>,
603    pub secret_preview: Option<String>,
604    pub redirect_uris: Vec<String>,
605    pub allowed_scopes: Vec<String>,
606    pub allowed_grant_types: Vec<OAuthGrantType>,
607    pub is_confidential: bool,
608    pub is_active: bool,
609    pub created_at: DateTime<Utc>,
610    pub updated_at: DateTime<Utc>,
611}
612
613#[derive(Debug, Clone)]
614pub struct OAuthClientUpdate {
615    pub name: Option<String>,
616    pub redirect_uris: Option<Vec<String>>,
617    pub allowed_scopes: Option<Vec<String>>,
618    pub allowed_grant_types: Option<Vec<OAuthGrantType>>,
619    pub is_confidential: Option<bool>,
620    pub is_active: Option<bool>,
621    pub secret_hash: Option<String>,
622    pub secret_preview: Option<String>,
623    pub updated_at: DateTime<Utc>,
624}
625
626#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
627pub struct OAuthClientListResponse {
628    pub clients: Vec<OAuthClient>,
629}
630
631#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
632pub struct CreateOAuthClientRequest {
633    #[validate(length(min = 3, max = 96))]
634    pub name: Option<String>,
635    #[validate(length(min = 8, max = 96))]
636    pub client_id: Option<String>,
637    #[validate(length(min = 1))]
638    pub redirect_uris: Vec<String>,
639    #[validate(length(min = 1))]
640    pub allowed_scopes: Vec<String>,
641    #[validate(length(min = 1))]
642    pub allowed_grant_types: Vec<OAuthGrantType>,
643    pub is_confidential: Option<bool>,
644    #[validate(length(min = 16, max = 256))]
645    pub client_secret: Option<String>,
646    pub is_active: Option<bool>,
647}
648
649#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
650pub struct UpdateOAuthClientRequest {
651    #[validate(length(min = 3, max = 96))]
652    pub name: Option<String>,
653    #[validate(length(min = 1))]
654    pub redirect_uris: Option<Vec<String>>,
655    #[validate(length(min = 1))]
656    pub allowed_scopes: Option<Vec<String>>,
657    #[validate(length(min = 1))]
658    pub allowed_grant_types: Option<Vec<OAuthGrantType>>,
659    pub is_confidential: Option<bool>,
660    pub is_active: Option<bool>,
661}
662
663#[derive(Debug, Clone)]
664pub struct OAuthAuthorizationCode {
665    pub code: String,
666    pub client_id: String,
667    pub user_id: Uuid,
668    pub redirect_uri: String,
669    pub scope: Vec<String>,
670    pub code_challenge: Option<String>,
671    pub code_challenge_method: Option<CodeChallengeMethod>,
672    pub nonce: Option<String>,
673    pub created_at: DateTime<Utc>,
674    pub expires_at: DateTime<Utc>,
675}
676
677#[derive(Debug, Clone)]
678pub struct OAuthRefreshTokenData {
679    pub fingerprint: String,
680    pub token_hash: String,
681    pub client_id: String,
682    pub user_id: Option<Uuid>,
683    pub scope: Vec<String>,
684    pub created_at: DateTime<Utc>,
685    pub expires_at: DateTime<Utc>,
686}
687
688#[derive(Debug, Deserialize, Serialize, IntoParams, ToSchema)]
689#[serde(rename_all = "snake_case")]
690pub struct AuthorizationRequest {
691    pub response_type: Option<String>,
692    pub client_id: String,
693    pub redirect_uri: Option<String>,
694    pub scope: Option<String>,
695    pub state: Option<String>,
696    pub code_challenge: Option<String>,
697    pub code_challenge_method: Option<String>,
698    pub nonce: Option<String>,
699}
700
701#[derive(Debug, Deserialize, Serialize, ToSchema)]
702#[serde(rename_all = "snake_case")]
703pub struct TokenRequest {
704    pub grant_type: String,
705    pub code: Option<String>,
706    pub redirect_uri: Option<String>,
707    pub code_verifier: Option<String>,
708    pub refresh_token: Option<String>,
709    pub scope: Option<String>,
710    pub client_id: Option<String>,
711    pub client_secret: Option<String>,
712}
713
714#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
715#[serde(rename_all = "snake_case")]
716pub enum TaskKind {
717    Command,
718    Keyboard,
719    Mouse,
720    Capture,
721    SystemInfo,
722    FilesystemCrawl,
723    Custom,
724}
725
726#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
727#[serde(rename_all = "snake_case")]
728pub enum TaskState {
729    Queued,
730    Assigned,
731    Running,
732    WaitingInput,
733    Cancelling,
734    Cancelled,
735    Succeeded,
736    Failed,
737}
738
739#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
740pub struct TaskStatus {
741    pub state: TaskState,
742    #[serde(default, skip_serializing_if = "Option::is_none")]
743    pub progress: Option<f64>,
744    #[serde(skip_serializing_if = "Option::is_none")]
745    pub step: Option<String>,
746    #[serde(skip_serializing_if = "Option::is_none")]
747    pub message: Option<String>,
748    #[serde(skip_serializing_if = "Option::is_none")]
749    pub updated_at: Option<DateTime<Utc>>,
750    #[serde(skip_serializing_if = "Option::is_none")]
751    pub error: Option<String>,
752}
753
754impl TaskStatus {
755    #[must_use]
756    pub fn queued(now: DateTime<Utc>) -> Self {
757        Self {
758            state: TaskState::Queued,
759            progress: Some(0.0),
760            step: None,
761            message: None,
762            updated_at: Some(now),
763            error: None,
764        }
765    }
766
767    #[must_use]
768    pub fn with_state(mut self, state: TaskState, now: DateTime<Utc>) -> Self {
769        self.state = state;
770        self.updated_at = Some(now);
771        self
772    }
773}
774
775#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
776pub struct CreateTaskRequest {
777    pub kind: TaskKind,
778    #[serde(skip_serializing_if = "Option::is_none")]
779    pub agent_id: Option<Uuid>,
780    #[serde(skip_serializing_if = "Option::is_none")]
781    pub run_id: Option<Uuid>,
782    #[serde(default)]
783    pub payload: Value,
784    #[serde(default)]
785    pub priority: i32,
786    #[serde(skip_serializing_if = "Option::is_none")]
787    pub deadline: Option<DateTime<Utc>>,
788}
789
790#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
791pub struct AgentTask {
792    pub id: Uuid,
793    #[serde(skip_serializing_if = "Option::is_none")]
794    pub agent_id: Option<Uuid>,
795    #[serde(skip_serializing_if = "Option::is_none")]
796    pub run_id: Option<Uuid>,
797    #[serde(skip_serializing_if = "Option::is_none")]
798    pub agent_info: Option<AgentSystemInfo>,
799    pub kind: TaskKind,
800    pub payload: Value,
801    pub priority: i32,
802    pub status: TaskStatus,
803    pub created_at: DateTime<Utc>,
804    pub updated_at: DateTime<Utc>,
805    #[serde(skip_serializing_if = "Option::is_none")]
806    pub deadline: Option<DateTime<Utc>>,
807    #[serde(skip_serializing_if = "Option::is_none")]
808    pub result: Option<TaskResult>,
809    #[serde(default, skip_serializing_if = "Vec::is_empty")]
810    pub artifacts: Vec<Artifact>,
811}
812
813#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
814pub struct TaskStatusUpdate {
815    #[serde(skip_serializing_if = "Option::is_none")]
816    pub state: Option<TaskState>,
817    #[serde(skip_serializing_if = "Option::is_none")]
818    pub progress: Option<f64>,
819    #[serde(skip_serializing_if = "Option::is_none")]
820    pub step: Option<String>,
821    #[serde(skip_serializing_if = "Option::is_none")]
822    pub message: Option<String>,
823    #[serde(skip_serializing_if = "Option::is_none")]
824    pub error: Option<String>,
825}
826
827#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
828pub struct CancelRequest {
829    #[serde(skip_serializing_if = "Option::is_none")]
830    pub reason: Option<String>,
831}
832
833#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
834pub struct PollRequest {
835    #[serde(default = "PollRequest::default_max_tasks")]
836    pub max_tasks: u32,
837    #[serde(default = "PollRequest::default_wait_seconds")]
838    pub wait_seconds: u64,
839}
840
841impl PollRequest {
842    const fn default_max_tasks() -> u32 {
843        1
844    }
845
846    const fn default_wait_seconds() -> u64 {
847        15
848    }
849}
850
851#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, Default)]
852pub struct SystemInfoRequest {
853    #[serde(default)]
854    pub include_usage: bool,
855}
856
857#[allow(clippy::struct_excessive_bools)]
858#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, Default)]
859pub struct HashingRequest {
860    #[serde(default)]
861    pub md5: bool,
862    #[serde(default)]
863    pub sha256: bool,
864    #[serde(default)]
865    pub sha512: bool,
866    #[serde(default)]
867    pub blake3: bool,
868    #[serde(skip_serializing_if = "Option::is_none")]
869    pub max_concurrent: Option<u32>,
870    #[serde(skip_serializing_if = "Option::is_none")]
871    pub buffer_size: Option<u32>,
872}
873
874#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, Default)]
875pub struct FileHashes {
876    #[serde(skip_serializing_if = "Option::is_none")]
877    pub md5: Option<String>,
878    #[serde(skip_serializing_if = "Option::is_none")]
879    pub sha256: Option<String>,
880    #[serde(skip_serializing_if = "Option::is_none")]
881    pub sha512: Option<String>,
882    #[serde(skip_serializing_if = "Option::is_none")]
883    pub blake3: Option<String>,
884}
885
886#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema, Default)]
887#[serde(rename_all = "snake_case")]
888pub enum FileVisibility {
889    #[default]
890    Private,
891    Shared,
892    Public,
893}
894
895#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
896pub struct CreateFileRequest {
897    #[validate(length(min = 1, max = 255))]
898    pub name: String,
899    #[validate(length(max = 1024))]
900    pub description: Option<String>,
901    #[validate(length(max = 256))]
902    pub content_type: Option<String>,
903    #[serde(default)]
904    pub visibility: FileVisibility,
905    #[serde(default)]
906    pub shared_with: Vec<Uuid>,
907    #[serde(skip_serializing_if = "Option::is_none")]
908    pub size_bytes: Option<i64>,
909    #[serde(skip_serializing_if = "Option::is_none")]
910    pub data_base64: Option<String>,
911    #[serde(skip_serializing_if = "Option::is_none")]
912    pub hashes: Option<FileHashes>,
913}
914
915#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
916pub struct CaptureFileRequest {
917    #[validate(length(min = 1, max = 255))]
918    pub name: String,
919    #[validate(length(max = 1024))]
920    pub description: Option<String>,
921    #[validate(length(max = 256))]
922    pub content_type: Option<String>,
923    #[serde(default)]
924    pub visibility: FileVisibility,
925    #[serde(default)]
926    pub shared_with: Vec<Uuid>,
927    #[serde(skip_serializing_if = "Option::is_none")]
928    pub size_bytes: Option<i64>,
929    #[serde(skip_serializing_if = "Option::is_none")]
930    pub data_base64: Option<String>,
931    #[serde(skip_serializing_if = "Option::is_none")]
932    pub hashes: Option<FileHashes>,
933}
934
935#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema, Default)]
936pub struct UpdateFileRequest {
937    #[serde(skip_serializing_if = "Option::is_none")]
938    #[validate(length(min = 1, max = 255))]
939    pub name: Option<String>,
940    #[serde(skip_serializing_if = "Option::is_none")]
941    #[validate(length(max = 1024))]
942    pub description: Option<String>,
943    #[serde(skip_serializing_if = "Option::is_none")]
944    pub visibility: Option<FileVisibility>,
945    #[serde(skip_serializing_if = "Option::is_none")]
946    pub shared_with: Option<Vec<Uuid>>,
947}
948
949#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
950pub struct FileMetadata {
951    pub id: Uuid,
952    pub owner_id: Uuid,
953    pub name: String,
954    #[serde(skip_serializing_if = "Option::is_none")]
955    pub description: Option<String>,
956    #[serde(skip_serializing_if = "Option::is_none")]
957    pub content_type: Option<String>,
958    pub size_bytes: i64,
959    pub visibility: FileVisibility,
960    #[serde(skip_serializing_if = "Option::is_none")]
961    pub hashes: Option<FileHashes>,
962    #[serde(default, skip_serializing_if = "Vec::is_empty")]
963    pub shared_with: Vec<Uuid>,
964    #[serde(skip_serializing_if = "Option::is_none")]
965    pub download_url: Option<String>,
966    pub created_at: DateTime<Utc>,
967    pub updated_at: DateTime<Utc>,
968}
969
970#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
971pub struct FileDownloadUrl {
972    pub url: String,
973    pub expires_in_seconds: u64,
974}
975
976#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
977#[serde(rename_all = "snake_case")]
978pub enum FilePermission {
979    Read,
980    Write,
981    Delete,
982    Admin,
983}
984
985impl std::str::FromStr for FilePermission {
986    type Err = ValidationError;
987
988    fn from_str(value: &str) -> Result<Self, Self::Err> {
989        match value.to_ascii_lowercase().as_str() {
990            "read" => Ok(Self::Read),
991            "write" => Ok(Self::Write),
992            "delete" => Ok(Self::Delete),
993            "admin" => Ok(Self::Admin),
994            _ => Err(ValidationError::new("file_permission")),
995        }
996    }
997}
998
999impl std::fmt::Display for FilePermission {
1000    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1001        let value = match self {
1002            Self::Read => "read",
1003            Self::Write => "write",
1004            Self::Delete => "delete",
1005            Self::Admin => "admin",
1006        };
1007        write!(f, "{value}")
1008    }
1009}
1010
1011#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
1012#[validate(schema(function = "validate_grant_file_permission"))]
1013pub struct GrantFilePermissionRequest {
1014    #[serde(skip_serializing_if = "Option::is_none")]
1015    pub user_id: Option<Uuid>,
1016    #[serde(skip_serializing_if = "Option::is_none")]
1017    pub team_id: Option<Uuid>,
1018    #[serde(skip_serializing_if = "Option::is_none")]
1019    #[validate(length(min = 1, max = 128))]
1020    pub role: Option<String>,
1021    pub permission: FilePermission,
1022    #[serde(skip_serializing_if = "Option::is_none")]
1023    pub expires_at: Option<DateTime<Utc>>,
1024}
1025
1026#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1027pub struct FilePermissionResponse {
1028    pub id: Uuid,
1029    pub file_id: Uuid,
1030    #[serde(skip_serializing_if = "Option::is_none")]
1031    pub user_id: Option<Uuid>,
1032    #[serde(skip_serializing_if = "Option::is_none")]
1033    pub team_id: Option<Uuid>,
1034    #[serde(skip_serializing_if = "Option::is_none")]
1035    pub role: Option<String>,
1036    pub permission: FilePermission,
1037    pub granted_by: Uuid,
1038    pub granted_at: DateTime<Utc>,
1039    #[serde(skip_serializing_if = "Option::is_none")]
1040    pub expires_at: Option<DateTime<Utc>>,
1041}
1042
1043#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1044pub struct FileDownloadResponse {
1045    pub downloaded_by: Uuid,
1046    pub downloaded_at: DateTime<Utc>,
1047    pub download_size: i64,
1048    #[serde(skip_serializing_if = "Option::is_none")]
1049    pub user_agent: Option<String>,
1050}
1051
1052#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1053pub struct FileStatsResponse {
1054    pub total_downloads: i64,
1055    pub total_download_size: i64,
1056    pub unique_downloaders: i64,
1057    pub recent_downloads: Vec<FileDownloadResponse>,
1058}
1059
1060#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
1061pub struct UploadFileRequest {
1062    #[validate(length(min = 1, max = 255))]
1063    pub filename: String,
1064    #[validate(length(min = 1, max = 256))]
1065    pub content_type: String,
1066    #[validate(range(min = 1))]
1067    pub file_size: i64,
1068    #[serde(skip_serializing_if = "Option::is_none")]
1069    #[validate(length(max = 1024))]
1070    pub description: Option<String>,
1071    #[serde(skip_serializing_if = "Option::is_none")]
1072    pub visibility: Option<FileVisibility>,
1073}
1074
1075#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1076pub struct PresignedUrlResponse {
1077    pub upload_url: String,
1078    pub expires_at: DateTime<Utc>,
1079    pub file_id: Uuid,
1080}
1081
1082#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
1083pub struct StartMultipartUploadRequest {
1084    #[validate(length(min = 1, max = 255))]
1085    pub filename: String,
1086    #[validate(length(min = 1, max = 256))]
1087    pub content_type: String,
1088    #[serde(skip_serializing_if = "Option::is_none")]
1089    pub expected_size: Option<i64>,
1090    #[serde(skip_serializing_if = "Option::is_none")]
1091    #[validate(length(max = 1024))]
1092    pub description: Option<String>,
1093    #[serde(skip_serializing_if = "Option::is_none")]
1094    pub visibility: Option<FileVisibility>,
1095}
1096
1097#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1098pub struct UploadSessionResponse {
1099    pub session_id: Uuid,
1100    pub upload_id: String,
1101    pub upload_url: String,
1102    pub expires_at: DateTime<Utc>,
1103}
1104
1105#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1106pub struct UploadPartInfo {
1107    pub part_number: i32,
1108    pub etag: String,
1109}
1110
1111#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
1112pub struct CompleteMultipartUploadRequest {
1113    #[validate(length(min = 1, max = 512))]
1114    pub upload_id: String,
1115    pub parts: Vec<UploadPartInfo>,
1116}
1117
1118#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
1119#[serde(rename_all = "snake_case")]
1120pub enum FilesystemEntryType {
1121    File,
1122    Directory,
1123    Symlink,
1124    Other,
1125}
1126
1127#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1128pub struct FilesystemEntry {
1129    pub path: String,
1130    pub entry_type: FilesystemEntryType,
1131    #[serde(skip_serializing_if = "Option::is_none")]
1132    pub size_bytes: Option<u64>,
1133    #[serde(skip_serializing_if = "Option::is_none")]
1134    pub modified: Option<DateTime<Utc>>,
1135    #[serde(skip_serializing_if = "Option::is_none")]
1136    pub hashes: Option<FileHashes>,
1137}
1138
1139#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1140pub struct FilesystemCrawlRequest {
1141    pub root: String,
1142    #[serde(skip_serializing_if = "Option::is_none")]
1143    pub max_depth: Option<u32>,
1144    #[serde(default)]
1145    pub follow_symlinks: bool,
1146    #[serde(skip_serializing_if = "Option::is_none")]
1147    pub hash: Option<HashingRequest>,
1148}
1149
1150#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1151pub struct FilesystemCrawlResult {
1152    pub root: String,
1153    pub entries: Vec<FilesystemEntry>,
1154}
1155
1156#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1157pub struct TaskResult {
1158    pub status: String,
1159    #[serde(default)]
1160    pub output: Value,
1161    #[serde(skip_serializing_if = "Option::is_none")]
1162    pub error: Option<String>,
1163    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1164    pub artifacts: Vec<Artifact>,
1165}
1166
1167#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1168pub struct TaskLogAppend {
1169    pub stream: Option<String>,
1170    pub lines: Vec<String>,
1171    #[serde(skip_serializing_if = "Option::is_none")]
1172    pub timestamp: Option<DateTime<Utc>>,
1173}
1174
1175#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1176pub struct TaskLogEvent {
1177    pub ts: DateTime<Utc>,
1178    pub stream: String,
1179    pub line: String,
1180}
1181
1182#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1183pub struct RunLogEvent {
1184    pub ts: DateTime<Utc>,
1185    pub stream: String,
1186    pub line: String,
1187    #[serde(skip_serializing_if = "Option::is_none")]
1188    pub task_id: Option<Uuid>,
1189}
1190
1191#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1192pub struct CommandRequest {
1193    pub command: String,
1194    #[serde(default)]
1195    pub args: Vec<String>,
1196    #[serde(skip_serializing_if = "Option::is_none")]
1197    pub cwd: Option<String>,
1198    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
1199    pub env: HashMap<String, String>,
1200    #[serde(skip_serializing_if = "Option::is_none")]
1201    pub timeout_seconds: Option<u64>,
1202}
1203
1204#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1205pub struct CommandResponse {
1206    pub command_id: Uuid,
1207    pub task_id: Uuid,
1208}
1209
1210#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1211pub struct CommandOutputEvent {
1212    pub ts: DateTime<Utc>,
1213    pub stream: String,
1214    #[serde(skip_serializing_if = "Option::is_none")]
1215    pub data: Option<String>,
1216    #[serde(skip_serializing_if = "Option::is_none")]
1217    pub exit_code: Option<i32>,
1218}
1219
1220#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1221pub struct KeyboardInput {
1222    pub text: String,
1223    #[serde(skip_serializing_if = "Option::is_none")]
1224    pub delay_ms: Option<u64>,
1225}
1226
1227#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1228pub struct MouseInput {
1229    pub action: String,
1230    #[serde(skip_serializing_if = "Option::is_none")]
1231    pub x: Option<i32>,
1232    #[serde(skip_serializing_if = "Option::is_none")]
1233    pub y: Option<i32>,
1234    #[serde(skip_serializing_if = "Option::is_none")]
1235    pub button: Option<String>,
1236    #[serde(skip_serializing_if = "Option::is_none")]
1237    pub scroll_x: Option<i32>,
1238    #[serde(skip_serializing_if = "Option::is_none")]
1239    pub scroll_y: Option<i32>,
1240}
1241
1242#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1243pub struct ScreenCaptureRequest {
1244    #[serde(skip_serializing_if = "Option::is_none")]
1245    pub region: Option<CaptureRegion>,
1246    #[serde(default)]
1247    pub include_cursor: bool,
1248    #[serde(skip_serializing_if = "Option::is_none")]
1249    pub format: Option<String>,
1250    #[serde(skip_serializing_if = "Option::is_none")]
1251    pub quality: Option<u8>,
1252    #[serde(skip_serializing_if = "Option::is_none")]
1253    pub display: Option<u32>,
1254}
1255
1256#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1257pub struct CaptureRegion {
1258    pub x: i32,
1259    pub y: i32,
1260    pub width: i32,
1261    pub height: i32,
1262}
1263
1264#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1265pub struct ScreenCaptureTicket {
1266    pub capture_id: Uuid,
1267    pub task_id: Uuid,
1268}
1269
1270#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1271pub struct ScreenCapture {
1272    pub id: Uuid,
1273    pub task_id: Uuid,
1274    pub agent_id: Uuid,
1275    pub status: String,
1276    pub format: String,
1277    #[serde(skip_serializing_if = "Option::is_none")]
1278    pub size_bytes: Option<u64>,
1279    #[serde(skip_serializing_if = "Option::is_none")]
1280    pub download_url: Option<String>,
1281    pub created_at: DateTime<Utc>,
1282}
1283
1284#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1285pub struct Artifact {
1286    pub id: Uuid,
1287    pub task_id: Uuid,
1288    pub name: String,
1289    pub content_type: String,
1290    pub size_bytes: u64,
1291    pub download_url: String,
1292}
1293
1294#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
1295pub struct CreateArtifactRequest {
1296    pub task_id: Uuid,
1297    #[validate(length(min = 1))]
1298    pub name: String,
1299    #[validate(length(min = 1))]
1300    pub content_type: String,
1301    pub size_bytes: u64,
1302}
1303
1304#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1305pub struct ArtifactUpload {
1306    pub artifact: Artifact,
1307    pub upload_url: String,
1308    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
1309    pub headers: HashMap<String, String>,
1310}
1311
1312#[derive(Debug, Clone)]
1313pub struct StoredUser {
1314    pub user: User,
1315    pub password_hash: Option<String>,
1316}
1317
1318#[derive(Debug, Clone)]
1319pub struct RefreshTokenData {
1320    pub user_id: Uuid,
1321    pub expires_at: DateTime<Utc>,
1322    pub token_hash: String,
1323    pub fingerprint: String,
1324}
1325
1326#[derive(Debug, Clone)]
1327pub struct OAuthStateRecord {
1328    pub provider: String,
1329}
1330
1331#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1332pub struct ErrorResponse {
1333    pub error: String,
1334    pub status: u16,
1335}
1336
1337#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
1338#[serde(rename_all = "snake_case")]
1339pub enum ApiKeyRole {
1340    Owner,
1341    Manager,
1342    Contributor,
1343    Viewer,
1344}
1345
1346impl ApiKeyRole {
1347    #[must_use]
1348    pub const fn as_str(&self) -> &'static str {
1349        match self {
1350            Self::Owner => "owner",
1351            Self::Manager => "manager",
1352            Self::Contributor => "contributor",
1353            Self::Viewer => "viewer",
1354        }
1355    }
1356}
1357
1358#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
1359#[serde(rename_all = "snake_case")]
1360pub enum ApiKeyStatus {
1361    Active,
1362    Revoked,
1363    Expired,
1364}
1365
1366impl ApiKeyStatus {
1367    #[must_use]
1368    pub const fn as_str(&self) -> &'static str {
1369        match self {
1370            Self::Active => "active",
1371            Self::Revoked => "revoked",
1372            Self::Expired => "expired",
1373        }
1374    }
1375}
1376
1377impl FromStr for ApiKeyRole {
1378    type Err = ();
1379
1380    fn from_str(value: &str) -> Result<Self, Self::Err> {
1381        match value {
1382            "owner" => Ok(Self::Owner),
1383            "manager" => Ok(Self::Manager),
1384            "contributor" => Ok(Self::Contributor),
1385            "viewer" => Ok(Self::Viewer),
1386            _ => Err(()),
1387        }
1388    }
1389}
1390
1391impl FromStr for ApiKeyStatus {
1392    type Err = ();
1393
1394    fn from_str(value: &str) -> Result<Self, Self::Err> {
1395        match value {
1396            "active" => Ok(Self::Active),
1397            "revoked" => Ok(Self::Revoked),
1398            "expired" => Ok(Self::Expired),
1399            _ => Err(()),
1400        }
1401    }
1402}
1403
1404#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, ToSchema)]
1405#[serde(transparent)]
1406pub struct ApiKeyScope(pub String);
1407
1408impl ApiKeyScope {
1409    #[must_use]
1410    pub fn new(value: impl Into<String>) -> Self {
1411        Self(value.into())
1412    }
1413
1414    #[must_use]
1415    pub fn as_str(&self) -> &str {
1416        &self.0
1417    }
1418}
1419
1420#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1421pub struct ApiKey {
1422    pub id: Uuid,
1423    pub name: String,
1424    #[serde(skip_serializing_if = "Option::is_none")]
1425    pub description: Option<String>,
1426    pub role: ApiKeyRole,
1427    #[serde(skip_serializing_if = "Option::is_none")]
1428    pub scopes: Option<Vec<ApiKeyScope>>,
1429    pub status: ApiKeyStatus,
1430    #[serde(skip_serializing_if = "Option::is_none")]
1431    pub metadata: Option<HashMap<String, String>>,
1432    #[serde(skip_serializing_if = "Option::is_none")]
1433    pub expires_at: Option<DateTime<Utc>>,
1434    #[serde(skip_serializing_if = "Option::is_none")]
1435    pub last_used_at: Option<DateTime<Utc>>,
1436    pub created_at: DateTime<Utc>,
1437    pub updated_at: DateTime<Utc>,
1438    pub created_by: Uuid,
1439    #[serde(skip_serializing_if = "Option::is_none")]
1440    pub managed_by: Option<Uuid>,
1441    #[serde(skip_serializing_if = "Option::is_none")]
1442    pub token_preview: Option<String>,
1443}
1444
1445impl ApiKey {
1446    #[must_use]
1447    pub fn to_summary(&self) -> ApiKeySummary {
1448        ApiKeySummary {
1449            id: self.id,
1450            name: self.name.clone(),
1451            role: self.role.clone(),
1452            status: self.status.clone(),
1453            created_at: self.created_at,
1454            expires_at: self.expires_at,
1455            last_used_at: self.last_used_at,
1456            token_preview: self.token_preview.clone(),
1457        }
1458    }
1459}
1460
1461#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1462pub struct ApiKeySummary {
1463    pub id: Uuid,
1464    pub name: String,
1465    pub role: ApiKeyRole,
1466    pub status: ApiKeyStatus,
1467    pub created_at: DateTime<Utc>,
1468    #[serde(skip_serializing_if = "Option::is_none")]
1469    pub expires_at: Option<DateTime<Utc>>,
1470    #[serde(skip_serializing_if = "Option::is_none")]
1471    pub last_used_at: Option<DateTime<Utc>>,
1472    #[serde(skip_serializing_if = "Option::is_none")]
1473    pub token_preview: Option<String>,
1474}
1475
1476#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1477pub struct ApiKeyWithSecret {
1478    #[serde(flatten)]
1479    pub api_key: ApiKey,
1480    pub secret: String,
1481}
1482
1483#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1484pub struct CreateApiKeyRequest {
1485    pub name: String,
1486    pub description: Option<String>,
1487    pub role: ApiKeyRole,
1488    pub scopes: Option<Vec<ApiKeyScope>>,
1489    pub expires_at: Option<DateTime<Utc>>,
1490    pub ttl_days: Option<i32>,
1491    pub metadata: Option<HashMap<String, String>>,
1492}
1493
1494#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1495pub struct UpdateApiKeyRequest {
1496    pub name: Option<String>,
1497    pub description: Option<Option<String>>,
1498    pub role: Option<ApiKeyRole>,
1499    pub scopes: Option<Option<Vec<ApiKeyScope>>>,
1500    pub status: Option<ApiKeyStatus>,
1501    pub expires_at: Option<Option<DateTime<Utc>>>,
1502    pub ttl_days: Option<i32>,
1503    pub metadata: Option<Option<HashMap<String, String>>>,
1504}
1505
1506#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1507pub struct RotateApiKeyRequest {
1508    pub expires_at: Option<DateTime<Utc>>,
1509    pub ttl_days: Option<i32>,
1510    pub reason: Option<String>,
1511}
1512
1513#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1514pub struct ApiKeyListResponse {
1515    pub object: String,
1516    pub data: Vec<ApiKeySummary>,
1517    pub has_more: bool,
1518    #[serde(skip_serializing_if = "Option::is_none")]
1519    pub first_id: Option<Uuid>,
1520    #[serde(skip_serializing_if = "Option::is_none")]
1521    pub last_id: Option<Uuid>,
1522}
1523
1524#[derive(Debug, Clone)]
1525pub struct NewApiKey {
1526    pub id: Uuid,
1527    pub name: String,
1528    pub description: Option<String>,
1529    pub role: ApiKeyRole,
1530    pub scopes: Option<Vec<ApiKeyScope>>,
1531    pub status: ApiKeyStatus,
1532    pub secret_hash: String,
1533    pub token_preview: Option<String>,
1534    pub created_by: Uuid,
1535    pub managed_by: Option<Uuid>,
1536    pub metadata: Option<HashMap<String, String>>,
1537    pub created_at: DateTime<Utc>,
1538    pub updated_at: DateTime<Utc>,
1539    pub expires_at: Option<DateTime<Utc>>,
1540    pub last_used_at: Option<DateTime<Utc>>,
1541}
1542
1543#[derive(Debug, Clone)]
1544pub struct ApiKeyUpdate {
1545    pub name: Option<String>,
1546    pub description: Option<Option<String>>,
1547    pub role: Option<ApiKeyRole>,
1548    pub scopes: Option<Option<Vec<ApiKeyScope>>>,
1549    pub status: Option<ApiKeyStatus>,
1550    pub token_preview: Option<Option<String>>,
1551    pub secret_hash: Option<String>,
1552    pub expires_at: Option<Option<DateTime<Utc>>>,
1553    pub metadata: Option<Option<HashMap<String, String>>>,
1554    pub managed_by: Option<Uuid>,
1555    pub last_used_at: Option<Option<DateTime<Utc>>>,
1556    pub updated_at: DateTime<Utc>,
1557}
1558
1559impl ApiKeyUpdate {
1560    #[must_use]
1561    pub const fn new(updated_at: DateTime<Utc>) -> Self {
1562        Self {
1563            name: None,
1564            description: None,
1565            role: None,
1566            scopes: None,
1567            status: None,
1568            token_preview: None,
1569            secret_hash: None,
1570            expires_at: None,
1571            metadata: None,
1572            managed_by: None,
1573            last_used_at: None,
1574            updated_at,
1575        }
1576    }
1577}
1578
1579#[derive(Debug, Clone)]
1580pub struct StoredApiKey {
1581    pub api_key: ApiKey,
1582    pub secret_hash: String,
1583}
1584
1585impl StoredApiKey {
1586    #[must_use]
1587    pub fn into_api_key(self) -> ApiKey {
1588        self.api_key
1589    }
1590}
1591
1592#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
1593#[serde(rename_all = "snake_case")]
1594pub enum OrganizationRole {
1595    Owner,
1596    Admin,
1597    Member,
1598    Viewer,
1599}
1600
1601impl OrganizationRole {
1602    #[must_use]
1603    pub const fn is_admin_like(&self) -> bool {
1604        matches!(self, Self::Owner | Self::Admin)
1605    }
1606
1607    #[must_use]
1608    pub const fn as_str(&self) -> &'static str {
1609        match self {
1610            Self::Owner => "owner",
1611            Self::Admin => "admin",
1612            Self::Member => "member",
1613            Self::Viewer => "viewer",
1614        }
1615    }
1616}
1617
1618impl FromStr for OrganizationRole {
1619    type Err = ();
1620
1621    fn from_str(value: &str) -> Result<Self, Self::Err> {
1622        match value {
1623            "owner" => Ok(Self::Owner),
1624            "admin" => Ok(Self::Admin),
1625            "member" => Ok(Self::Member),
1626            "viewer" => Ok(Self::Viewer),
1627            _ => Err(()),
1628        }
1629    }
1630}
1631
1632#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
1633#[serde(rename_all = "snake_case")]
1634pub enum OrganizationMemberStatus {
1635    Pending,
1636    Active,
1637    Suspended,
1638    Removed,
1639}
1640
1641impl OrganizationMemberStatus {
1642    #[must_use]
1643    pub const fn as_str(&self) -> &'static str {
1644        match self {
1645            Self::Pending => "pending",
1646            Self::Active => "active",
1647            Self::Suspended => "suspended",
1648            Self::Removed => "removed",
1649        }
1650    }
1651}
1652
1653impl FromStr for OrganizationMemberStatus {
1654    type Err = ();
1655
1656    fn from_str(value: &str) -> Result<Self, Self::Err> {
1657        match value {
1658            "pending" => Ok(Self::Pending),
1659            "active" => Ok(Self::Active),
1660            "suspended" => Ok(Self::Suspended),
1661            "removed" => Ok(Self::Removed),
1662            _ => Err(()),
1663        }
1664    }
1665}
1666
1667#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1668pub struct Organization {
1669    pub id: Uuid,
1670    pub name: String,
1671    pub slug: String,
1672    #[serde(skip_serializing_if = "Option::is_none")]
1673    pub description: Option<String>,
1674    #[serde(skip_serializing_if = "Option::is_none")]
1675    pub metadata: Option<HashMap<String, String>>,
1676    pub created_by: Uuid,
1677    pub created_at: DateTime<Utc>,
1678    pub updated_at: DateTime<Utc>,
1679    pub is_active: bool,
1680    pub member_count: usize,
1681}
1682
1683#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
1684pub struct CreateOrganizationRequest {
1685    #[validate(length(min = 3, max = 128))]
1686    pub name: String,
1687    #[validate(length(min = 3, max = 64))]
1688    pub slug: Option<String>,
1689    #[validate(length(max = 1024))]
1690    pub description: Option<String>,
1691    pub metadata: Option<HashMap<String, String>>,
1692}
1693
1694#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
1695pub struct UpdateOrganizationRequest {
1696    #[validate(length(min = 3, max = 128))]
1697    pub name: Option<String>,
1698    #[validate(length(min = 3, max = 64))]
1699    pub slug: Option<Option<String>>,
1700    #[validate(length(max = 1024))]
1701    pub description: Option<Option<String>>,
1702    pub metadata: Option<Option<HashMap<String, String>>>,
1703    pub is_active: Option<bool>,
1704}
1705
1706#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1707pub struct OrganizationSummary {
1708    #[serde(flatten)]
1709    pub organization: Organization,
1710}
1711
1712#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1713pub struct OrganizationListResponse {
1714    pub organizations: Vec<OrganizationSummary>,
1715}
1716
1717#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1718pub struct OrganizationMember {
1719    pub user_id: Uuid,
1720    pub role: OrganizationRole,
1721    pub status: OrganizationMemberStatus,
1722    #[serde(skip_serializing_if = "Option::is_none")]
1723    pub invited_by: Option<Uuid>,
1724    #[serde(skip_serializing_if = "Option::is_none")]
1725    pub invited_at: Option<DateTime<Utc>>,
1726    #[serde(skip_serializing_if = "Option::is_none")]
1727    pub joined_at: Option<DateTime<Utc>>,
1728}
1729
1730#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1731pub struct OrganizationMemberListResponse {
1732    pub members: Vec<OrganizationMember>,
1733}
1734
1735#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
1736pub struct AddOrganizationMemberRequest {
1737    pub user_id: Uuid,
1738    pub role: OrganizationRole,
1739}
1740
1741#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
1742pub struct UpdateOrganizationMemberRequest {
1743    pub role: Option<OrganizationRole>,
1744    pub status: Option<OrganizationMemberStatus>,
1745}
1746
1747#[derive(Debug, Clone)]
1748pub struct NewOrganization {
1749    pub id: Uuid,
1750    pub name: String,
1751    pub slug: String,
1752    pub description: Option<String>,
1753    pub metadata: Option<HashMap<String, String>>,
1754    pub created_by: Uuid,
1755    pub created_at: DateTime<Utc>,
1756    pub updated_at: DateTime<Utc>,
1757    pub is_active: bool,
1758}
1759
1760#[derive(Debug, Clone)]
1761pub struct OrganizationUpdate {
1762    pub name: Option<String>,
1763    pub slug: Option<Option<String>>,
1764    pub description: Option<Option<String>>,
1765    pub metadata: Option<Option<HashMap<String, String>>>,
1766    pub is_active: Option<bool>,
1767    pub updated_at: DateTime<Utc>,
1768}
1769
1770#[derive(Debug, Clone)]
1771pub struct NewOrganizationMember {
1772    pub organization_id: Uuid,
1773    pub user_id: Uuid,
1774    pub role: OrganizationRole,
1775    pub status: OrganizationMemberStatus,
1776    pub invited_by: Option<Uuid>,
1777    pub invited_at: Option<DateTime<Utc>>,
1778    pub joined_at: Option<DateTime<Utc>>,
1779}
1780
1781#[derive(Debug, Clone)]
1782pub struct OrganizationMemberUpdate {
1783    pub role: Option<OrganizationRole>,
1784    pub status: Option<OrganizationMemberStatus>,
1785    pub joined_at: Option<Option<DateTime<Utc>>>,
1786}
1787
1788#[derive(Debug, Clone, Default)]
1789pub struct OrganizationListFilters {
1790    pub limit: Option<usize>,
1791    pub after: Option<Uuid>,
1792    pub search: Option<String>,
1793    pub include_inactive: bool,
1794}
1795
1796#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema, Default)]
1797#[serde(rename_all = "snake_case")]
1798pub enum GroupType {
1799    #[default]
1800    Standard,
1801    Team,
1802}
1803
1804impl GroupType {
1805    #[must_use]
1806    pub const fn as_str(&self) -> &'static str {
1807        match self {
1808            Self::Standard => "standard",
1809            Self::Team => "team",
1810        }
1811    }
1812}
1813
1814impl FromStr for GroupType {
1815    type Err = ();
1816
1817    fn from_str(value: &str) -> Result<Self, Self::Err> {
1818        match value {
1819            "standard" => Ok(Self::Standard),
1820            "team" => Ok(Self::Team),
1821            _ => Err(()),
1822        }
1823    }
1824}
1825
1826#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
1827#[serde(rename_all = "snake_case")]
1828pub enum GroupRole {
1829    Owner,
1830    Admin,
1831    Member,
1832    Viewer,
1833}
1834
1835impl GroupRole {
1836    #[must_use]
1837    pub const fn is_admin_like(&self) -> bool {
1838        matches!(self, Self::Owner | Self::Admin)
1839    }
1840
1841    #[must_use]
1842    pub const fn as_str(&self) -> &'static str {
1843        match self {
1844            Self::Owner => "owner",
1845            Self::Admin => "admin",
1846            Self::Member => "member",
1847            Self::Viewer => "viewer",
1848        }
1849    }
1850}
1851
1852impl FromStr for GroupRole {
1853    type Err = ();
1854
1855    fn from_str(value: &str) -> Result<Self, Self::Err> {
1856        match value {
1857            "owner" => Ok(Self::Owner),
1858            "admin" => Ok(Self::Admin),
1859            "member" => Ok(Self::Member),
1860            "viewer" => Ok(Self::Viewer),
1861            _ => Err(()),
1862        }
1863    }
1864}
1865
1866#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
1867#[serde(rename_all = "snake_case")]
1868pub enum GroupMemberStatus {
1869    Pending,
1870    Active,
1871    Suspended,
1872    Removed,
1873}
1874
1875impl GroupMemberStatus {
1876    #[must_use]
1877    pub const fn as_str(&self) -> &'static str {
1878        match self {
1879            Self::Pending => "pending",
1880            Self::Active => "active",
1881            Self::Suspended => "suspended",
1882            Self::Removed => "removed",
1883        }
1884    }
1885}
1886
1887impl FromStr for GroupMemberStatus {
1888    type Err = ();
1889
1890    fn from_str(value: &str) -> Result<Self, Self::Err> {
1891        match value {
1892            "pending" => Ok(Self::Pending),
1893            "active" => Ok(Self::Active),
1894            "suspended" => Ok(Self::Suspended),
1895            "removed" => Ok(Self::Removed),
1896            _ => Err(()),
1897        }
1898    }
1899}
1900
1901#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1902pub struct Group {
1903    pub id: Uuid,
1904    pub organization_id: Uuid,
1905    pub name: String,
1906    pub slug: String,
1907    pub group_type: GroupType,
1908    #[serde(skip_serializing_if = "Option::is_none")]
1909    pub description: Option<String>,
1910    #[serde(skip_serializing_if = "Option::is_none")]
1911    pub metadata: Option<HashMap<String, String>>,
1912    pub created_by: Uuid,
1913    pub created_at: DateTime<Utc>,
1914    pub updated_at: DateTime<Utc>,
1915    pub is_active: bool,
1916    pub member_count: usize,
1917}
1918
1919#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
1920pub struct CreateGroupRequest {
1921    #[validate(length(min = 3, max = 128))]
1922    pub name: String,
1923    #[validate(length(min = 3, max = 64))]
1924    pub slug: Option<String>,
1925    #[serde(default)]
1926    pub group_type: GroupType,
1927    #[validate(length(max = 1024))]
1928    pub description: Option<String>,
1929    pub metadata: Option<HashMap<String, String>>,
1930}
1931
1932#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
1933pub struct UpdateGroupRequest {
1934    #[validate(length(min = 3, max = 128))]
1935    pub name: Option<String>,
1936    #[validate(length(min = 3, max = 64))]
1937    pub slug: Option<Option<String>>,
1938    pub group_type: Option<GroupType>,
1939    #[validate(length(max = 1024))]
1940    pub description: Option<Option<String>>,
1941    pub metadata: Option<Option<HashMap<String, String>>>,
1942    pub is_active: Option<bool>,
1943}
1944
1945#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1946pub struct GroupSummary {
1947    #[serde(flatten)]
1948    pub group: Group,
1949}
1950
1951#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1952pub struct GroupListResponse {
1953    pub groups: Vec<GroupSummary>,
1954}
1955
1956#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1957pub struct GroupMember {
1958    pub user_id: Uuid,
1959    pub role: GroupRole,
1960    pub status: GroupMemberStatus,
1961    #[serde(skip_serializing_if = "Option::is_none")]
1962    pub invited_by: Option<Uuid>,
1963    #[serde(skip_serializing_if = "Option::is_none")]
1964    pub invited_at: Option<DateTime<Utc>>,
1965    #[serde(skip_serializing_if = "Option::is_none")]
1966    pub joined_at: Option<DateTime<Utc>>,
1967}
1968
1969#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
1970pub struct GroupMemberListResponse {
1971    pub members: Vec<GroupMember>,
1972}
1973
1974#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
1975pub struct AddGroupMemberRequest {
1976    pub user_id: Uuid,
1977    pub role: GroupRole,
1978}
1979
1980#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
1981pub struct UpdateGroupMemberRequest {
1982    pub role: Option<GroupRole>,
1983    pub status: Option<GroupMemberStatus>,
1984}
1985
1986#[derive(Debug, Clone)]
1987pub struct NewGroup {
1988    pub id: Uuid,
1989    pub organization_id: Uuid,
1990    pub name: String,
1991    pub slug: String,
1992    pub group_type: GroupType,
1993    pub description: Option<String>,
1994    pub metadata: Option<HashMap<String, String>>,
1995    pub created_by: Uuid,
1996    pub created_at: DateTime<Utc>,
1997    pub updated_at: DateTime<Utc>,
1998    pub is_active: bool,
1999}
2000
2001#[derive(Debug, Clone)]
2002pub struct GroupUpdate {
2003    pub name: Option<String>,
2004    pub slug: Option<Option<String>>,
2005    pub group_type: Option<GroupType>,
2006    pub description: Option<Option<String>>,
2007    pub metadata: Option<Option<HashMap<String, String>>>,
2008    pub is_active: Option<bool>,
2009    pub updated_at: DateTime<Utc>,
2010}
2011
2012#[derive(Debug, Clone)]
2013pub struct NewGroupMember {
2014    pub group_id: Uuid,
2015    pub user_id: Uuid,
2016    pub role: GroupRole,
2017    pub status: GroupMemberStatus,
2018    pub invited_by: Option<Uuid>,
2019    pub invited_at: Option<DateTime<Utc>>,
2020    pub joined_at: Option<DateTime<Utc>>,
2021}
2022
2023#[derive(Debug, Clone)]
2024pub struct GroupMemberUpdate {
2025    pub role: Option<GroupRole>,
2026    pub status: Option<GroupMemberStatus>,
2027    pub joined_at: Option<Option<DateTime<Utc>>>,
2028}
2029
2030#[derive(Debug, Clone, Default)]
2031pub struct GroupListFilters {
2032    pub organization_id: Uuid,
2033    pub group_type: Option<GroupType>,
2034    pub limit: Option<usize>,
2035    pub after: Option<Uuid>,
2036    pub search: Option<String>,
2037    pub include_inactive: bool,
2038}
2039
2040#[derive(Debug, Clone, Default)]
2041pub struct ApiKeyListFilters {
2042    pub role: Option<ApiKeyRole>,
2043    pub status: Option<ApiKeyStatus>,
2044    pub expires_before: Option<DateTime<Utc>>,
2045    pub created_by: Option<Uuid>,
2046    pub search: Option<String>,
2047    pub limit: Option<i64>,
2048}
2049
2050#[derive(Debug, Clone, Serialize, Deserialize, Default, ToSchema)]
2051pub struct ObjectMeta {
2052    #[serde(skip_serializing_if = "Option::is_none")]
2053    pub name: Option<String>,
2054    #[serde(skip_serializing_if = "Option::is_none")]
2055    pub namespace: Option<String>,
2056    #[serde(skip_serializing_if = "Option::is_none")]
2057    pub uid: Option<String>,
2058    #[serde(skip_serializing_if = "Option::is_none")]
2059    pub resource_version: Option<String>,
2060    #[serde(skip_serializing_if = "Option::is_none")]
2061    pub creation_timestamp: Option<DateTime<Utc>>,
2062    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
2063    pub labels: HashMap<String, String>,
2064    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
2065    pub annotations: HashMap<String, String>,
2066}
2067
2068#[derive(Debug, Clone, Serialize, Deserialize, Default, ToSchema)]
2069pub struct ListMeta {
2070    #[serde(skip_serializing_if = "Option::is_none")]
2071    pub resource_version: Option<String>,
2072    #[serde(rename = "continue", skip_serializing_if = "Option::is_none")]
2073    pub continue_token: Option<String>,
2074}
2075
2076#[derive(Debug, Clone, Serialize, Deserialize, Default, ToSchema)]
2077pub struct ResourceListOptions {
2078    #[serde(rename = "labelSelector", skip_serializing_if = "Option::is_none")]
2079    pub label_selector: Option<String>,
2080    #[serde(rename = "fieldSelector", skip_serializing_if = "Option::is_none")]
2081    pub field_selector: Option<String>,
2082    #[serde(skip_serializing_if = "Option::is_none")]
2083    pub limit: Option<usize>,
2084    #[serde(rename = "continue", skip_serializing_if = "Option::is_none")]
2085    pub continue_token: Option<String>,
2086}
2087
2088#[derive(Debug, Clone, Serialize, Deserialize, Default, ToSchema)]
2089pub struct LabelSelector {
2090    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
2091    pub match_labels: HashMap<String, String>,
2092    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2093    pub match_expressions: Vec<LabelSelectorRequirement>,
2094}
2095
2096#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2097pub struct LabelSelectorRequirement {
2098    pub key: String,
2099    pub operator: String,
2100    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2101    pub values: Vec<String>,
2102}
2103
2104#[derive(Debug, Clone, Serialize, Deserialize, Default, ToSchema)]
2105pub struct ObjectReference {
2106    #[serde(skip_serializing_if = "Option::is_none")]
2107    pub api_version: Option<String>,
2108    #[serde(skip_serializing_if = "Option::is_none")]
2109    pub kind: Option<String>,
2110    #[serde(skip_serializing_if = "Option::is_none")]
2111    pub name: Option<String>,
2112    #[serde(skip_serializing_if = "Option::is_none")]
2113    pub namespace: Option<String>,
2114    #[serde(skip_serializing_if = "Option::is_none")]
2115    pub uid: Option<String>,
2116}
2117
2118#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2119pub struct NamespaceStatus {
2120    #[serde(skip_serializing_if = "Option::is_none")]
2121    pub phase: Option<String>,
2122}
2123
2124#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2125pub struct Namespace {
2126    #[serde(skip_serializing_if = "Option::is_none")]
2127    pub api_version: Option<String>,
2128    #[serde(skip_serializing_if = "Option::is_none")]
2129    pub kind: Option<String>,
2130    pub metadata: ObjectMeta,
2131    #[serde(skip_serializing_if = "Option::is_none")]
2132    pub status: Option<NamespaceStatus>,
2133}
2134
2135#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2136pub struct NamespaceList {
2137    #[serde(skip_serializing_if = "Option::is_none")]
2138    pub api_version: Option<String>,
2139    #[serde(skip_serializing_if = "Option::is_none")]
2140    pub kind: Option<String>,
2141    #[serde(default)]
2142    pub metadata: ListMeta,
2143    pub items: Vec<Namespace>,
2144}
2145
2146#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2147pub struct Taint {
2148    pub key: String,
2149    #[serde(skip_serializing_if = "Option::is_none")]
2150    pub value: Option<String>,
2151    #[serde(skip_serializing_if = "Option::is_none")]
2152    pub effect: Option<String>,
2153    #[serde(skip_serializing_if = "Option::is_none")]
2154    pub time_added: Option<DateTime<Utc>>,
2155}
2156
2157#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2158pub struct NodeSpec {
2159    #[serde(skip_serializing_if = "Option::is_none")]
2160    pub unschedulable: Option<bool>,
2161    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2162    pub taints: Vec<Taint>,
2163}
2164
2165#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2166pub struct NodeResourceCapacity {
2167    #[serde(skip_serializing_if = "Option::is_none")]
2168    pub pods: Option<i32>,
2169    #[serde(skip_serializing_if = "Option::is_none")]
2170    pub cpu_millis: Option<u64>,
2171    #[serde(skip_serializing_if = "Option::is_none")]
2172    pub memory_mb: Option<u64>,
2173    #[serde(skip_serializing_if = "Option::is_none")]
2174    pub gpus: Option<u64>,
2175}
2176
2177#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2178pub struct NodeInventory {
2179    #[serde(skip_serializing_if = "Option::is_none")]
2180    pub capacity: Option<NodeResourceCapacity>,
2181    #[serde(skip_serializing_if = "Option::is_none")]
2182    pub allocatable: Option<NodeResourceCapacity>,
2183}
2184
2185#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2186pub struct NodeStatus {
2187    #[serde(skip_serializing_if = "Option::is_none")]
2188    pub ready: Option<bool>,
2189    #[serde(skip_serializing_if = "Option::is_none")]
2190    pub inventory: Option<NodeInventory>,
2191}
2192
2193#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2194pub struct Node {
2195    #[serde(skip_serializing_if = "Option::is_none")]
2196    pub api_version: Option<String>,
2197    #[serde(skip_serializing_if = "Option::is_none")]
2198    pub kind: Option<String>,
2199    pub metadata: ObjectMeta,
2200    #[serde(skip_serializing_if = "Option::is_none")]
2201    pub spec: Option<NodeSpec>,
2202    #[serde(skip_serializing_if = "Option::is_none")]
2203    pub status: Option<NodeStatus>,
2204}
2205
2206#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2207pub struct NodeList {
2208    #[serde(skip_serializing_if = "Option::is_none")]
2209    pub api_version: Option<String>,
2210    #[serde(skip_serializing_if = "Option::is_none")]
2211    pub kind: Option<String>,
2212    #[serde(default)]
2213    pub metadata: ListMeta,
2214    pub items: Vec<Node>,
2215}
2216
2217#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2218pub struct ConfigMap {
2219    #[serde(skip_serializing_if = "Option::is_none")]
2220    pub api_version: Option<String>,
2221    #[serde(skip_serializing_if = "Option::is_none")]
2222    pub kind: Option<String>,
2223    pub metadata: ObjectMeta,
2224    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
2225    pub data: HashMap<String, String>,
2226}
2227
2228#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2229pub struct ConfigMapList {
2230    #[serde(skip_serializing_if = "Option::is_none")]
2231    pub api_version: Option<String>,
2232    #[serde(skip_serializing_if = "Option::is_none")]
2233    pub kind: Option<String>,
2234    #[serde(default)]
2235    pub metadata: ListMeta,
2236    pub items: Vec<ConfigMap>,
2237}
2238
2239#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2240pub struct Secret {
2241    #[serde(skip_serializing_if = "Option::is_none")]
2242    pub api_version: Option<String>,
2243    #[serde(skip_serializing_if = "Option::is_none")]
2244    pub kind: Option<String>,
2245    pub metadata: ObjectMeta,
2246    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
2247    pub secret_type: Option<String>,
2248    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
2249    pub data: HashMap<String, String>,
2250}
2251
2252#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2253pub struct SecretList {
2254    #[serde(skip_serializing_if = "Option::is_none")]
2255    pub api_version: Option<String>,
2256    #[serde(skip_serializing_if = "Option::is_none")]
2257    pub kind: Option<String>,
2258    #[serde(default)]
2259    pub metadata: ListMeta,
2260    pub items: Vec<Secret>,
2261}
2262
2263#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2264pub struct ServiceAccount {
2265    #[serde(skip_serializing_if = "Option::is_none")]
2266    pub api_version: Option<String>,
2267    #[serde(skip_serializing_if = "Option::is_none")]
2268    pub kind: Option<String>,
2269    pub metadata: ObjectMeta,
2270    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2271    pub secrets: Vec<ObjectReference>,
2272}
2273
2274#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2275pub struct ServiceAccountList {
2276    #[serde(skip_serializing_if = "Option::is_none")]
2277    pub api_version: Option<String>,
2278    #[serde(skip_serializing_if = "Option::is_none")]
2279    pub kind: Option<String>,
2280    #[serde(default)]
2281    pub metadata: ListMeta,
2282    pub items: Vec<ServiceAccount>,
2283}
2284
2285#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2286pub struct ServicePort {
2287    #[serde(skip_serializing_if = "Option::is_none")]
2288    pub name: Option<String>,
2289    pub port: u16,
2290    #[serde(skip_serializing_if = "Option::is_none")]
2291    pub target_port: Option<String>,
2292    #[serde(skip_serializing_if = "Option::is_none")]
2293    pub protocol: Option<String>,
2294}
2295
2296#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2297pub struct ServiceSpec {
2298    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
2299    pub service_type: Option<String>,
2300    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
2301    pub selector: HashMap<String, String>,
2302    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2303    pub ports: Vec<ServicePort>,
2304}
2305
2306#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2307pub struct Service {
2308    #[serde(skip_serializing_if = "Option::is_none")]
2309    pub api_version: Option<String>,
2310    #[serde(skip_serializing_if = "Option::is_none")]
2311    pub kind: Option<String>,
2312    pub metadata: ObjectMeta,
2313    #[serde(skip_serializing_if = "Option::is_none")]
2314    pub spec: Option<ServiceSpec>,
2315    #[serde(skip_serializing_if = "Option::is_none")]
2316    pub status: Option<HashMap<String, Value>>,
2317}
2318
2319#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2320pub struct ServiceList {
2321    #[serde(skip_serializing_if = "Option::is_none")]
2322    pub api_version: Option<String>,
2323    #[serde(skip_serializing_if = "Option::is_none")]
2324    pub kind: Option<String>,
2325    #[serde(default)]
2326    pub metadata: ListMeta,
2327    pub items: Vec<Service>,
2328}
2329
2330#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2331pub struct EndpointAddress {
2332    pub ip: String,
2333    #[serde(skip_serializing_if = "Option::is_none")]
2334    pub target_ref: Option<ObjectReference>,
2335}
2336
2337#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2338pub struct EndpointPort {
2339    #[serde(skip_serializing_if = "Option::is_none")]
2340    pub name: Option<String>,
2341    pub port: u16,
2342    #[serde(skip_serializing_if = "Option::is_none")]
2343    pub protocol: Option<String>,
2344}
2345
2346#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2347pub struct EndpointSubset {
2348    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2349    pub addresses: Vec<EndpointAddress>,
2350    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2351    pub ports: Vec<EndpointPort>,
2352}
2353
2354#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2355pub struct Endpoints {
2356    #[serde(skip_serializing_if = "Option::is_none")]
2357    pub api_version: Option<String>,
2358    #[serde(skip_serializing_if = "Option::is_none")]
2359    pub kind: Option<String>,
2360    pub metadata: ObjectMeta,
2361    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2362    pub subsets: Vec<EndpointSubset>,
2363}
2364
2365#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2366pub struct EndpointsList {
2367    #[serde(skip_serializing_if = "Option::is_none")]
2368    pub api_version: Option<String>,
2369    #[serde(skip_serializing_if = "Option::is_none")]
2370    pub kind: Option<String>,
2371    #[serde(default)]
2372    pub metadata: ListMeta,
2373    pub items: Vec<Endpoints>,
2374}
2375
2376#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2377pub struct EndpointSlicePort {
2378    #[serde(skip_serializing_if = "Option::is_none")]
2379    pub name: Option<String>,
2380    #[serde(skip_serializing_if = "Option::is_none")]
2381    pub port: Option<u16>,
2382    #[serde(skip_serializing_if = "Option::is_none")]
2383    pub protocol: Option<String>,
2384}
2385
2386#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2387pub struct EndpointSliceEndpoint {
2388    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2389    pub addresses: Vec<String>,
2390    #[serde(skip_serializing_if = "Option::is_none")]
2391    pub target_ref: Option<ObjectReference>,
2392    #[serde(skip_serializing_if = "Option::is_none")]
2393    pub conditions: Option<HashMap<String, bool>>,
2394}
2395
2396#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2397pub struct EndpointSlice {
2398    #[serde(skip_serializing_if = "Option::is_none")]
2399    pub api_version: Option<String>,
2400    #[serde(skip_serializing_if = "Option::is_none")]
2401    pub kind: Option<String>,
2402    pub metadata: ObjectMeta,
2403    pub address_type: String,
2404    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2405    pub endpoints: Vec<EndpointSliceEndpoint>,
2406    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2407    pub ports: Vec<EndpointSlicePort>,
2408}
2409
2410#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2411pub struct EndpointSliceList {
2412    #[serde(skip_serializing_if = "Option::is_none")]
2413    pub api_version: Option<String>,
2414    #[serde(skip_serializing_if = "Option::is_none")]
2415    pub kind: Option<String>,
2416    #[serde(default)]
2417    pub metadata: ListMeta,
2418    pub items: Vec<EndpointSlice>,
2419}
2420
2421#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2422pub struct WebhookServiceReference {
2423    pub name: String,
2424    pub namespace: String,
2425    #[serde(skip_serializing_if = "Option::is_none")]
2426    pub path: Option<String>,
2427}
2428
2429#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2430pub struct WebhookClientConfig {
2431    #[serde(skip_serializing_if = "Option::is_none")]
2432    pub url: Option<String>,
2433    #[serde(skip_serializing_if = "Option::is_none")]
2434    pub service: Option<WebhookServiceReference>,
2435    #[serde(skip_serializing_if = "Option::is_none")]
2436    pub ca_bundle: Option<String>,
2437}
2438
2439#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2440pub struct WebhookRule {
2441    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2442    pub api_groups: Vec<String>,
2443    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2444    pub api_versions: Vec<String>,
2445    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2446    pub resources: Vec<String>,
2447    #[serde(skip_serializing_if = "Option::is_none")]
2448    pub scope: Option<String>,
2449}
2450
2451#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2452pub struct WebhookRuleWithOperations {
2453    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2454    pub operations: Vec<String>,
2455    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2456    pub api_groups: Vec<String>,
2457    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2458    pub api_versions: Vec<String>,
2459    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2460    pub resources: Vec<String>,
2461    #[serde(skip_serializing_if = "Option::is_none")]
2462    pub scope: Option<String>,
2463}
2464
2465#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2466pub struct Webhook {
2467    pub name: String,
2468    pub client_config: WebhookClientConfig,
2469    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2470    pub rules: Vec<WebhookRuleWithOperations>,
2471    #[serde(skip_serializing_if = "Option::is_none")]
2472    pub failure_policy: Option<String>,
2473    #[serde(skip_serializing_if = "Option::is_none")]
2474    pub match_policy: Option<String>,
2475    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
2476    pub namespace_selector: HashMap<String, Value>,
2477    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
2478    pub object_selector: HashMap<String, Value>,
2479    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2480    pub admission_review_versions: Vec<String>,
2481    #[serde(skip_serializing_if = "Option::is_none")]
2482    pub side_effects: Option<String>,
2483    #[serde(skip_serializing_if = "Option::is_none")]
2484    pub timeout_seconds: Option<u32>,
2485}
2486
2487#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2488pub struct MutatingWebhookConfiguration {
2489    #[serde(skip_serializing_if = "Option::is_none")]
2490    pub api_version: Option<String>,
2491    #[serde(skip_serializing_if = "Option::is_none")]
2492    pub kind: Option<String>,
2493    pub metadata: ObjectMeta,
2494    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2495    pub webhooks: Vec<Webhook>,
2496}
2497
2498#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2499pub struct MutatingWebhookConfigurationList {
2500    #[serde(skip_serializing_if = "Option::is_none")]
2501    pub api_version: Option<String>,
2502    #[serde(skip_serializing_if = "Option::is_none")]
2503    pub kind: Option<String>,
2504    #[serde(default)]
2505    pub metadata: ListMeta,
2506    pub items: Vec<MutatingWebhookConfiguration>,
2507}
2508
2509#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2510pub struct ValidatingWebhookConfiguration {
2511    #[serde(skip_serializing_if = "Option::is_none")]
2512    pub api_version: Option<String>,
2513    #[serde(skip_serializing_if = "Option::is_none")]
2514    pub kind: Option<String>,
2515    pub metadata: ObjectMeta,
2516    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2517    pub webhooks: Vec<Webhook>,
2518}
2519
2520#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2521pub struct ValidatingWebhookConfigurationList {
2522    #[serde(skip_serializing_if = "Option::is_none")]
2523    pub api_version: Option<String>,
2524    #[serde(skip_serializing_if = "Option::is_none")]
2525    pub kind: Option<String>,
2526    #[serde(default)]
2527    pub metadata: ListMeta,
2528    pub items: Vec<ValidatingWebhookConfiguration>,
2529}
2530
2531#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2532pub struct CustomResourceDefinition {
2533    #[serde(skip_serializing_if = "Option::is_none")]
2534    pub api_version: Option<String>,
2535    #[serde(skip_serializing_if = "Option::is_none")]
2536    pub kind: Option<String>,
2537    pub metadata: ObjectMeta,
2538    #[serde(skip_serializing_if = "Option::is_none")]
2539    pub spec: Option<HashMap<String, Value>>,
2540    #[serde(skip_serializing_if = "Option::is_none")]
2541    pub status: Option<HashMap<String, Value>>,
2542}
2543
2544#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2545pub struct CustomResourceDefinitionList {
2546    #[serde(skip_serializing_if = "Option::is_none")]
2547    pub api_version: Option<String>,
2548    #[serde(skip_serializing_if = "Option::is_none")]
2549    pub kind: Option<String>,
2550    #[serde(default)]
2551    pub metadata: ListMeta,
2552    pub items: Vec<CustomResourceDefinition>,
2553}
2554
2555#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2556pub struct ResourceQuotaSpec {
2557    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
2558    pub hard: HashMap<String, String>,
2559}
2560
2561#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2562pub struct ResourceQuotaStatus {
2563    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
2564    pub hard: HashMap<String, String>,
2565    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
2566    pub used: HashMap<String, String>,
2567}
2568
2569#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2570pub struct ResourceQuota {
2571    #[serde(skip_serializing_if = "Option::is_none")]
2572    pub api_version: Option<String>,
2573    #[serde(skip_serializing_if = "Option::is_none")]
2574    pub kind: Option<String>,
2575    pub metadata: ObjectMeta,
2576    #[serde(skip_serializing_if = "Option::is_none")]
2577    pub spec: Option<ResourceQuotaSpec>,
2578    #[serde(skip_serializing_if = "Option::is_none")]
2579    pub status: Option<ResourceQuotaStatus>,
2580}
2581
2582#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2583pub struct ResourceQuotaList {
2584    #[serde(skip_serializing_if = "Option::is_none")]
2585    pub api_version: Option<String>,
2586    #[serde(skip_serializing_if = "Option::is_none")]
2587    pub kind: Option<String>,
2588    #[serde(default)]
2589    pub metadata: ListMeta,
2590    pub items: Vec<ResourceQuota>,
2591}
2592
2593#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2594pub struct LimitRangeItem {
2595    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
2596    pub limit_type: Option<String>,
2597    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
2598    pub max: HashMap<String, String>,
2599    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
2600    pub min: HashMap<String, String>,
2601    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
2602    pub default: HashMap<String, String>,
2603    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
2604    pub default_request: HashMap<String, String>,
2605    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
2606    pub max_limit_request_ratio: HashMap<String, String>,
2607}
2608
2609#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2610pub struct LimitRangeSpec {
2611    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2612    pub limits: Vec<LimitRangeItem>,
2613}
2614
2615#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2616pub struct LimitRange {
2617    #[serde(skip_serializing_if = "Option::is_none")]
2618    pub api_version: Option<String>,
2619    #[serde(skip_serializing_if = "Option::is_none")]
2620    pub kind: Option<String>,
2621    pub metadata: ObjectMeta,
2622    #[serde(skip_serializing_if = "Option::is_none")]
2623    pub spec: Option<LimitRangeSpec>,
2624}
2625
2626#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2627pub struct LimitRangeList {
2628    #[serde(skip_serializing_if = "Option::is_none")]
2629    pub api_version: Option<String>,
2630    #[serde(skip_serializing_if = "Option::is_none")]
2631    pub kind: Option<String>,
2632    #[serde(default)]
2633    pub metadata: ListMeta,
2634    pub items: Vec<LimitRange>,
2635}
2636
2637#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2638pub struct PodDisruptionBudgetSpec {
2639    #[serde(skip_serializing_if = "Option::is_none")]
2640    pub min_available: Option<String>,
2641    #[serde(skip_serializing_if = "Option::is_none")]
2642    pub max_unavailable: Option<String>,
2643    #[serde(skip_serializing_if = "Option::is_none")]
2644    pub selector: Option<LabelSelector>,
2645}
2646
2647#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2648pub struct PodDisruptionBudgetStatus {
2649    #[serde(skip_serializing_if = "Option::is_none")]
2650    pub current_healthy: Option<i32>,
2651    #[serde(skip_serializing_if = "Option::is_none")]
2652    pub desired_healthy: Option<i32>,
2653    #[serde(skip_serializing_if = "Option::is_none")]
2654    pub disruptions_allowed: Option<i32>,
2655}
2656
2657#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2658pub struct PodDisruptionBudget {
2659    #[serde(skip_serializing_if = "Option::is_none")]
2660    pub api_version: Option<String>,
2661    #[serde(skip_serializing_if = "Option::is_none")]
2662    pub kind: Option<String>,
2663    pub metadata: ObjectMeta,
2664    #[serde(skip_serializing_if = "Option::is_none")]
2665    pub spec: Option<PodDisruptionBudgetSpec>,
2666    #[serde(skip_serializing_if = "Option::is_none")]
2667    pub status: Option<PodDisruptionBudgetStatus>,
2668}
2669
2670#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2671pub struct PodDisruptionBudgetList {
2672    #[serde(skip_serializing_if = "Option::is_none")]
2673    pub api_version: Option<String>,
2674    #[serde(skip_serializing_if = "Option::is_none")]
2675    pub kind: Option<String>,
2676    #[serde(default)]
2677    pub metadata: ListMeta,
2678    pub items: Vec<PodDisruptionBudget>,
2679}
2680
2681#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2682pub struct PriorityClass {
2683    #[serde(skip_serializing_if = "Option::is_none")]
2684    pub api_version: Option<String>,
2685    #[serde(skip_serializing_if = "Option::is_none")]
2686    pub kind: Option<String>,
2687    pub metadata: ObjectMeta,
2688    pub value: i32,
2689    #[serde(skip_serializing_if = "Option::is_none")]
2690    pub global_default: Option<bool>,
2691    #[serde(skip_serializing_if = "Option::is_none")]
2692    pub description: Option<String>,
2693    #[serde(skip_serializing_if = "Option::is_none")]
2694    pub preemption_policy: Option<String>,
2695}
2696
2697#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2698pub struct PriorityClassList {
2699    #[serde(skip_serializing_if = "Option::is_none")]
2700    pub api_version: Option<String>,
2701    #[serde(skip_serializing_if = "Option::is_none")]
2702    pub kind: Option<String>,
2703    #[serde(default)]
2704    pub metadata: ListMeta,
2705    pub items: Vec<PriorityClass>,
2706}
2707
2708#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2709pub struct NetworkPolicySpec {
2710    #[serde(skip_serializing_if = "Option::is_none")]
2711    pub pod_selector: Option<LabelSelector>,
2712    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2713    pub policy_types: Vec<String>,
2714    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2715    pub ingress: Vec<Value>,
2716    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2717    pub egress: Vec<Value>,
2718}
2719
2720#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2721pub struct NetworkPolicy {
2722    #[serde(skip_serializing_if = "Option::is_none")]
2723    pub api_version: Option<String>,
2724    #[serde(skip_serializing_if = "Option::is_none")]
2725    pub kind: Option<String>,
2726    pub metadata: ObjectMeta,
2727    #[serde(skip_serializing_if = "Option::is_none")]
2728    pub spec: Option<NetworkPolicySpec>,
2729}
2730
2731#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2732pub struct NetworkPolicyList {
2733    #[serde(skip_serializing_if = "Option::is_none")]
2734    pub api_version: Option<String>,
2735    #[serde(skip_serializing_if = "Option::is_none")]
2736    pub kind: Option<String>,
2737    #[serde(default)]
2738    pub metadata: ListMeta,
2739    pub items: Vec<NetworkPolicy>,
2740}
2741
2742#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2743pub struct IngressSpec {
2744    #[serde(skip_serializing_if = "Option::is_none")]
2745    pub ingress_class_name: Option<String>,
2746    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2747    pub rules: Vec<Value>,
2748    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2749    pub tls: Vec<Value>,
2750}
2751
2752#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2753pub struct Ingress {
2754    #[serde(skip_serializing_if = "Option::is_none")]
2755    pub api_version: Option<String>,
2756    #[serde(skip_serializing_if = "Option::is_none")]
2757    pub kind: Option<String>,
2758    pub metadata: ObjectMeta,
2759    #[serde(skip_serializing_if = "Option::is_none")]
2760    pub spec: Option<IngressSpec>,
2761    #[serde(skip_serializing_if = "Option::is_none")]
2762    pub status: Option<HashMap<String, Value>>,
2763}
2764
2765#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2766pub struct IngressList {
2767    #[serde(skip_serializing_if = "Option::is_none")]
2768    pub api_version: Option<String>,
2769    #[serde(skip_serializing_if = "Option::is_none")]
2770    pub kind: Option<String>,
2771    #[serde(default)]
2772    pub metadata: ListMeta,
2773    pub items: Vec<Ingress>,
2774}
2775
2776#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2777pub struct IngressClassSpec {
2778    #[serde(skip_serializing_if = "Option::is_none")]
2779    pub controller: Option<String>,
2780    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
2781    pub parameters: HashMap<String, Value>,
2782}
2783
2784#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2785pub struct IngressClass {
2786    #[serde(skip_serializing_if = "Option::is_none")]
2787    pub api_version: Option<String>,
2788    #[serde(skip_serializing_if = "Option::is_none")]
2789    pub kind: Option<String>,
2790    pub metadata: ObjectMeta,
2791    #[serde(skip_serializing_if = "Option::is_none")]
2792    pub spec: Option<IngressClassSpec>,
2793}
2794
2795#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2796pub struct IngressClassList {
2797    #[serde(skip_serializing_if = "Option::is_none")]
2798    pub api_version: Option<String>,
2799    #[serde(skip_serializing_if = "Option::is_none")]
2800    pub kind: Option<String>,
2801    #[serde(default)]
2802    pub metadata: ListMeta,
2803    pub items: Vec<IngressClass>,
2804}
2805
2806#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2807pub struct GatewayClassSpec {
2808    #[serde(skip_serializing_if = "Option::is_none")]
2809    pub controller_name: Option<String>,
2810    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
2811    pub parameters_ref: HashMap<String, Value>,
2812}
2813
2814#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2815pub struct GatewayClass {
2816    #[serde(skip_serializing_if = "Option::is_none")]
2817    pub api_version: Option<String>,
2818    #[serde(skip_serializing_if = "Option::is_none")]
2819    pub kind: Option<String>,
2820    pub metadata: ObjectMeta,
2821    #[serde(skip_serializing_if = "Option::is_none")]
2822    pub spec: Option<GatewayClassSpec>,
2823    #[serde(skip_serializing_if = "Option::is_none")]
2824    pub status: Option<HashMap<String, Value>>,
2825}
2826
2827#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2828pub struct GatewayClassList {
2829    #[serde(skip_serializing_if = "Option::is_none")]
2830    pub api_version: Option<String>,
2831    #[serde(skip_serializing_if = "Option::is_none")]
2832    pub kind: Option<String>,
2833    #[serde(default)]
2834    pub metadata: ListMeta,
2835    pub items: Vec<GatewayClass>,
2836}
2837
2838#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2839pub struct GatewaySpec {
2840    #[serde(skip_serializing_if = "Option::is_none")]
2841    pub gateway_class_name: Option<String>,
2842    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2843    pub listeners: Vec<Value>,
2844}
2845
2846#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2847pub struct Gateway {
2848    #[serde(skip_serializing_if = "Option::is_none")]
2849    pub api_version: Option<String>,
2850    #[serde(skip_serializing_if = "Option::is_none")]
2851    pub kind: Option<String>,
2852    pub metadata: ObjectMeta,
2853    #[serde(skip_serializing_if = "Option::is_none")]
2854    pub spec: Option<GatewaySpec>,
2855    #[serde(skip_serializing_if = "Option::is_none")]
2856    pub status: Option<HashMap<String, Value>>,
2857}
2858
2859#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2860pub struct GatewayList {
2861    #[serde(skip_serializing_if = "Option::is_none")]
2862    pub api_version: Option<String>,
2863    #[serde(skip_serializing_if = "Option::is_none")]
2864    pub kind: Option<String>,
2865    #[serde(default)]
2866    pub metadata: ListMeta,
2867    pub items: Vec<Gateway>,
2868}
2869
2870#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2871pub struct HTTPRouteSpec {
2872    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2873    pub parent_refs: Vec<Value>,
2874    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2875    pub hostnames: Vec<String>,
2876    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2877    pub rules: Vec<Value>,
2878}
2879
2880#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2881pub struct HTTPRoute {
2882    #[serde(skip_serializing_if = "Option::is_none")]
2883    pub api_version: Option<String>,
2884    #[serde(skip_serializing_if = "Option::is_none")]
2885    pub kind: Option<String>,
2886    pub metadata: ObjectMeta,
2887    #[serde(skip_serializing_if = "Option::is_none")]
2888    pub spec: Option<HTTPRouteSpec>,
2889    #[serde(skip_serializing_if = "Option::is_none")]
2890    pub status: Option<HashMap<String, Value>>,
2891}
2892
2893#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2894pub struct HTTPRouteList {
2895    #[serde(skip_serializing_if = "Option::is_none")]
2896    pub api_version: Option<String>,
2897    #[serde(skip_serializing_if = "Option::is_none")]
2898    pub kind: Option<String>,
2899    #[serde(default)]
2900    pub metadata: ListMeta,
2901    pub items: Vec<HTTPRoute>,
2902}
2903
2904#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2905pub struct TCPRouteSpec {
2906    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2907    pub parent_refs: Vec<Value>,
2908    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2909    pub rules: Vec<Value>,
2910}
2911
2912#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2913pub struct TCPRoute {
2914    #[serde(skip_serializing_if = "Option::is_none")]
2915    pub api_version: Option<String>,
2916    #[serde(skip_serializing_if = "Option::is_none")]
2917    pub kind: Option<String>,
2918    pub metadata: ObjectMeta,
2919    #[serde(skip_serializing_if = "Option::is_none")]
2920    pub spec: Option<TCPRouteSpec>,
2921    #[serde(skip_serializing_if = "Option::is_none")]
2922    pub status: Option<HashMap<String, Value>>,
2923}
2924
2925#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2926pub struct TCPRouteList {
2927    #[serde(skip_serializing_if = "Option::is_none")]
2928    pub api_version: Option<String>,
2929    #[serde(skip_serializing_if = "Option::is_none")]
2930    pub kind: Option<String>,
2931    #[serde(default)]
2932    pub metadata: ListMeta,
2933    pub items: Vec<TCPRoute>,
2934}
2935
2936#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2937pub struct TLSRouteSpec {
2938    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2939    pub parent_refs: Vec<Value>,
2940    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2941    pub rules: Vec<Value>,
2942}
2943
2944#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2945pub struct TLSRoute {
2946    #[serde(skip_serializing_if = "Option::is_none")]
2947    pub api_version: Option<String>,
2948    #[serde(skip_serializing_if = "Option::is_none")]
2949    pub kind: Option<String>,
2950    pub metadata: ObjectMeta,
2951    #[serde(skip_serializing_if = "Option::is_none")]
2952    pub spec: Option<TLSRouteSpec>,
2953    #[serde(skip_serializing_if = "Option::is_none")]
2954    pub status: Option<HashMap<String, Value>>,
2955}
2956
2957#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2958pub struct TLSRouteList {
2959    #[serde(skip_serializing_if = "Option::is_none")]
2960    pub api_version: Option<String>,
2961    #[serde(skip_serializing_if = "Option::is_none")]
2962    pub kind: Option<String>,
2963    #[serde(default)]
2964    pub metadata: ListMeta,
2965    pub items: Vec<TLSRoute>,
2966}
2967
2968#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2969pub struct UDPRouteSpec {
2970    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2971    pub parent_refs: Vec<Value>,
2972    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2973    pub rules: Vec<Value>,
2974}
2975
2976#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2977pub struct UDPRoute {
2978    #[serde(skip_serializing_if = "Option::is_none")]
2979    pub api_version: Option<String>,
2980    #[serde(skip_serializing_if = "Option::is_none")]
2981    pub kind: Option<String>,
2982    pub metadata: ObjectMeta,
2983    #[serde(skip_serializing_if = "Option::is_none")]
2984    pub spec: Option<UDPRouteSpec>,
2985    #[serde(skip_serializing_if = "Option::is_none")]
2986    pub status: Option<HashMap<String, Value>>,
2987}
2988
2989#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
2990pub struct UDPRouteList {
2991    #[serde(skip_serializing_if = "Option::is_none")]
2992    pub api_version: Option<String>,
2993    #[serde(skip_serializing_if = "Option::is_none")]
2994    pub kind: Option<String>,
2995    #[serde(default)]
2996    pub metadata: ListMeta,
2997    pub items: Vec<UDPRoute>,
2998}
2999
3000#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3001pub struct GRPCRouteSpec {
3002    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3003    pub parent_refs: Vec<Value>,
3004    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3005    pub rules: Vec<Value>,
3006}
3007
3008#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3009pub struct GRPCRoute {
3010    #[serde(skip_serializing_if = "Option::is_none")]
3011    pub api_version: Option<String>,
3012    #[serde(skip_serializing_if = "Option::is_none")]
3013    pub kind: Option<String>,
3014    pub metadata: ObjectMeta,
3015    #[serde(skip_serializing_if = "Option::is_none")]
3016    pub spec: Option<GRPCRouteSpec>,
3017    #[serde(skip_serializing_if = "Option::is_none")]
3018    pub status: Option<HashMap<String, Value>>,
3019}
3020
3021#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3022pub struct GRPCRouteList {
3023    #[serde(skip_serializing_if = "Option::is_none")]
3024    pub api_version: Option<String>,
3025    #[serde(skip_serializing_if = "Option::is_none")]
3026    pub kind: Option<String>,
3027    #[serde(default)]
3028    pub metadata: ListMeta,
3029    pub items: Vec<GRPCRoute>,
3030}
3031
3032#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3033pub struct ReferenceGrantSpec {
3034    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3035    pub from: Vec<Value>,
3036    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3037    pub to: Vec<Value>,
3038}
3039
3040#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3041pub struct ReferenceGrant {
3042    #[serde(skip_serializing_if = "Option::is_none")]
3043    pub api_version: Option<String>,
3044    #[serde(skip_serializing_if = "Option::is_none")]
3045    pub kind: Option<String>,
3046    pub metadata: ObjectMeta,
3047    #[serde(skip_serializing_if = "Option::is_none")]
3048    pub spec: Option<ReferenceGrantSpec>,
3049}
3050
3051#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3052pub struct ReferenceGrantList {
3053    #[serde(skip_serializing_if = "Option::is_none")]
3054    pub api_version: Option<String>,
3055    #[serde(skip_serializing_if = "Option::is_none")]
3056    pub kind: Option<String>,
3057    #[serde(default)]
3058    pub metadata: ListMeta,
3059    pub items: Vec<ReferenceGrant>,
3060}
3061
3062#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3063pub struct StorageClass {
3064    #[serde(skip_serializing_if = "Option::is_none")]
3065    pub api_version: Option<String>,
3066    #[serde(skip_serializing_if = "Option::is_none")]
3067    pub kind: Option<String>,
3068    pub metadata: ObjectMeta,
3069    pub provisioner: String,
3070    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
3071    pub parameters: HashMap<String, String>,
3072    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3073    pub mount_options: Vec<String>,
3074    #[serde(skip_serializing_if = "Option::is_none")]
3075    pub reclaim_policy: Option<String>,
3076    #[serde(skip_serializing_if = "Option::is_none")]
3077    pub volume_binding_mode: Option<String>,
3078    #[serde(skip_serializing_if = "Option::is_none")]
3079    pub allow_volume_expansion: Option<bool>,
3080}
3081
3082#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3083pub struct StorageClassList {
3084    #[serde(skip_serializing_if = "Option::is_none")]
3085    pub api_version: Option<String>,
3086    #[serde(skip_serializing_if = "Option::is_none")]
3087    pub kind: Option<String>,
3088    #[serde(default)]
3089    pub metadata: ListMeta,
3090    pub items: Vec<StorageClass>,
3091}
3092
3093#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3094pub struct CsiDriverSpec {
3095    #[serde(skip_serializing_if = "Option::is_none")]
3096    pub attach_required: Option<bool>,
3097    #[serde(skip_serializing_if = "Option::is_none")]
3098    pub pod_info_on_mount: Option<bool>,
3099    #[serde(skip_serializing_if = "Option::is_none")]
3100    pub fs_group_policy: Option<String>,
3101    #[serde(skip_serializing_if = "Option::is_none")]
3102    pub storage_capacity: Option<bool>,
3103    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3104    pub volume_lifecycle_modes: Vec<String>,
3105}
3106
3107#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3108pub struct CsiDriver {
3109    #[serde(skip_serializing_if = "Option::is_none")]
3110    pub api_version: Option<String>,
3111    #[serde(skip_serializing_if = "Option::is_none")]
3112    pub kind: Option<String>,
3113    pub metadata: ObjectMeta,
3114    #[serde(skip_serializing_if = "Option::is_none")]
3115    pub spec: Option<CsiDriverSpec>,
3116}
3117
3118#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3119pub struct CsiDriverList {
3120    #[serde(skip_serializing_if = "Option::is_none")]
3121    pub api_version: Option<String>,
3122    #[serde(skip_serializing_if = "Option::is_none")]
3123    pub kind: Option<String>,
3124    #[serde(default)]
3125    pub metadata: ListMeta,
3126    pub items: Vec<CsiDriver>,
3127}
3128
3129#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3130pub struct PersistentVolumeSpec {
3131    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
3132    pub capacity: HashMap<String, String>,
3133    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3134    pub access_modes: Vec<String>,
3135    #[serde(skip_serializing_if = "Option::is_none")]
3136    pub storage_class_name: Option<String>,
3137    #[serde(skip_serializing_if = "Option::is_none")]
3138    pub claim_ref: Option<ObjectReference>,
3139    #[serde(skip_serializing_if = "Option::is_none")]
3140    pub volume_mode: Option<String>,
3141    #[serde(skip_serializing_if = "Option::is_none")]
3142    pub persistent_volume_reclaim_policy: Option<String>,
3143    #[serde(skip_serializing_if = "Option::is_none")]
3144    pub volume_source: Option<Value>,
3145}
3146
3147#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3148pub struct PersistentVolumeStatus {
3149    #[serde(skip_serializing_if = "Option::is_none")]
3150    pub phase: Option<String>,
3151    #[serde(skip_serializing_if = "Option::is_none")]
3152    pub message: Option<String>,
3153    #[serde(skip_serializing_if = "Option::is_none")]
3154    pub reason: Option<String>,
3155    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
3156    pub capacity: HashMap<String, String>,
3157}
3158
3159#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3160pub struct PersistentVolume {
3161    #[serde(skip_serializing_if = "Option::is_none")]
3162    pub api_version: Option<String>,
3163    #[serde(skip_serializing_if = "Option::is_none")]
3164    pub kind: Option<String>,
3165    pub metadata: ObjectMeta,
3166    #[serde(skip_serializing_if = "Option::is_none")]
3167    pub spec: Option<PersistentVolumeSpec>,
3168    #[serde(skip_serializing_if = "Option::is_none")]
3169    pub status: Option<PersistentVolumeStatus>,
3170}
3171
3172#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3173pub struct PersistentVolumeList {
3174    #[serde(skip_serializing_if = "Option::is_none")]
3175    pub api_version: Option<String>,
3176    #[serde(skip_serializing_if = "Option::is_none")]
3177    pub kind: Option<String>,
3178    #[serde(default)]
3179    pub metadata: ListMeta,
3180    pub items: Vec<PersistentVolume>,
3181}
3182
3183#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3184pub struct PersistentVolumeClaimSpec {
3185    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3186    pub access_modes: Vec<String>,
3187    #[serde(skip_serializing_if = "Option::is_none")]
3188    pub storage_class_name: Option<String>,
3189    #[serde(skip_serializing_if = "Option::is_none")]
3190    pub volume_name: Option<String>,
3191    #[serde(skip_serializing_if = "Option::is_none")]
3192    pub resources: Option<ResourceRequirements>,
3193    #[serde(skip_serializing_if = "Option::is_none")]
3194    pub volume_mode: Option<String>,
3195    #[serde(skip_serializing_if = "Option::is_none")]
3196    pub selector: Option<LabelSelector>,
3197}
3198
3199#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3200pub struct PersistentVolumeClaimStatus {
3201    #[serde(skip_serializing_if = "Option::is_none")]
3202    pub phase: Option<String>,
3203    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3204    pub access_modes: Vec<String>,
3205    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
3206    pub capacity: HashMap<String, String>,
3207    #[serde(skip_serializing_if = "Option::is_none")]
3208    pub volume_name: Option<String>,
3209}
3210
3211#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3212pub struct PersistentVolumeClaim {
3213    #[serde(skip_serializing_if = "Option::is_none")]
3214    pub api_version: Option<String>,
3215    #[serde(skip_serializing_if = "Option::is_none")]
3216    pub kind: Option<String>,
3217    pub metadata: ObjectMeta,
3218    #[serde(skip_serializing_if = "Option::is_none")]
3219    pub spec: Option<PersistentVolumeClaimSpec>,
3220    #[serde(skip_serializing_if = "Option::is_none")]
3221    pub status: Option<PersistentVolumeClaimStatus>,
3222}
3223
3224#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3225pub struct PersistentVolumeClaimList {
3226    #[serde(skip_serializing_if = "Option::is_none")]
3227    pub api_version: Option<String>,
3228    #[serde(skip_serializing_if = "Option::is_none")]
3229    pub kind: Option<String>,
3230    #[serde(default)]
3231    pub metadata: ListMeta,
3232    pub items: Vec<PersistentVolumeClaim>,
3233}
3234
3235#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3236pub struct VolumeSnapshotSource {
3237    #[serde(skip_serializing_if = "Option::is_none")]
3238    pub persistent_volume_claim_name: Option<String>,
3239    #[serde(skip_serializing_if = "Option::is_none")]
3240    pub volume_snapshot_content_name: Option<String>,
3241}
3242
3243#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3244pub struct VolumeSnapshotSpec {
3245    #[serde(skip_serializing_if = "Option::is_none")]
3246    pub volume_snapshot_class_name: Option<String>,
3247    pub source: VolumeSnapshotSource,
3248}
3249
3250#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3251pub struct VolumeSnapshotStatus {
3252    #[serde(skip_serializing_if = "Option::is_none")]
3253    pub ready_to_use: Option<bool>,
3254    #[serde(skip_serializing_if = "Option::is_none")]
3255    pub creation_time: Option<DateTime<Utc>>,
3256    #[serde(skip_serializing_if = "Option::is_none")]
3257    pub restore_size: Option<String>,
3258    #[serde(skip_serializing_if = "Option::is_none")]
3259    pub bound_volume_snapshot_content_name: Option<String>,
3260}
3261
3262#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3263pub struct VolumeSnapshot {
3264    #[serde(skip_serializing_if = "Option::is_none")]
3265    pub api_version: Option<String>,
3266    #[serde(skip_serializing_if = "Option::is_none")]
3267    pub kind: Option<String>,
3268    pub metadata: ObjectMeta,
3269    #[serde(skip_serializing_if = "Option::is_none")]
3270    pub spec: Option<VolumeSnapshotSpec>,
3271    #[serde(skip_serializing_if = "Option::is_none")]
3272    pub status: Option<VolumeSnapshotStatus>,
3273}
3274
3275#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3276pub struct VolumeSnapshotList {
3277    #[serde(skip_serializing_if = "Option::is_none")]
3278    pub api_version: Option<String>,
3279    #[serde(skip_serializing_if = "Option::is_none")]
3280    pub kind: Option<String>,
3281    #[serde(default)]
3282    pub metadata: ListMeta,
3283    pub items: Vec<VolumeSnapshot>,
3284}
3285
3286#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3287pub struct Volume {
3288    #[serde(skip_serializing_if = "Option::is_none")]
3289    pub name: Option<String>,
3290    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
3291    pub volume_type: Option<String>,
3292    #[serde(skip_serializing_if = "Option::is_none")]
3293    pub source: Option<String>,
3294}
3295
3296#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3297pub struct VirtualMachineDomain {
3298    #[serde(skip_serializing_if = "Option::is_none")]
3299    pub cpu: Option<String>,
3300    #[serde(skip_serializing_if = "Option::is_none")]
3301    pub memory: Option<String>,
3302    #[serde(skip_serializing_if = "Option::is_none")]
3303    pub devices: Option<HashMap<String, Value>>,
3304}
3305
3306#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3307pub struct VirtualMachineInstanceSpec {
3308    #[serde(skip_serializing_if = "Option::is_none")]
3309    pub domain: Option<VirtualMachineDomain>,
3310    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
3311    pub node_selector: HashMap<String, String>,
3312    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3313    pub networks: Vec<String>,
3314    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3315    pub volumes: Vec<Volume>,
3316}
3317
3318#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3319pub struct VirtualMachineTemplateSpec {
3320    #[serde(skip_serializing_if = "Option::is_none")]
3321    pub metadata: Option<ObjectMeta>,
3322    #[serde(skip_serializing_if = "Option::is_none")]
3323    pub spec: Option<VirtualMachineInstanceSpec>,
3324}
3325
3326#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3327pub struct VirtualMachineSpec {
3328    #[serde(skip_serializing_if = "Option::is_none")]
3329    pub run_strategy: Option<String>,
3330    #[serde(skip_serializing_if = "Option::is_none")]
3331    pub template: Option<VirtualMachineTemplateSpec>,
3332    #[serde(skip_serializing_if = "Option::is_none")]
3333    pub cpu: Option<String>,
3334    #[serde(skip_serializing_if = "Option::is_none")]
3335    pub memory: Option<String>,
3336    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3337    pub disks: Vec<Volume>,
3338    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3339    pub networks: Vec<String>,
3340}
3341
3342#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3343pub struct VirtualMachine {
3344    #[serde(skip_serializing_if = "Option::is_none")]
3345    pub api_version: Option<String>,
3346    #[serde(skip_serializing_if = "Option::is_none")]
3347    pub kind: Option<String>,
3348    pub metadata: ObjectMeta,
3349    #[serde(skip_serializing_if = "Option::is_none")]
3350    pub spec: Option<VirtualMachineSpec>,
3351    #[serde(skip_serializing_if = "Option::is_none")]
3352    pub status: Option<HashMap<String, Value>>,
3353}
3354
3355#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3356pub struct VirtualMachineList {
3357    #[serde(skip_serializing_if = "Option::is_none")]
3358    pub api_version: Option<String>,
3359    #[serde(skip_serializing_if = "Option::is_none")]
3360    pub kind: Option<String>,
3361    #[serde(default)]
3362    pub metadata: ListMeta,
3363    pub items: Vec<VirtualMachine>,
3364}
3365
3366#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3367pub struct VirtualMachineInstance {
3368    #[serde(skip_serializing_if = "Option::is_none")]
3369    pub api_version: Option<String>,
3370    #[serde(skip_serializing_if = "Option::is_none")]
3371    pub kind: Option<String>,
3372    pub metadata: ObjectMeta,
3373    #[serde(skip_serializing_if = "Option::is_none")]
3374    pub spec: Option<VirtualMachineInstanceSpec>,
3375    #[serde(skip_serializing_if = "Option::is_none")]
3376    pub status: Option<HashMap<String, Value>>,
3377}
3378
3379#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3380pub struct VirtualMachineInstanceList {
3381    #[serde(skip_serializing_if = "Option::is_none")]
3382    pub api_version: Option<String>,
3383    #[serde(skip_serializing_if = "Option::is_none")]
3384    pub kind: Option<String>,
3385    #[serde(default)]
3386    pub metadata: ListMeta,
3387    pub items: Vec<VirtualMachineInstance>,
3388}
3389
3390#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3391pub struct VmSnapshotSpec {
3392    #[serde(skip_serializing_if = "Option::is_none")]
3393    pub source_name: Option<String>,
3394}
3395
3396#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3397pub struct VmSnapshot {
3398    #[serde(skip_serializing_if = "Option::is_none")]
3399    pub api_version: Option<String>,
3400    #[serde(skip_serializing_if = "Option::is_none")]
3401    pub kind: Option<String>,
3402    pub metadata: ObjectMeta,
3403    #[serde(skip_serializing_if = "Option::is_none")]
3404    pub spec: Option<VmSnapshotSpec>,
3405    #[serde(skip_serializing_if = "Option::is_none")]
3406    pub status: Option<HashMap<String, Value>>,
3407}
3408
3409#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3410pub struct VmSnapshotList {
3411    #[serde(skip_serializing_if = "Option::is_none")]
3412    pub api_version: Option<String>,
3413    #[serde(skip_serializing_if = "Option::is_none")]
3414    pub kind: Option<String>,
3415    #[serde(default)]
3416    pub metadata: ListMeta,
3417    pub items: Vec<VmSnapshot>,
3418}
3419
3420#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3421pub struct VmMemoryDumpSpec {
3422    #[serde(skip_serializing_if = "Option::is_none")]
3423    pub source_name: Option<String>,
3424    pub file: CreateFileRequest,
3425}
3426
3427#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3428pub struct VmMemoryDumpStatus {
3429    #[serde(skip_serializing_if = "Option::is_none")]
3430    pub ready: Option<bool>,
3431    #[serde(skip_serializing_if = "Option::is_none")]
3432    pub file_id: Option<Uuid>,
3433    #[serde(skip_serializing_if = "Option::is_none")]
3434    pub file_name: Option<String>,
3435    #[serde(skip_serializing_if = "Option::is_none")]
3436    pub size_bytes: Option<i64>,
3437    #[serde(skip_serializing_if = "Option::is_none")]
3438    pub content_type: Option<String>,
3439    #[serde(skip_serializing_if = "Option::is_none")]
3440    pub created_at: Option<DateTime<Utc>>,
3441    #[serde(skip_serializing_if = "Option::is_none")]
3442    pub message: Option<String>,
3443}
3444
3445#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3446pub struct VmMemoryDump {
3447    #[serde(skip_serializing_if = "Option::is_none")]
3448    pub api_version: Option<String>,
3449    #[serde(skip_serializing_if = "Option::is_none")]
3450    pub kind: Option<String>,
3451    pub metadata: ObjectMeta,
3452    #[serde(skip_serializing_if = "Option::is_none")]
3453    pub spec: Option<VmMemoryDumpSpec>,
3454    #[serde(skip_serializing_if = "Option::is_none")]
3455    pub status: Option<VmMemoryDumpStatus>,
3456}
3457
3458#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3459pub struct VmMemoryDumpList {
3460    #[serde(skip_serializing_if = "Option::is_none")]
3461    pub api_version: Option<String>,
3462    #[serde(skip_serializing_if = "Option::is_none")]
3463    pub kind: Option<String>,
3464    #[serde(default)]
3465    pub metadata: ListMeta,
3466    pub items: Vec<VmMemoryDump>,
3467}
3468
3469#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3470pub struct NetworkCaptureTarget {
3471    pub kind: String,
3472    #[serde(skip_serializing_if = "Option::is_none")]
3473    pub name: Option<String>,
3474    #[serde(skip_serializing_if = "Option::is_none")]
3475    pub namespace: Option<String>,
3476    #[serde(skip_serializing_if = "Option::is_none")]
3477    pub container_id: Option<String>,
3478    #[serde(skip_serializing_if = "Option::is_none")]
3479    pub pod_name: Option<String>,
3480    #[serde(skip_serializing_if = "Option::is_none")]
3481    pub container_name: Option<String>,
3482}
3483
3484#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3485pub struct NetworkCaptureFilter {
3486    #[serde(skip_serializing_if = "Option::is_none")]
3487    pub bpf: Option<String>,
3488    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3489    pub protocols: Vec<String>,
3490    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3491    pub ports: Vec<u16>,
3492    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3493    pub cidrs: Vec<String>,
3494}
3495
3496#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3497pub struct NetworkCaptureRotation {
3498    #[serde(skip_serializing_if = "Option::is_none")]
3499    pub enabled: Option<bool>,
3500    #[serde(skip_serializing_if = "Option::is_none")]
3501    pub max_bytes: Option<i64>,
3502    #[serde(skip_serializing_if = "Option::is_none")]
3503    pub max_duration_seconds: Option<u64>,
3504    #[serde(skip_serializing_if = "Option::is_none")]
3505    pub max_files: Option<u32>,
3506}
3507
3508#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3509pub struct NetworkCaptureCaptureOptions {
3510    #[serde(skip_serializing_if = "Option::is_none")]
3511    pub format: Option<String>,
3512    #[serde(skip_serializing_if = "Option::is_none")]
3513    pub snaplen: Option<u32>,
3514    #[serde(skip_serializing_if = "Option::is_none")]
3515    pub max_packets: Option<u64>,
3516    #[serde(skip_serializing_if = "Option::is_none")]
3517    pub max_bytes: Option<i64>,
3518    #[serde(skip_serializing_if = "Option::is_none")]
3519    pub rotation: Option<NetworkCaptureRotation>,
3520    #[serde(skip_serializing_if = "Option::is_none")]
3521    pub compression: Option<String>,
3522}
3523
3524#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3525pub struct NetworkCaptureLifecycle {
3526    #[serde(skip_serializing_if = "Option::is_none")]
3527    pub desired_state: Option<String>,
3528    #[serde(skip_serializing_if = "Option::is_none")]
3529    pub stop_at: Option<DateTime<Utc>>,
3530    #[serde(skip_serializing_if = "Option::is_none")]
3531    pub ttl_seconds_after_finished: Option<u64>,
3532}
3533
3534#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3535pub struct NetworkCaptureSegment {
3536    #[serde(skip_serializing_if = "Option::is_none")]
3537    pub index: Option<u32>,
3538    #[serde(skip_serializing_if = "Option::is_none")]
3539    pub file_id: Option<Uuid>,
3540    #[serde(skip_serializing_if = "Option::is_none")]
3541    pub size_bytes: Option<i64>,
3542    #[serde(skip_serializing_if = "Option::is_none")]
3543    pub started_at: Option<DateTime<Utc>>,
3544    #[serde(skip_serializing_if = "Option::is_none")]
3545    pub finished_at: Option<DateTime<Utc>>,
3546}
3547
3548#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3549pub struct NetworkCaptureSpec {
3550    pub target: NetworkCaptureTarget,
3551    #[serde(skip_serializing_if = "Option::is_none")]
3552    pub direction: Option<String>,
3553    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3554    pub interfaces: Vec<String>,
3555    #[serde(skip_serializing_if = "Option::is_none")]
3556    pub filter: Option<NetworkCaptureFilter>,
3557    #[serde(skip_serializing_if = "Option::is_none")]
3558    pub capture: Option<NetworkCaptureCaptureOptions>,
3559    pub file: CaptureFileRequest,
3560    #[serde(skip_serializing_if = "Option::is_none")]
3561    pub run_id: Option<Uuid>,
3562    #[serde(skip_serializing_if = "Option::is_none")]
3563    pub lifecycle: Option<NetworkCaptureLifecycle>,
3564}
3565
3566#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3567pub struct NetworkCaptureStatus {
3568    #[serde(skip_serializing_if = "Option::is_none")]
3569    pub phase: Option<String>,
3570    #[serde(skip_serializing_if = "Option::is_none")]
3571    pub message: Option<String>,
3572    #[serde(skip_serializing_if = "Option::is_none")]
3573    pub started_at: Option<DateTime<Utc>>,
3574    #[serde(skip_serializing_if = "Option::is_none")]
3575    pub finished_at: Option<DateTime<Utc>>,
3576    #[serde(skip_serializing_if = "Option::is_none")]
3577    pub bytes_captured: Option<i64>,
3578    #[serde(skip_serializing_if = "Option::is_none")]
3579    pub packets_captured: Option<i64>,
3580    #[serde(skip_serializing_if = "Option::is_none")]
3581    pub file_id: Option<Uuid>,
3582    #[serde(skip_serializing_if = "Option::is_none")]
3583    pub file_name: Option<String>,
3584    #[serde(skip_serializing_if = "Option::is_none")]
3585    pub content_type: Option<String>,
3586    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3587    pub segments: Vec<NetworkCaptureSegment>,
3588}
3589
3590#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3591pub struct NetworkCapture {
3592    #[serde(skip_serializing_if = "Option::is_none")]
3593    pub api_version: Option<String>,
3594    #[serde(skip_serializing_if = "Option::is_none")]
3595    pub kind: Option<String>,
3596    pub metadata: ObjectMeta,
3597    #[serde(skip_serializing_if = "Option::is_none")]
3598    pub spec: Option<NetworkCaptureSpec>,
3599    #[serde(skip_serializing_if = "Option::is_none")]
3600    pub status: Option<NetworkCaptureStatus>,
3601}
3602
3603#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3604pub struct NetworkCaptureList {
3605    #[serde(skip_serializing_if = "Option::is_none")]
3606    pub api_version: Option<String>,
3607    #[serde(skip_serializing_if = "Option::is_none")]
3608    pub kind: Option<String>,
3609    #[serde(default)]
3610    pub metadata: ListMeta,
3611    pub items: Vec<NetworkCapture>,
3612}
3613
3614#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3615pub struct VmMigrationSpec {
3616    #[serde(skip_serializing_if = "Option::is_none")]
3617    pub vmi_name: Option<String>,
3618    #[serde(skip_serializing_if = "Option::is_none")]
3619    pub target_node: Option<String>,
3620}
3621
3622#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3623pub struct VmMigration {
3624    #[serde(skip_serializing_if = "Option::is_none")]
3625    pub api_version: Option<String>,
3626    #[serde(skip_serializing_if = "Option::is_none")]
3627    pub kind: Option<String>,
3628    pub metadata: ObjectMeta,
3629    #[serde(skip_serializing_if = "Option::is_none")]
3630    pub spec: Option<VmMigrationSpec>,
3631    #[serde(skip_serializing_if = "Option::is_none")]
3632    pub status: Option<HashMap<String, Value>>,
3633}
3634
3635#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3636pub struct VmMigrationList {
3637    #[serde(skip_serializing_if = "Option::is_none")]
3638    pub api_version: Option<String>,
3639    #[serde(skip_serializing_if = "Option::is_none")]
3640    pub kind: Option<String>,
3641    #[serde(default)]
3642    pub metadata: ListMeta,
3643    pub items: Vec<VmMigration>,
3644}
3645
3646#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3647pub struct ConsoleSession {
3648    pub protocol: String,
3649    pub url: String,
3650}
3651
3652#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3653pub struct ContainerPort {
3654    #[serde(skip_serializing_if = "Option::is_none")]
3655    pub name: Option<String>,
3656    pub container_port: u16,
3657    #[serde(skip_serializing_if = "Option::is_none")]
3658    pub protocol: Option<String>,
3659}
3660
3661#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3662pub struct EnvVar {
3663    pub name: String,
3664    #[serde(skip_serializing_if = "Option::is_none")]
3665    pub value: Option<String>,
3666}
3667
3668#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, Default)]
3669pub struct ResourceRequirements {
3670    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
3671    pub limits: HashMap<String, String>,
3672    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
3673    pub requests: HashMap<String, String>,
3674}
3675
3676#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3677pub struct Toleration {
3678    #[serde(skip_serializing_if = "Option::is_none")]
3679    pub key: Option<String>,
3680    #[serde(skip_serializing_if = "Option::is_none")]
3681    pub operator: Option<String>,
3682    #[serde(skip_serializing_if = "Option::is_none")]
3683    pub value: Option<String>,
3684    #[serde(skip_serializing_if = "Option::is_none")]
3685    pub effect: Option<String>,
3686    #[serde(skip_serializing_if = "Option::is_none")]
3687    pub toleration_seconds: Option<i64>,
3688}
3689
3690#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3691pub struct PreferredSchedulingTerm {
3692    pub weight: i32,
3693    pub preference: LabelSelector,
3694}
3695
3696#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3697pub struct NodeAffinity {
3698    #[serde(skip_serializing_if = "Option::is_none")]
3699    pub required: Option<LabelSelector>,
3700    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3701    pub preferred: Vec<PreferredSchedulingTerm>,
3702}
3703
3704#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3705pub struct PodAffinityTerm {
3706    pub label_selector: LabelSelector,
3707    pub topology_key: String,
3708}
3709
3710#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3711pub struct WeightedPodAffinityTerm {
3712    pub weight: i32,
3713    pub pod_affinity_term: PodAffinityTerm,
3714}
3715
3716#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3717pub struct PodAffinity {
3718    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3719    pub required: Vec<PodAffinityTerm>,
3720    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3721    pub preferred: Vec<WeightedPodAffinityTerm>,
3722}
3723
3724#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3725pub struct Affinity {
3726    #[serde(skip_serializing_if = "Option::is_none")]
3727    pub node_affinity: Option<NodeAffinity>,
3728    #[serde(skip_serializing_if = "Option::is_none")]
3729    pub pod_affinity: Option<PodAffinity>,
3730    #[serde(skip_serializing_if = "Option::is_none")]
3731    pub pod_anti_affinity: Option<PodAffinity>,
3732}
3733
3734#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3735pub struct PodContainer {
3736    pub name: String,
3737    #[serde(skip_serializing_if = "Option::is_none")]
3738    pub image: Option<String>,
3739    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3740    pub command: Vec<String>,
3741    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3742    pub args: Vec<String>,
3743    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3744    pub env: Vec<EnvVar>,
3745    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3746    pub ports: Vec<ContainerPort>,
3747    #[serde(default, skip_serializing_if = "Option::is_none")]
3748    pub resources: Option<ResourceRequirements>,
3749}
3750
3751#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3752pub struct PodSpec {
3753    #[serde(skip_serializing_if = "Option::is_none")]
3754    pub service_account_name: Option<String>,
3755    #[serde(skip_serializing_if = "Option::is_none")]
3756    pub node_name: Option<String>,
3757    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
3758    pub node_selector: HashMap<String, String>,
3759    #[serde(skip_serializing_if = "Option::is_none")]
3760    pub priority_class_name: Option<String>,
3761    #[serde(skip_serializing_if = "Option::is_none")]
3762    pub priority: Option<i32>,
3763    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3764    pub tolerations: Vec<Toleration>,
3765    #[serde(skip_serializing_if = "Option::is_none")]
3766    pub affinity: Option<Affinity>,
3767    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3768    pub containers: Vec<PodContainer>,
3769    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3770    pub volumes: Vec<Value>,
3771}
3772
3773#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3774pub struct PodContainerStatus {
3775    pub name: String,
3776    #[serde(skip_serializing_if = "Option::is_none")]
3777    pub container_id: Option<String>,
3778    #[serde(skip_serializing_if = "Option::is_none")]
3779    pub ready: Option<bool>,
3780    #[serde(skip_serializing_if = "Option::is_none")]
3781    pub restart_count: Option<i32>,
3782}
3783
3784#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3785pub struct PodStatus {
3786    #[serde(skip_serializing_if = "Option::is_none")]
3787    pub phase: Option<String>,
3788    #[serde(skip_serializing_if = "Option::is_none")]
3789    pub pod_ip: Option<String>,
3790    #[serde(skip_serializing_if = "Option::is_none")]
3791    pub host_ip: Option<String>,
3792    #[serde(skip_serializing_if = "Option::is_none")]
3793    pub qos_class: Option<String>,
3794    #[serde(skip_serializing_if = "Option::is_none")]
3795    pub start_time: Option<DateTime<Utc>>,
3796    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3797    pub container_statuses: Vec<PodContainerStatus>,
3798    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3799    pub conditions: Vec<PodCondition>,
3800}
3801
3802#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3803pub struct PodCondition {
3804    #[serde(rename = "type")]
3805    pub condition_type: String,
3806    pub status: String,
3807    #[serde(skip_serializing_if = "Option::is_none")]
3808    pub reason: Option<String>,
3809    #[serde(skip_serializing_if = "Option::is_none")]
3810    pub message: Option<String>,
3811    #[serde(skip_serializing_if = "Option::is_none")]
3812    pub last_probe_time: Option<DateTime<Utc>>,
3813    #[serde(skip_serializing_if = "Option::is_none")]
3814    pub last_transition_time: Option<DateTime<Utc>>,
3815}
3816
3817#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3818pub struct PodTemplateSpec {
3819    pub metadata: ObjectMeta,
3820    pub spec: PodSpec,
3821}
3822
3823#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3824pub struct Pod {
3825    #[serde(skip_serializing_if = "Option::is_none")]
3826    pub api_version: Option<String>,
3827    #[serde(skip_serializing_if = "Option::is_none")]
3828    pub kind: Option<String>,
3829    pub metadata: ObjectMeta,
3830    pub spec: PodSpec,
3831    #[serde(skip_serializing_if = "Option::is_none")]
3832    pub status: Option<PodStatus>,
3833}
3834
3835#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3836pub struct PodStatusUpdate {
3837    #[serde(skip_serializing_if = "Option::is_none")]
3838    pub api_version: Option<String>,
3839    #[serde(skip_serializing_if = "Option::is_none")]
3840    pub kind: Option<String>,
3841    pub metadata: ObjectMeta,
3842    #[serde(skip_serializing_if = "Option::is_none")]
3843    pub status: Option<PodStatus>,
3844}
3845
3846#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3847pub struct PodList {
3848    #[serde(skip_serializing_if = "Option::is_none")]
3849    pub api_version: Option<String>,
3850    #[serde(skip_serializing_if = "Option::is_none")]
3851    pub kind: Option<String>,
3852    #[serde(default)]
3853    pub metadata: ListMeta,
3854    pub items: Vec<Pod>,
3855}
3856
3857#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3858pub struct DeploymentSpec {
3859    #[serde(skip_serializing_if = "Option::is_none")]
3860    pub replicas: Option<i32>,
3861    pub selector: LabelSelector,
3862    pub template: PodTemplateSpec,
3863    #[serde(skip_serializing_if = "Option::is_none")]
3864    pub strategy: Option<HashMap<String, Value>>,
3865}
3866
3867#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3868pub struct WorkloadDeploymentStatus {
3869    #[serde(skip_serializing_if = "Option::is_none")]
3870    pub available_replicas: Option<i32>,
3871    #[serde(skip_serializing_if = "Option::is_none")]
3872    pub updated_replicas: Option<i32>,
3873}
3874
3875#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3876pub struct Deployment {
3877    #[serde(skip_serializing_if = "Option::is_none")]
3878    pub api_version: Option<String>,
3879    #[serde(skip_serializing_if = "Option::is_none")]
3880    pub kind: Option<String>,
3881    pub metadata: ObjectMeta,
3882    #[serde(skip_serializing_if = "Option::is_none")]
3883    pub spec: Option<DeploymentSpec>,
3884    #[serde(skip_serializing_if = "Option::is_none")]
3885    pub status: Option<WorkloadDeploymentStatus>,
3886}
3887
3888#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3889pub struct DeploymentList {
3890    #[serde(skip_serializing_if = "Option::is_none")]
3891    pub api_version: Option<String>,
3892    #[serde(skip_serializing_if = "Option::is_none")]
3893    pub kind: Option<String>,
3894    #[serde(default)]
3895    pub metadata: ListMeta,
3896    pub items: Vec<Deployment>,
3897}
3898
3899#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3900pub struct ReplicaSet {
3901    #[serde(skip_serializing_if = "Option::is_none")]
3902    pub api_version: Option<String>,
3903    #[serde(skip_serializing_if = "Option::is_none")]
3904    pub kind: Option<String>,
3905    pub metadata: ObjectMeta,
3906    #[serde(skip_serializing_if = "Option::is_none")]
3907    pub spec: Option<DeploymentSpec>,
3908    #[serde(skip_serializing_if = "Option::is_none")]
3909    pub status: Option<HashMap<String, Value>>,
3910}
3911
3912#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3913pub struct ReplicaSetList {
3914    #[serde(skip_serializing_if = "Option::is_none")]
3915    pub api_version: Option<String>,
3916    #[serde(skip_serializing_if = "Option::is_none")]
3917    pub kind: Option<String>,
3918    #[serde(default)]
3919    pub metadata: ListMeta,
3920    pub items: Vec<ReplicaSet>,
3921}
3922
3923#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3924pub struct StatefulSetSpec {
3925    #[serde(skip_serializing_if = "Option::is_none")]
3926    pub replicas: Option<i32>,
3927    #[serde(skip_serializing_if = "Option::is_none")]
3928    pub service_name: Option<String>,
3929    pub selector: LabelSelector,
3930    pub template: PodTemplateSpec,
3931    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3932    pub volume_claim_templates: Vec<Value>,
3933}
3934
3935#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3936pub struct StatefulSet {
3937    #[serde(skip_serializing_if = "Option::is_none")]
3938    pub api_version: Option<String>,
3939    #[serde(skip_serializing_if = "Option::is_none")]
3940    pub kind: Option<String>,
3941    pub metadata: ObjectMeta,
3942    #[serde(skip_serializing_if = "Option::is_none")]
3943    pub spec: Option<StatefulSetSpec>,
3944    #[serde(skip_serializing_if = "Option::is_none")]
3945    pub status: Option<HashMap<String, Value>>,
3946}
3947
3948#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3949pub struct StatefulSetList {
3950    #[serde(skip_serializing_if = "Option::is_none")]
3951    pub api_version: Option<String>,
3952    #[serde(skip_serializing_if = "Option::is_none")]
3953    pub kind: Option<String>,
3954    #[serde(default)]
3955    pub metadata: ListMeta,
3956    pub items: Vec<StatefulSet>,
3957}
3958
3959#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3960pub struct DaemonSetSpec {
3961    pub selector: LabelSelector,
3962    pub template: PodTemplateSpec,
3963    #[serde(skip_serializing_if = "Option::is_none")]
3964    pub update_strategy: Option<HashMap<String, Value>>,
3965}
3966
3967#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3968pub struct DaemonSet {
3969    #[serde(skip_serializing_if = "Option::is_none")]
3970    pub api_version: Option<String>,
3971    #[serde(skip_serializing_if = "Option::is_none")]
3972    pub kind: Option<String>,
3973    pub metadata: ObjectMeta,
3974    #[serde(skip_serializing_if = "Option::is_none")]
3975    pub spec: Option<DaemonSetSpec>,
3976    #[serde(skip_serializing_if = "Option::is_none")]
3977    pub status: Option<HashMap<String, Value>>,
3978}
3979
3980#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3981pub struct DaemonSetList {
3982    #[serde(skip_serializing_if = "Option::is_none")]
3983    pub api_version: Option<String>,
3984    #[serde(skip_serializing_if = "Option::is_none")]
3985    pub kind: Option<String>,
3986    #[serde(default)]
3987    pub metadata: ListMeta,
3988    pub items: Vec<DaemonSet>,
3989}
3990
3991#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
3992pub struct JobSpec {
3993    #[serde(skip_serializing_if = "Option::is_none")]
3994    pub parallelism: Option<i32>,
3995    #[serde(skip_serializing_if = "Option::is_none")]
3996    pub completions: Option<i32>,
3997    #[serde(skip_serializing_if = "Option::is_none")]
3998    pub backoff_limit: Option<i32>,
3999    pub template: PodTemplateSpec,
4000}
4001
4002#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4003pub struct Job {
4004    #[serde(skip_serializing_if = "Option::is_none")]
4005    pub api_version: Option<String>,
4006    #[serde(skip_serializing_if = "Option::is_none")]
4007    pub kind: Option<String>,
4008    pub metadata: ObjectMeta,
4009    #[serde(skip_serializing_if = "Option::is_none")]
4010    pub spec: Option<JobSpec>,
4011    #[serde(skip_serializing_if = "Option::is_none")]
4012    pub status: Option<HashMap<String, Value>>,
4013}
4014
4015#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4016pub struct JobList {
4017    #[serde(skip_serializing_if = "Option::is_none")]
4018    pub api_version: Option<String>,
4019    #[serde(skip_serializing_if = "Option::is_none")]
4020    pub kind: Option<String>,
4021    #[serde(default)]
4022    pub metadata: ListMeta,
4023    pub items: Vec<Job>,
4024}
4025
4026#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4027pub struct JobTemplateSpec {
4028    pub spec: JobSpec,
4029}
4030
4031#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4032pub struct CronJobSpec {
4033    pub schedule: String,
4034    pub job_template: JobTemplateSpec,
4035}
4036
4037#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4038pub struct CronJob {
4039    #[serde(skip_serializing_if = "Option::is_none")]
4040    pub api_version: Option<String>,
4041    #[serde(skip_serializing_if = "Option::is_none")]
4042    pub kind: Option<String>,
4043    pub metadata: ObjectMeta,
4044    #[serde(skip_serializing_if = "Option::is_none")]
4045    pub spec: Option<CronJobSpec>,
4046    #[serde(skip_serializing_if = "Option::is_none")]
4047    pub status: Option<HashMap<String, Value>>,
4048}
4049
4050#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4051pub struct CronJobList {
4052    #[serde(skip_serializing_if = "Option::is_none")]
4053    pub api_version: Option<String>,
4054    #[serde(skip_serializing_if = "Option::is_none")]
4055    pub kind: Option<String>,
4056    #[serde(default)]
4057    pub metadata: ListMeta,
4058    pub items: Vec<CronJob>,
4059}
4060
4061#[derive(Debug, Clone, Serialize, Deserialize, Default, ToSchema)]
4062pub struct ScaleSpec {
4063    #[serde(skip_serializing_if = "Option::is_none")]
4064    pub replicas: Option<i32>,
4065}
4066
4067#[derive(Debug, Clone, Serialize, Deserialize, Default, ToSchema)]
4068pub struct ScaleStatus {
4069    #[serde(skip_serializing_if = "Option::is_none")]
4070    pub replicas: Option<i32>,
4071}
4072
4073#[derive(Debug, Clone, Serialize, Deserialize, Default, ToSchema)]
4074pub struct Scale {
4075    #[serde(skip_serializing_if = "Option::is_none")]
4076    pub spec: Option<ScaleSpec>,
4077    #[serde(skip_serializing_if = "Option::is_none")]
4078    pub status: Option<ScaleStatus>,
4079}
4080
4081#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4082pub struct CrossVersionObjectReference {
4083    #[serde(skip_serializing_if = "Option::is_none")]
4084    pub api_version: Option<String>,
4085    #[serde(skip_serializing_if = "Option::is_none")]
4086    pub kind: Option<String>,
4087    pub name: String,
4088}
4089
4090#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4091pub struct MetricIdentifier {
4092    pub name: String,
4093    #[serde(skip_serializing_if = "Option::is_none")]
4094    pub selector: Option<LabelSelector>,
4095}
4096
4097#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4098pub struct MetricTarget {
4099    #[serde(rename = "type")]
4100    pub target_type: String,
4101    #[serde(skip_serializing_if = "Option::is_none")]
4102    pub value: Option<String>,
4103    #[serde(skip_serializing_if = "Option::is_none")]
4104    pub average_value: Option<String>,
4105    #[serde(skip_serializing_if = "Option::is_none")]
4106    pub average_utilization: Option<i32>,
4107}
4108
4109#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4110pub struct MetricValueStatus {
4111    #[serde(skip_serializing_if = "Option::is_none")]
4112    pub value: Option<String>,
4113    #[serde(skip_serializing_if = "Option::is_none")]
4114    pub average_value: Option<String>,
4115    #[serde(skip_serializing_if = "Option::is_none")]
4116    pub average_utilization: Option<i32>,
4117}
4118
4119#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4120pub struct ResourceMetricSource {
4121    pub name: String,
4122    pub target: MetricTarget,
4123}
4124
4125#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4126pub struct ResourceMetricStatus {
4127    pub name: String,
4128    pub current: MetricValueStatus,
4129}
4130
4131#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4132pub struct PodsMetricSource {
4133    pub metric: MetricIdentifier,
4134    pub target: MetricTarget,
4135}
4136
4137#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4138pub struct PodsMetricStatus {
4139    pub metric: MetricIdentifier,
4140    pub current: MetricValueStatus,
4141}
4142
4143#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4144pub struct ObjectMetricSource {
4145    pub described_object: CrossVersionObjectReference,
4146    pub metric: MetricIdentifier,
4147    pub target: MetricTarget,
4148}
4149
4150#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4151pub struct ObjectMetricStatus {
4152    pub described_object: CrossVersionObjectReference,
4153    pub metric: MetricIdentifier,
4154    pub current: MetricValueStatus,
4155}
4156
4157#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4158pub struct ExternalMetricSource {
4159    pub metric: MetricIdentifier,
4160    pub target: MetricTarget,
4161}
4162
4163#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4164pub struct ExternalMetricStatus {
4165    pub metric: MetricIdentifier,
4166    pub current: MetricValueStatus,
4167}
4168
4169#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4170pub struct MetricSpec {
4171    #[serde(rename = "type")]
4172    pub metric_type: String,
4173    #[serde(skip_serializing_if = "Option::is_none")]
4174    pub resource: Option<ResourceMetricSource>,
4175    #[serde(skip_serializing_if = "Option::is_none")]
4176    pub pods: Option<PodsMetricSource>,
4177    #[serde(skip_serializing_if = "Option::is_none")]
4178    pub object: Option<ObjectMetricSource>,
4179    #[serde(skip_serializing_if = "Option::is_none")]
4180    pub external: Option<ExternalMetricSource>,
4181}
4182
4183#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4184pub struct MetricStatus {
4185    #[serde(rename = "type")]
4186    pub metric_type: String,
4187    #[serde(skip_serializing_if = "Option::is_none")]
4188    pub resource: Option<ResourceMetricStatus>,
4189    #[serde(skip_serializing_if = "Option::is_none")]
4190    pub pods: Option<PodsMetricStatus>,
4191    #[serde(skip_serializing_if = "Option::is_none")]
4192    pub object: Option<ObjectMetricStatus>,
4193    #[serde(skip_serializing_if = "Option::is_none")]
4194    pub external: Option<ExternalMetricStatus>,
4195}
4196
4197#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4198pub struct AutoscalerCondition {
4199    #[serde(rename = "type")]
4200    pub condition_type: String,
4201    pub status: String,
4202    #[serde(skip_serializing_if = "Option::is_none")]
4203    pub reason: Option<String>,
4204    #[serde(skip_serializing_if = "Option::is_none")]
4205    pub message: Option<String>,
4206    #[serde(skip_serializing_if = "Option::is_none")]
4207    pub last_transition_time: Option<DateTime<Utc>>,
4208}
4209
4210#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4211pub struct HorizontalPodAutoscalerBehavior {
4212    #[serde(skip_serializing_if = "Option::is_none")]
4213    pub scale_up: Option<HorizontalPodAutoscalerScalingRules>,
4214    #[serde(skip_serializing_if = "Option::is_none")]
4215    pub scale_down: Option<HorizontalPodAutoscalerScalingRules>,
4216}
4217
4218#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4219pub struct HorizontalPodAutoscalerScalingRules {
4220    #[serde(skip_serializing_if = "Option::is_none")]
4221    pub stabilization_window_seconds: Option<i32>,
4222    #[serde(skip_serializing_if = "Option::is_none")]
4223    pub select_policy: Option<String>,
4224    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4225    pub policies: Vec<HorizontalPodAutoscalerScalingPolicy>,
4226}
4227
4228#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4229pub struct HorizontalPodAutoscalerScalingPolicy {
4230    #[serde(rename = "type")]
4231    pub policy_type: String,
4232    pub value: i32,
4233    pub period_seconds: i32,
4234}
4235
4236#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4237pub struct HorizontalPodAutoscalerSpec {
4238    pub scale_target_ref: CrossVersionObjectReference,
4239    #[serde(skip_serializing_if = "Option::is_none")]
4240    pub min_replicas: Option<i32>,
4241    pub max_replicas: i32,
4242    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4243    pub metrics: Vec<MetricSpec>,
4244    #[serde(skip_serializing_if = "Option::is_none")]
4245    pub behavior: Option<HorizontalPodAutoscalerBehavior>,
4246}
4247
4248#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4249pub struct HorizontalPodAutoscalerStatus {
4250    #[serde(skip_serializing_if = "Option::is_none")]
4251    pub current_replicas: Option<i32>,
4252    #[serde(skip_serializing_if = "Option::is_none")]
4253    pub desired_replicas: Option<i32>,
4254    #[serde(skip_serializing_if = "Option::is_none")]
4255    pub last_scale_time: Option<DateTime<Utc>>,
4256    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4257    pub current_metrics: Vec<MetricStatus>,
4258    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4259    pub conditions: Vec<AutoscalerCondition>,
4260}
4261
4262#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4263pub struct HorizontalPodAutoscaler {
4264    #[serde(skip_serializing_if = "Option::is_none")]
4265    pub api_version: Option<String>,
4266    #[serde(skip_serializing_if = "Option::is_none")]
4267    pub kind: Option<String>,
4268    pub metadata: ObjectMeta,
4269    #[serde(skip_serializing_if = "Option::is_none")]
4270    pub spec: Option<HorizontalPodAutoscalerSpec>,
4271    #[serde(skip_serializing_if = "Option::is_none")]
4272    pub status: Option<HorizontalPodAutoscalerStatus>,
4273}
4274
4275#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4276pub struct HorizontalPodAutoscalerList {
4277    #[serde(skip_serializing_if = "Option::is_none")]
4278    pub api_version: Option<String>,
4279    #[serde(skip_serializing_if = "Option::is_none")]
4280    pub kind: Option<String>,
4281    #[serde(default)]
4282    pub metadata: ListMeta,
4283    pub items: Vec<HorizontalPodAutoscaler>,
4284}
4285
4286#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4287pub struct VerticalPodAutoscalerUpdatePolicy {
4288    #[serde(skip_serializing_if = "Option::is_none")]
4289    pub update_mode: Option<String>,
4290}
4291
4292#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4293pub struct VerticalPodAutoscalerContainerPolicy {
4294    pub container_name: String,
4295    #[serde(skip_serializing_if = "Option::is_none")]
4296    pub mode: Option<String>,
4297    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
4298    pub min_allowed: HashMap<String, String>,
4299    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
4300    pub max_allowed: HashMap<String, String>,
4301    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4302    pub controlled_resources: Vec<String>,
4303    #[serde(skip_serializing_if = "Option::is_none")]
4304    pub controlled_values: Option<String>,
4305}
4306
4307#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4308pub struct VerticalPodAutoscalerResourcePolicy {
4309    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4310    pub container_policies: Vec<VerticalPodAutoscalerContainerPolicy>,
4311}
4312
4313#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4314pub struct VerticalPodAutoscalerSpec {
4315    pub target_ref: CrossVersionObjectReference,
4316    #[serde(skip_serializing_if = "Option::is_none")]
4317    pub update_policy: Option<VerticalPodAutoscalerUpdatePolicy>,
4318    #[serde(skip_serializing_if = "Option::is_none")]
4319    pub resource_policy: Option<VerticalPodAutoscalerResourcePolicy>,
4320}
4321
4322#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4323pub struct VerticalPodAutoscalerContainerRecommendation {
4324    pub container_name: String,
4325    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
4326    pub lower_bound: HashMap<String, String>,
4327    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
4328    pub target: HashMap<String, String>,
4329    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
4330    pub upper_bound: HashMap<String, String>,
4331    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
4332    pub uncapped_target: HashMap<String, String>,
4333}
4334
4335#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4336pub struct VerticalPodAutoscalerRecommendation {
4337    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4338    pub container_recommendations: Vec<VerticalPodAutoscalerContainerRecommendation>,
4339}
4340
4341#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4342pub struct VerticalPodAutoscalerStatus {
4343    #[serde(skip_serializing_if = "Option::is_none")]
4344    pub recommendation: Option<VerticalPodAutoscalerRecommendation>,
4345    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4346    pub conditions: Vec<AutoscalerCondition>,
4347}
4348
4349#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4350pub struct VerticalPodAutoscaler {
4351    #[serde(skip_serializing_if = "Option::is_none")]
4352    pub api_version: Option<String>,
4353    #[serde(skip_serializing_if = "Option::is_none")]
4354    pub kind: Option<String>,
4355    pub metadata: ObjectMeta,
4356    #[serde(skip_serializing_if = "Option::is_none")]
4357    pub spec: Option<VerticalPodAutoscalerSpec>,
4358    #[serde(skip_serializing_if = "Option::is_none")]
4359    pub status: Option<VerticalPodAutoscalerStatus>,
4360}
4361
4362#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4363pub struct VerticalPodAutoscalerList {
4364    #[serde(skip_serializing_if = "Option::is_none")]
4365    pub api_version: Option<String>,
4366    #[serde(skip_serializing_if = "Option::is_none")]
4367    pub kind: Option<String>,
4368    #[serde(default)]
4369    pub metadata: ListMeta,
4370    pub items: Vec<VerticalPodAutoscaler>,
4371}
4372
4373#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4374pub struct RolloutRequest {
4375    pub action: String,
4376}
4377
4378#[derive(Debug, Clone, Serialize, Deserialize, Default, ToSchema)]
4379pub struct MetricsSample {
4380    #[serde(flatten)]
4381    pub values: HashMap<String, Value>,
4382}
4383
4384#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4385pub struct MetricsSeries {
4386    pub ts: DateTime<Utc>,
4387    pub samples: Vec<MetricsSample>,
4388}
4389
4390#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4391pub struct Event {
4392    #[serde(skip_serializing_if = "Option::is_none")]
4393    pub api_version: Option<String>,
4394    #[serde(skip_serializing_if = "Option::is_none")]
4395    pub kind: Option<String>,
4396    pub metadata: ObjectMeta,
4397    #[serde(skip_serializing_if = "Option::is_none")]
4398    pub involved_object: Option<ObjectReference>,
4399    #[serde(skip_serializing_if = "Option::is_none")]
4400    pub reason: Option<String>,
4401    #[serde(skip_serializing_if = "Option::is_none")]
4402    pub message: Option<String>,
4403    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
4404    pub event_type: Option<String>,
4405    #[serde(skip_serializing_if = "Option::is_none")]
4406    pub count: Option<i32>,
4407    #[serde(skip_serializing_if = "Option::is_none")]
4408    pub first_timestamp: Option<DateTime<Utc>>,
4409    #[serde(skip_serializing_if = "Option::is_none")]
4410    pub last_timestamp: Option<DateTime<Utc>>,
4411}
4412
4413#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4414pub struct EventList {
4415    #[serde(skip_serializing_if = "Option::is_none")]
4416    pub api_version: Option<String>,
4417    #[serde(skip_serializing_if = "Option::is_none")]
4418    pub kind: Option<String>,
4419    #[serde(default)]
4420    pub metadata: ListMeta,
4421    pub items: Vec<Event>,
4422}
4423
4424pub fn compute_expiration(
4425    explicit: Option<DateTime<Utc>>,
4426    ttl_days: Option<i32>,
4427) -> Result<Option<DateTime<Utc>>, ValidationError> {
4428    if let Some(ttl) = ttl_days {
4429        if ttl <= 0 {
4430            return Err(ValidationError::new("ttl_days"));
4431        }
4432        if explicit.is_some() {
4433            return Err(ValidationError::new("expires_at_conflict"));
4434        }
4435        return Ok(Some(Utc::now() + Duration::days(ttl.into())));
4436    }
4437    Ok(explicit)
4438}
4439
4440fn validate_grant_file_permission(
4441    request: &GrantFilePermissionRequest,
4442) -> Result<(), ValidationError> {
4443    if request.user_id.is_none()
4444        && request.team_id.is_none()
4445        && request
4446            .role
4447            .as_ref()
4448            .is_none_or(|value| value.trim().is_empty())
4449    {
4450        return Err(ValidationError::new("missing_grantee"));
4451    }
4452    Ok(())
4453}
4454
4455impl AuthProvider {
4456    #[must_use]
4457    pub fn as_storage(&self) -> (String, Option<String>) {
4458        match self {
4459            Self::Local => ("local".to_string(), None),
4460            Self::OAuth { provider } => ("oauth".to_string(), Some(provider.clone())),
4461        }
4462    }
4463
4464    #[must_use]
4465    pub fn from_storage(kind: &str, provider: Option<String>) -> Self {
4466        if kind.eq_ignore_ascii_case("oauth") {
4467            Self::OAuth {
4468                provider: provider.unwrap_or_else(|| "unknown".to_string()),
4469            }
4470        } else {
4471            Self::Local
4472        }
4473    }
4474}
4475
4476// ============================================================================
4477// MFA (Multi-Factor Authentication) Types
4478// ============================================================================
4479
4480/// MFA method types
4481#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
4482#[serde(rename_all = "snake_case")]
4483pub enum MfaMethod {
4484    /// Time-based One-Time Password
4485    Totp,
4486}
4487
4488impl std::fmt::Display for MfaMethod {
4489    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4490        match self {
4491            Self::Totp => write!(f, "totp"),
4492        }
4493    }
4494}
4495
4496impl FromStr for MfaMethod {
4497    type Err = String;
4498
4499    fn from_str(s: &str) -> Result<Self, Self::Err> {
4500        match s.to_lowercase().as_str() {
4501            "totp" => Ok(Self::Totp),
4502            _ => Err(format!("Unknown MFA method: {s}")),
4503        }
4504    }
4505}
4506
4507/// MFA secret stored in the database
4508#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4509pub struct MfaSecret {
4510    pub id: Uuid,
4511    pub user_id: Uuid,
4512    /// AES-256-GCM encrypted TOTP secret
4513    pub secret_encrypted: String,
4514    pub method: MfaMethod,
4515    pub is_enabled: bool,
4516    #[serde(skip_serializing_if = "Option::is_none")]
4517    pub verified_at: Option<DateTime<Utc>>,
4518    pub created_at: DateTime<Utc>,
4519    pub updated_at: DateTime<Utc>,
4520}
4521
4522/// MFA backup code for account recovery
4523#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4524pub struct MfaBackupCode {
4525    pub id: Uuid,
4526    pub user_id: Uuid,
4527    /// SHA-256 hash of the backup code
4528    pub code_hash: String,
4529    #[serde(skip_serializing_if = "Option::is_none")]
4530    pub used_at: Option<DateTime<Utc>>,
4531    pub created_at: DateTime<Utc>,
4532}
4533
4534/// Partial session purpose for multi-step authentication
4535#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
4536#[serde(rename_all = "snake_case")]
4537pub enum PartialSessionPurpose {
4538    /// User needs to verify MFA code
4539    MfaVerify,
4540    /// User needs to complete MFA setup
4541    MfaSetup,
4542}
4543
4544impl std::fmt::Display for PartialSessionPurpose {
4545    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4546        match self {
4547            Self::MfaVerify => write!(f, "mfa_verify"),
4548            Self::MfaSetup => write!(f, "mfa_setup"),
4549        }
4550    }
4551}
4552
4553impl FromStr for PartialSessionPurpose {
4554    type Err = String;
4555
4556    fn from_str(s: &str) -> Result<Self, Self::Err> {
4557        match s.to_lowercase().as_str() {
4558            "mfa_verify" => Ok(Self::MfaVerify),
4559            "mfa_setup" => Ok(Self::MfaSetup),
4560            _ => Err(format!("Unknown partial session purpose: {s}")),
4561        }
4562    }
4563}
4564
4565/// Partial session for multi-step authentication flows
4566#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4567pub struct PartialSession {
4568    pub id: Uuid,
4569    pub user_id: Uuid,
4570    /// SHA-256 hash of the session token
4571    pub token_hash: String,
4572    pub purpose: PartialSessionPurpose,
4573    pub expires_at: DateTime<Utc>,
4574    #[serde(skip_serializing_if = "Option::is_none")]
4575    pub ip_address: Option<String>,
4576    pub created_at: DateTime<Utc>,
4577}
4578
4579/// Request to setup TOTP MFA
4580#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
4581pub struct MfaSetupRequest {
4582    /// TOTP code to verify setup
4583    #[validate(length(equal = 6))]
4584    pub code: String,
4585}
4586
4587/// Response for TOTP setup
4588#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4589pub struct MfaSetupResponse {
4590    /// Base32-encoded TOTP secret (only shown once)
4591    pub secret: String,
4592    /// QR code as data URL for authenticator apps
4593    pub qr_code_url: String,
4594    /// TOTP provisioning URI
4595    pub provisioning_uri: String,
4596}
4597
4598/// Request to verify MFA code
4599#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
4600pub struct MfaVerifyRequest {
4601    /// TOTP code or backup code
4602    #[validate(length(min = 6, max = 32))]
4603    pub code: String,
4604    /// Partial session token from login
4605    #[validate(length(min = 32, max = 64))]
4606    pub partial_token: String,
4607}
4608
4609/// MFA status response
4610#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4611pub struct MfaStatusResponse {
4612    pub enabled: bool,
4613    pub method: Option<MfaMethod>,
4614    pub backup_codes_remaining: i32,
4615    #[serde(skip_serializing_if = "Option::is_none")]
4616    pub verified_at: Option<DateTime<Utc>>,
4617}
4618
4619/// Response containing backup codes
4620#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4621pub struct BackupCodesResponse {
4622    /// Plaintext backup codes (only shown once)
4623    pub codes: Vec<String>,
4624    pub generated_at: DateTime<Utc>,
4625}
4626
4627// ============================================================================
4628// Token Types (Magic Links, Password Reset)
4629// ============================================================================
4630
4631/// Token purpose for magic links and password resets
4632#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
4633#[serde(rename_all = "snake_case")]
4634pub enum TokenPurpose {
4635    /// Magic link for passwordless login
4636    MagicLink,
4637    /// Password reset token
4638    PasswordReset,
4639}
4640
4641impl std::fmt::Display for TokenPurpose {
4642    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4643        match self {
4644            Self::MagicLink => write!(f, "magic_link"),
4645            Self::PasswordReset => write!(f, "password_reset"),
4646        }
4647    }
4648}
4649
4650impl FromStr for TokenPurpose {
4651    type Err = String;
4652
4653    fn from_str(s: &str) -> Result<Self, Self::Err> {
4654        match s.to_lowercase().as_str() {
4655            "magic_link" => Ok(Self::MagicLink),
4656            "password_reset" => Ok(Self::PasswordReset),
4657            _ => Err(format!("Unknown token purpose: {s}")),
4658        }
4659    }
4660}
4661
4662/// Magic link stored in the database
4663#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4664pub struct MagicLink {
4665    pub id: Uuid,
4666    pub user_id: Uuid,
4667    /// SHA-256 hash of the token
4668    pub token_hash: String,
4669    pub expires_at: DateTime<Utc>,
4670    #[serde(skip_serializing_if = "Option::is_none")]
4671    pub used_at: Option<DateTime<Utc>>,
4672    #[serde(skip_serializing_if = "Option::is_none")]
4673    pub ip_address: Option<String>,
4674    #[serde(skip_serializing_if = "Option::is_none")]
4675    pub user_agent: Option<String>,
4676    pub created_at: DateTime<Utc>,
4677}
4678
4679/// Password reset token stored in the database
4680#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4681pub struct PasswordResetToken {
4682    pub id: Uuid,
4683    pub user_id: Uuid,
4684    /// SHA-256 hash of the token
4685    pub token_hash: String,
4686    pub expires_at: DateTime<Utc>,
4687    #[serde(skip_serializing_if = "Option::is_none")]
4688    pub used_at: Option<DateTime<Utc>>,
4689    #[serde(skip_serializing_if = "Option::is_none")]
4690    pub ip_address: Option<String>,
4691    #[serde(skip_serializing_if = "Option::is_none")]
4692    pub user_agent: Option<String>,
4693    pub created_at: DateTime<Utc>,
4694}
4695
4696/// Request for magic link
4697#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
4698pub struct MagicLinkRequest {
4699    #[validate(email)]
4700    pub email: String,
4701}
4702
4703/// Request for password reset
4704#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
4705pub struct PasswordResetRequest {
4706    #[validate(email)]
4707    pub email: String,
4708}
4709
4710/// Request to complete password reset
4711#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
4712pub struct PasswordResetCompleteRequest {
4713    #[validate(length(min = 32, max = 64))]
4714    pub token: String,
4715    #[validate(length(min = 12, max = 128))]
4716    pub new_password: String,
4717}
4718
4719/// Request to verify magic link
4720#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
4721pub struct MagicLinkVerifyRequest {
4722    #[validate(length(min = 32, max = 64))]
4723    pub token: String,
4724}
4725
4726// ============================================================================
4727// Device/Session Types
4728// ============================================================================
4729
4730/// Device type detected from User-Agent
4731#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
4732#[serde(rename_all = "snake_case")]
4733pub enum DeviceType {
4734    Desktop,
4735    Mobile,
4736    Tablet,
4737    #[default]
4738    Unknown,
4739}
4740
4741/// Parsed device information from User-Agent
4742#[derive(Debug, Clone, Default, Serialize, Deserialize, ToSchema)]
4743pub struct DeviceInfo {
4744    #[serde(skip_serializing_if = "Option::is_none")]
4745    pub browser: Option<String>,
4746    #[serde(skip_serializing_if = "Option::is_none")]
4747    pub os: Option<String>,
4748    pub device_type: DeviceType,
4749    #[serde(skip_serializing_if = "Option::is_none")]
4750    pub fingerprint: Option<String>,
4751}
4752
4753/// User session stored in the database
4754#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4755pub struct UserSession {
4756    pub id: Uuid,
4757    pub user_id: Uuid,
4758    /// Hash of the session token
4759    pub session_token_hash: String,
4760    #[serde(skip_serializing_if = "Option::is_none")]
4761    pub refresh_token_hash: Option<String>,
4762    #[serde(skip_serializing_if = "Option::is_none")]
4763    pub device_fingerprint: Option<String>,
4764    #[serde(skip_serializing_if = "Option::is_none")]
4765    pub device_info: Option<String>,
4766    #[serde(skip_serializing_if = "Option::is_none")]
4767    pub browser: Option<String>,
4768    #[serde(skip_serializing_if = "Option::is_none")]
4769    pub os: Option<String>,
4770    #[serde(skip_serializing_if = "Option::is_none")]
4771    pub ip_address: Option<String>,
4772    #[serde(skip_serializing_if = "Option::is_none")]
4773    pub location_city: Option<String>,
4774    #[serde(skip_serializing_if = "Option::is_none")]
4775    pub location_country: Option<String>,
4776    pub is_current: bool,
4777    pub last_active_at: DateTime<Utc>,
4778    pub created_at: DateTime<Utc>,
4779    #[serde(skip_serializing_if = "Option::is_none")]
4780    pub revoked_at: Option<DateTime<Utc>>,
4781}
4782
4783/// Session summary for listing user sessions
4784#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4785pub struct SessionSummary {
4786    pub id: Uuid,
4787    #[serde(skip_serializing_if = "Option::is_none")]
4788    pub browser: Option<String>,
4789    #[serde(skip_serializing_if = "Option::is_none")]
4790    pub os: Option<String>,
4791    #[serde(skip_serializing_if = "Option::is_none")]
4792    pub ip_address: Option<String>,
4793    #[serde(skip_serializing_if = "Option::is_none")]
4794    pub location: Option<String>,
4795    pub is_current: bool,
4796    pub last_active_at: DateTime<Utc>,
4797    pub created_at: DateTime<Utc>,
4798}
4799
4800impl From<UserSession> for SessionSummary {
4801    fn from(session: UserSession) -> Self {
4802        let location = match (&session.location_city, &session.location_country) {
4803            (Some(city), Some(country)) => Some(format!("{city}, {country}")),
4804            (Some(city), None) => Some(city.clone()),
4805            (None, Some(country)) => Some(country.clone()),
4806            _ => None,
4807        };
4808
4809        Self {
4810            id: session.id,
4811            browser: session.browser,
4812            os: session.os,
4813            ip_address: session.ip_address,
4814            location,
4815            is_current: session.is_current,
4816            last_active_at: session.last_active_at,
4817            created_at: session.created_at,
4818        }
4819    }
4820}
4821
4822/// Request to revoke a session
4823#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
4824pub struct RevokeSessionRequest {
4825    pub session_id: Uuid,
4826}
4827
4828// ============================================================================
4829// WebAuthn/Passkey Types
4830// ============================================================================
4831
4832/// `WebAuthn` credential stored in the database
4833#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4834pub struct WebAuthnCredential {
4835    pub id: Uuid,
4836    pub user_id: Uuid,
4837    /// Raw credential ID bytes (base64 encoded for JSON)
4838    #[serde(with = "base64_bytes")]
4839    pub credential_id: Vec<u8>,
4840    /// Serialized passkey data (encrypted JSON)
4841    pub passkey: String,
4842    /// User-friendly name for the credential
4843    pub name: String,
4844    #[serde(skip_serializing_if = "Option::is_none")]
4845    pub aaguid: Option<Uuid>,
4846    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4847    pub transports: Vec<String>,
4848    pub is_discoverable: bool,
4849    pub created_at: DateTime<Utc>,
4850    #[serde(skip_serializing_if = "Option::is_none")]
4851    pub last_used_at: Option<DateTime<Utc>>,
4852    pub updated_at: DateTime<Utc>,
4853}
4854
4855/// `WebAuthn` registration challenge
4856#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4857pub struct WebAuthnChallenge {
4858    pub id: Uuid,
4859    #[serde(skip_serializing_if = "Option::is_none")]
4860    pub user_id: Option<Uuid>,
4861    /// SHA-256 hash of the challenge
4862    pub challenge_hash: String,
4863    /// AES-encrypted registration/authentication state
4864    pub state_encrypted: String,
4865    pub expires_at: DateTime<Utc>,
4866    pub created_at: DateTime<Utc>,
4867}
4868
4869/// Summary of a passkey for listing
4870#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4871pub struct PasskeySummary {
4872    pub id: Uuid,
4873    pub name: String,
4874    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4875    pub transports: Vec<String>,
4876    pub is_discoverable: bool,
4877    pub created_at: DateTime<Utc>,
4878    #[serde(skip_serializing_if = "Option::is_none")]
4879    pub last_used_at: Option<DateTime<Utc>>,
4880}
4881
4882impl From<WebAuthnCredential> for PasskeySummary {
4883    fn from(cred: WebAuthnCredential) -> Self {
4884        Self {
4885            id: cred.id,
4886            name: cred.name,
4887            transports: cred.transports,
4888            is_discoverable: cred.is_discoverable,
4889            created_at: cred.created_at,
4890            last_used_at: cred.last_used_at,
4891        }
4892    }
4893}
4894
4895/// Request to rename a passkey
4896#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
4897pub struct RenamePasskeyRequest {
4898    #[validate(length(min = 1, max = 100))]
4899    pub name: String,
4900}
4901
4902/// Request to start passkey registration
4903#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
4904pub struct PasskeyRegisterStartRequest {
4905    #[validate(length(min = 1, max = 100))]
4906    pub name: Option<String>,
4907}
4908
4909// ============================================================================
4910// Storage Metadata + Service Accounts
4911// ============================================================================
4912
4913#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
4914#[serde(rename_all = "snake_case")]
4915pub enum StorageBucketVersioning {
4916    Disabled,
4917    Enabled,
4918    Suspended,
4919}
4920
4921#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
4922#[serde(rename_all = "snake_case")]
4923pub enum StorageReplicationStrategy {
4924    Mirror,
4925    Erasure,
4926}
4927
4928#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4929pub struct StorageObjectLock {
4930    pub enabled: bool,
4931    #[serde(skip_serializing_if = "Option::is_none")]
4932    pub default_retention_days: Option<i32>,
4933}
4934
4935#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4936pub struct StorageReplicationPolicy {
4937    pub factor: i32,
4938    pub strategy: StorageReplicationStrategy,
4939    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4940    pub zones: Vec<String>,
4941    #[serde(skip_serializing_if = "Option::is_none")]
4942    pub min_success: Option<i32>,
4943}
4944
4945#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4946pub struct StorageCacheTier {
4947    #[serde(skip_serializing_if = "Option::is_none")]
4948    pub max_bytes: Option<i64>,
4949    #[serde(skip_serializing_if = "Option::is_none")]
4950    pub ttl_seconds: Option<i64>,
4951}
4952
4953#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4954pub struct StorageCachePolicy {
4955    #[serde(skip_serializing_if = "Option::is_none")]
4956    pub memory: Option<StorageCacheTier>,
4957    #[serde(skip_serializing_if = "Option::is_none")]
4958    pub nvme: Option<StorageCacheTier>,
4959}
4960
4961#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4962pub struct StorageLifecyclePolicy {
4963    #[serde(skip_serializing_if = "Option::is_none")]
4964    pub expire_days: Option<i32>,
4965    #[serde(skip_serializing_if = "Option::is_none")]
4966    pub transition_days: Option<i32>,
4967    #[serde(skip_serializing_if = "Option::is_none")]
4968    pub transition_class: Option<String>,
4969}
4970
4971#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4972pub struct StorageQuota {
4973    #[serde(skip_serializing_if = "Option::is_none")]
4974    pub max_bytes: Option<i64>,
4975    #[serde(skip_serializing_if = "Option::is_none")]
4976    pub max_objects: Option<i64>,
4977}
4978
4979#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
4980pub struct StorageBucket {
4981    pub id: Uuid,
4982    pub organization_id: Uuid,
4983    pub name: String,
4984    pub region: String,
4985    pub versioning: StorageBucketVersioning,
4986    #[serde(skip_serializing_if = "Option::is_none")]
4987    pub object_lock: Option<StorageObjectLock>,
4988    #[serde(skip_serializing_if = "Option::is_none")]
4989    pub replication: Option<StorageReplicationPolicy>,
4990    #[serde(skip_serializing_if = "Option::is_none")]
4991    pub cache_policy: Option<StorageCachePolicy>,
4992    #[serde(skip_serializing_if = "Option::is_none")]
4993    pub lifecycle: Option<StorageLifecyclePolicy>,
4994    #[serde(skip_serializing_if = "Option::is_none")]
4995    pub quota: Option<StorageQuota>,
4996    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4997    pub tags: Vec<String>,
4998    pub created_at: DateTime<Utc>,
4999    pub updated_at: DateTime<Utc>,
5000}
5001
5002#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
5003pub struct StorageBucketList {
5004    pub items: Vec<StorageBucket>,
5005    #[serde(skip_serializing_if = "Option::is_none")]
5006    pub next_cursor: Option<String>,
5007}
5008
5009#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
5010pub struct StorageObject {
5011    pub bucket_id: Uuid,
5012    pub key: String,
5013    pub size_bytes: i64,
5014    #[serde(skip_serializing_if = "Option::is_none")]
5015    pub etag: Option<String>,
5016    #[serde(skip_serializing_if = "Option::is_none")]
5017    pub content_type: Option<String>,
5018    #[serde(skip_serializing_if = "Option::is_none")]
5019    pub version_id: Option<String>,
5020    pub created_at: DateTime<Utc>,
5021    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
5022    pub metadata: HashMap<String, String>,
5023}
5024
5025#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
5026pub struct StorageObjectList {
5027    pub items: Vec<StorageObject>,
5028    #[serde(skip_serializing_if = "Option::is_none")]
5029    pub next_cursor: Option<String>,
5030}
5031
5032#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
5033#[serde(rename_all = "snake_case")]
5034pub enum StorageVolumeStatus {
5035    Available,
5036    Bound,
5037    InUse,
5038    Error,
5039}
5040
5041#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
5042pub struct StorageVolume {
5043    pub id: Uuid,
5044    pub organization_id: Uuid,
5045    pub name: String,
5046    pub size_bytes: i64,
5047    #[serde(skip_serializing_if = "Option::is_none")]
5048    pub filesystem: Option<String>,
5049    #[serde(skip_serializing_if = "Option::is_none")]
5050    pub replica_count: Option<i32>,
5051    #[serde(skip_serializing_if = "Option::is_none")]
5052    pub storage_class: Option<String>,
5053    pub status: StorageVolumeStatus,
5054    #[serde(skip_serializing_if = "Option::is_none")]
5055    pub cache_policy: Option<StorageCachePolicy>,
5056    pub created_at: DateTime<Utc>,
5057    pub updated_at: DateTime<Utc>,
5058}
5059
5060#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
5061pub struct StorageVolumeList {
5062    pub items: Vec<StorageVolume>,
5063    #[serde(skip_serializing_if = "Option::is_none")]
5064    pub next_cursor: Option<String>,
5065}
5066
5067#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
5068#[serde(rename_all = "snake_case")]
5069pub enum StorageDriveType {
5070    Personal,
5071    Shared,
5072    Project,
5073    Compute,
5074}
5075
5076#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
5077pub struct StorageDrive {
5078    pub id: Uuid,
5079    pub organization_id: Uuid,
5080    pub name: String,
5081    pub drive_type: StorageDriveType,
5082    #[serde(skip_serializing_if = "Option::is_none")]
5083    pub root_folder_id: Option<Uuid>,
5084    pub created_at: DateTime<Utc>,
5085    pub updated_at: DateTime<Utc>,
5086}
5087
5088#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
5089pub struct StorageDriveList {
5090    pub items: Vec<StorageDrive>,
5091    #[serde(skip_serializing_if = "Option::is_none")]
5092    pub next_cursor: Option<String>,
5093}
5094
5095#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
5096pub struct StorageContentRef {
5097    pub bucket_id: Uuid,
5098    pub key: String,
5099}
5100
5101#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
5102pub struct StorageFile {
5103    pub id: Uuid,
5104    pub organization_id: Uuid,
5105    pub drive_id: Uuid,
5106    pub name: String,
5107    #[serde(skip_serializing_if = "Option::is_none")]
5108    pub mime_type: Option<String>,
5109    #[serde(skip_serializing_if = "Option::is_none")]
5110    pub size_bytes: Option<i64>,
5111    #[serde(default, skip_serializing_if = "Vec::is_empty")]
5112    pub parents: Vec<Uuid>,
5113    pub trashed: bool,
5114    pub version: i32,
5115    #[serde(skip_serializing_if = "Option::is_none")]
5116    pub content_ref: Option<StorageContentRef>,
5117    pub created_at: DateTime<Utc>,
5118    pub updated_at: DateTime<Utc>,
5119}
5120
5121#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
5122pub struct StorageFileList {
5123    pub items: Vec<StorageFile>,
5124    #[serde(skip_serializing_if = "Option::is_none")]
5125    pub next_cursor: Option<String>,
5126}
5127
5128#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
5129#[serde(rename_all = "snake_case")]
5130pub enum StoragePermissionResourceKind {
5131    Bucket,
5132    Drive,
5133    File,
5134    Volume,
5135}
5136
5137#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
5138#[serde(rename_all = "snake_case")]
5139pub enum StoragePermissionSubjectType {
5140    User,
5141    Group,
5142    ServiceAccount,
5143}
5144
5145#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
5146#[serde(rename_all = "snake_case")]
5147pub enum StoragePermissionRole {
5148    Owner,
5149    Admin,
5150    Editor,
5151    Viewer,
5152    Commenter,
5153}
5154
5155#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
5156pub struct StoragePermission {
5157    pub id: Uuid,
5158    pub organization_id: Uuid,
5159    pub resource_kind: StoragePermissionResourceKind,
5160    pub resource_id: Uuid,
5161    pub subject_type: StoragePermissionSubjectType,
5162    pub subject_id: Uuid,
5163    pub role: StoragePermissionRole,
5164    pub allow_file_discovery: bool,
5165    pub created_at: DateTime<Utc>,
5166    pub updated_at: DateTime<Utc>,
5167}
5168
5169#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
5170pub struct StoragePermissionList {
5171    pub items: Vec<StoragePermission>,
5172    #[serde(skip_serializing_if = "Option::is_none")]
5173    pub next_cursor: Option<String>,
5174}
5175
5176#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
5177#[serde(rename_all = "snake_case")]
5178pub enum StorageServiceAccountStatus {
5179    Active,
5180    Disabled,
5181}
5182
5183impl StorageServiceAccountStatus {
5184    #[must_use]
5185    pub const fn as_str(&self) -> &'static str {
5186        match self {
5187            Self::Active => "active",
5188            Self::Disabled => "disabled",
5189        }
5190    }
5191}
5192
5193impl FromStr for StorageServiceAccountStatus {
5194    type Err = ();
5195
5196    fn from_str(value: &str) -> Result<Self, Self::Err> {
5197        match value {
5198            "active" => Ok(Self::Active),
5199            "disabled" => Ok(Self::Disabled),
5200            _ => Err(()),
5201        }
5202    }
5203}
5204
5205#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, ToSchema)]
5206#[serde(transparent)]
5207pub struct StorageServiceAccountScope(pub String);
5208
5209impl StorageServiceAccountScope {
5210    #[must_use]
5211    pub fn new(value: impl Into<String>) -> Self {
5212        Self(value.into())
5213    }
5214
5215    #[must_use]
5216    pub fn as_str(&self) -> &str {
5217        &self.0
5218    }
5219}
5220
5221#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
5222pub struct StorageServiceAccount {
5223    pub id: Uuid,
5224    pub organization_id: Uuid,
5225    pub name: String,
5226    #[serde(skip_serializing_if = "Option::is_none")]
5227    pub description: Option<String>,
5228    #[serde(skip_serializing_if = "Option::is_none")]
5229    pub scopes: Option<Vec<StorageServiceAccountScope>>,
5230    pub status: StorageServiceAccountStatus,
5231    pub created_by: Uuid,
5232    pub created_at: DateTime<Utc>,
5233    pub updated_at: DateTime<Utc>,
5234    #[serde(skip_serializing_if = "Option::is_none")]
5235    pub last_used_at: Option<DateTime<Utc>>,
5236}
5237
5238#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
5239pub struct StorageServiceAccountList {
5240    pub items: Vec<StorageServiceAccount>,
5241    #[serde(skip_serializing_if = "Option::is_none")]
5242    pub next_cursor: Option<String>,
5243}
5244
5245#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
5246#[serde(rename_all = "snake_case")]
5247pub enum StorageAccessKeyStatus {
5248    Active,
5249    Revoked,
5250    Expired,
5251}
5252
5253impl StorageAccessKeyStatus {
5254    #[must_use]
5255    pub const fn as_str(&self) -> &'static str {
5256        match self {
5257            Self::Active => "active",
5258            Self::Revoked => "revoked",
5259            Self::Expired => "expired",
5260        }
5261    }
5262}
5263
5264impl FromStr for StorageAccessKeyStatus {
5265    type Err = ();
5266
5267    fn from_str(value: &str) -> Result<Self, Self::Err> {
5268        match value {
5269            "active" => Ok(Self::Active),
5270            "revoked" => Ok(Self::Revoked),
5271            "expired" => Ok(Self::Expired),
5272            _ => Err(()),
5273        }
5274    }
5275}
5276
5277#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
5278pub struct StorageAccessKey {
5279    pub id: Uuid,
5280    pub service_account_id: Uuid,
5281    pub access_key: String,
5282    pub status: StorageAccessKeyStatus,
5283    #[serde(skip_serializing_if = "Option::is_none")]
5284    pub secret_preview: Option<String>,
5285    #[serde(skip_serializing_if = "Option::is_none")]
5286    pub expires_at: Option<DateTime<Utc>>,
5287    #[serde(skip_serializing_if = "Option::is_none")]
5288    pub last_used_at: Option<DateTime<Utc>>,
5289    pub created_at: DateTime<Utc>,
5290    pub updated_at: DateTime<Utc>,
5291}
5292
5293#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
5294pub struct StorageAccessKeyWithSecret {
5295    #[serde(flatten)]
5296    pub access_key: StorageAccessKey,
5297    pub secret_key: String,
5298}
5299
5300#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
5301pub struct StorageAccessKeyList {
5302    pub items: Vec<StorageAccessKey>,
5303    #[serde(skip_serializing_if = "Option::is_none")]
5304    pub next_cursor: Option<String>,
5305}
5306
5307#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
5308pub struct CreateStorageServiceAccountRequest {
5309    pub name: String,
5310    #[serde(skip_serializing_if = "Option::is_none")]
5311    pub description: Option<String>,
5312    #[serde(skip_serializing_if = "Option::is_none")]
5313    pub scopes: Option<Vec<StorageServiceAccountScope>>,
5314}
5315
5316#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
5317pub struct UpdateStorageServiceAccountRequest {
5318    #[serde(skip_serializing_if = "Option::is_none")]
5319    pub name: Option<String>,
5320    #[serde(skip_serializing_if = "Option::is_none")]
5321    pub description: Option<Option<String>>,
5322    #[serde(skip_serializing_if = "Option::is_none")]
5323    pub scopes: Option<Option<Vec<StorageServiceAccountScope>>>,
5324    #[serde(skip_serializing_if = "Option::is_none")]
5325    pub status: Option<StorageServiceAccountStatus>,
5326}
5327
5328#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
5329pub struct CreateStorageAccessKeyRequest {
5330    #[serde(skip_serializing_if = "Option::is_none")]
5331    pub expires_at: Option<DateTime<Utc>>,
5332    #[serde(skip_serializing_if = "Option::is_none")]
5333    pub ttl_days: Option<i32>,
5334}
5335
5336#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
5337pub struct RotateStorageAccessKeyRequest {
5338    #[serde(skip_serializing_if = "Option::is_none")]
5339    pub expires_at: Option<DateTime<Utc>>,
5340    #[serde(skip_serializing_if = "Option::is_none")]
5341    pub ttl_days: Option<i32>,
5342    #[serde(skip_serializing_if = "Option::is_none")]
5343    pub reason: Option<String>,
5344}
5345
5346#[derive(Debug, Clone)]
5347pub struct NewStorageServiceAccount {
5348    pub id: Uuid,
5349    pub organization_id: Uuid,
5350    pub name: String,
5351    pub description: Option<String>,
5352    pub scopes: Option<Vec<StorageServiceAccountScope>>,
5353    pub status: StorageServiceAccountStatus,
5354    pub created_by: Uuid,
5355    pub created_at: DateTime<Utc>,
5356    pub updated_at: DateTime<Utc>,
5357    pub last_used_at: Option<DateTime<Utc>>,
5358}
5359
5360#[derive(Debug, Clone)]
5361pub struct StorageServiceAccountUpdate {
5362    pub name: Option<String>,
5363    pub description: Option<Option<String>>,
5364    pub scopes: Option<Option<Vec<StorageServiceAccountScope>>>,
5365    pub status: Option<StorageServiceAccountStatus>,
5366    pub updated_at: DateTime<Utc>,
5367    pub last_used_at: Option<Option<DateTime<Utc>>>,
5368}
5369
5370impl StorageServiceAccountUpdate {
5371    #[must_use]
5372    pub fn new(updated_at: DateTime<Utc>) -> Self {
5373        Self {
5374            name: None,
5375            description: None,
5376            scopes: None,
5377            status: None,
5378            updated_at,
5379            last_used_at: None,
5380        }
5381    }
5382}
5383
5384#[derive(Debug, Clone)]
5385pub struct NewStorageAccessKey {
5386    pub id: Uuid,
5387    pub service_account_id: Uuid,
5388    pub access_key: String,
5389    pub secret_hash: String,
5390    pub secret_preview: Option<String>,
5391    pub status: StorageAccessKeyStatus,
5392    pub created_at: DateTime<Utc>,
5393    pub updated_at: DateTime<Utc>,
5394    pub expires_at: Option<DateTime<Utc>>,
5395    pub last_used_at: Option<DateTime<Utc>>,
5396}
5397
5398#[derive(Debug, Clone)]
5399pub struct StorageAccessKeyUpdate {
5400    pub status: Option<StorageAccessKeyStatus>,
5401    pub secret_hash: Option<String>,
5402    pub secret_preview: Option<Option<String>>,
5403    pub expires_at: Option<Option<DateTime<Utc>>>,
5404    pub updated_at: DateTime<Utc>,
5405    pub last_used_at: Option<Option<DateTime<Utc>>>,
5406}
5407
5408impl StorageAccessKeyUpdate {
5409    #[must_use]
5410    pub fn new(updated_at: DateTime<Utc>) -> Self {
5411        Self {
5412            status: None,
5413            secret_hash: None,
5414            secret_preview: None,
5415            expires_at: None,
5416            updated_at,
5417            last_used_at: None,
5418        }
5419    }
5420}
5421
5422#[derive(Debug, Clone)]
5423pub struct StoredStorageAccessKey {
5424    pub access_key: StorageAccessKey,
5425    pub secret_hash: String,
5426}
5427
5428// Helper module for base64 serialization of byte arrays
5429mod base64_bytes {
5430    use base64::{engine::general_purpose::STANDARD, Engine};
5431    use serde::{Deserialize, Deserializer, Serializer};
5432
5433    pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
5434    where
5435        S: Serializer,
5436    {
5437        serializer.serialize_str(&STANDARD.encode(bytes))
5438    }
5439
5440    pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
5441    where
5442        D: Deserializer<'de>,
5443    {
5444        let s = String::deserialize(deserializer)?;
5445        STANDARD.decode(&s).map_err(serde::de::Error::custom)
5446    }
5447}