Skip to main content

ciab_gateway/
types.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use uuid::Uuid;
4
5/// A tunnel exposing a local port to a public URL.
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct GatewayTunnel {
8    pub id: Uuid,
9    pub sandbox_id: Option<Uuid>,
10    pub tunnel_type: TunnelType,
11    pub public_url: String,
12    pub local_port: u16,
13    pub state: TunnelState,
14    pub config_json: serde_json::Value,
15    pub error_message: Option<String>,
16    pub created_at: DateTime<Utc>,
17    pub updated_at: DateTime<Utc>,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
21#[serde(rename_all = "snake_case")]
22pub enum TunnelType {
23    Frp,
24    Bore,
25    Cloudflare,
26    Ngrok,
27    Lan,
28    Manual,
29}
30
31impl std::fmt::Display for TunnelType {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        match self {
34            Self::Frp => write!(f, "frp"),
35            Self::Bore => write!(f, "bore"),
36            Self::Cloudflare => write!(f, "cloudflare"),
37            Self::Ngrok => write!(f, "ngrok"),
38            Self::Lan => write!(f, "lan"),
39            Self::Manual => write!(f, "manual"),
40        }
41    }
42}
43
44impl std::str::FromStr for TunnelType {
45    type Err = String;
46    fn from_str(s: &str) -> Result<Self, Self::Err> {
47        match s {
48            "frp" => Ok(Self::Frp),
49            "bore" => Ok(Self::Bore),
50            "cloudflare" => Ok(Self::Cloudflare),
51            "ngrok" => Ok(Self::Ngrok),
52            "lan" => Ok(Self::Lan),
53            "manual" => Ok(Self::Manual),
54            other => Err(format!("unknown tunnel type: {}", other)),
55        }
56    }
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
60#[serde(rename_all = "snake_case")]
61pub enum TunnelState {
62    Active,
63    Stopped,
64    Error,
65}
66
67impl std::fmt::Display for TunnelState {
68    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69        match self {
70            Self::Active => write!(f, "active"),
71            Self::Stopped => write!(f, "stopped"),
72            Self::Error => write!(f, "error"),
73        }
74    }
75}
76
77impl std::str::FromStr for TunnelState {
78    type Err = String;
79    fn from_str(s: &str) -> Result<Self, Self::Err> {
80        match s {
81            "active" => Ok(Self::Active),
82            "stopped" => Ok(Self::Stopped),
83            "error" => Ok(Self::Error),
84            other => Err(format!("unknown tunnel state: {}", other)),
85        }
86    }
87}
88
89/// A scoped client token for gateway access.
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct ClientToken {
92    pub id: Uuid,
93    pub name: String,
94    /// SHA-256 hash of the raw token (raw token never stored).
95    pub token_hash: String,
96    pub scopes: Vec<TokenScope>,
97    pub expires_at: Option<DateTime<Utc>>,
98    pub last_used_at: Option<DateTime<Utc>>,
99    pub created_at: DateTime<Utc>,
100    pub revoked_at: Option<DateTime<Utc>>,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
104#[serde(tag = "type", rename_all = "snake_case")]
105pub enum TokenScope {
106    FullAccess,
107    SandboxAccess { sandbox_id: Uuid },
108    WorkspaceAccess { workspace_id: Uuid },
109    ReadOnly,
110    ChatOnly { sandbox_id: Uuid },
111}
112
113impl TokenScope {
114    /// Check if this scope allows access to a given sandbox.
115    pub fn allows_sandbox(&self, sandbox_id: &Uuid) -> bool {
116        match self {
117            Self::FullAccess => true,
118            Self::SandboxAccess { sandbox_id: sid } => sid == sandbox_id,
119            Self::ChatOnly { sandbox_id: sid } => sid == sandbox_id,
120            Self::ReadOnly => true,
121            Self::WorkspaceAccess { .. } => {
122                // Workspace scope requires external lookup; default to false here.
123                // The caller must resolve workspace membership.
124                false
125            }
126        }
127    }
128
129    /// Check if this scope permits write operations.
130    pub fn allows_write(&self) -> bool {
131        match self {
132            Self::FullAccess => true,
133            Self::SandboxAccess { .. } => true,
134            Self::WorkspaceAccess { .. } => true,
135            Self::ChatOnly { .. } => true,
136            Self::ReadOnly => false,
137        }
138    }
139}
140
141/// Overall gateway status returned by the status endpoint.
142#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct GatewayStatus {
144    pub enabled: bool,
145    /// Currently active tunnel provider name
146    pub active_provider: String,
147    pub lan: LanStatus,
148    pub providers: Vec<TunnelProviderInfo>,
149    pub active_tunnels: usize,
150    pub active_tokens: usize,
151    // Legacy fields for backward compat
152    pub frp: FrpStatus,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct LanStatus {
157    pub enabled: bool,
158    pub mdns_name: Option<String>,
159    pub local_addresses: Vec<String>,
160    pub advertise_port: u16,
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct FrpStatus {
165    pub enabled: bool,
166    pub process_running: bool,
167    pub server_addr: Option<String>,
168    pub proxy_count: usize,
169}
170
171/// Status info for a tunnel provider.
172#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct TunnelProviderInfo {
174    /// Provider name: "frp", "bore", "cloudflare", "ngrok"
175    pub name: String,
176    pub enabled: bool,
177    pub installed: bool,
178    pub binary_path: Option<String>,
179    pub version: Option<String>,
180    pub process_running: bool,
181    pub tunnel_count: usize,
182}
183
184/// Result from preparing (installing/validating) a tunnel provider.
185#[derive(Debug, Clone, Serialize, Deserialize)]
186pub struct ProviderPrepareResult {
187    pub provider: String,
188    pub installed: bool,
189    pub binary_path: String,
190    pub version: Option<String>,
191    pub message: String,
192}