Skip to main content

jax_daemon/database/models/
fuse_mount.rs

1use serde::{Deserialize, Serialize};
2use sqlx::FromRow;
3use time::OffsetDateTime;
4use uuid::Uuid;
5
6use crate::database::types::{DBool, DUuid, MountStatus};
7use crate::database::Database;
8
9/// FUSE mount configuration stored in database
10#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
11pub struct FuseMount {
12    pub mount_id: DUuid,
13    pub bucket_id: DUuid,
14    pub mount_point: String,
15    pub enabled: DBool,
16    pub auto_mount: DBool,
17    pub read_only: DBool,
18    pub cache_size_mb: i64,
19    pub cache_ttl_secs: i64,
20    pub status: MountStatus,
21    pub error_message: Option<String>,
22    pub created_at: OffsetDateTime,
23    pub updated_at: OffsetDateTime,
24}
25
26impl FuseMount {
27    /// Create a new FUSE mount configuration
28    pub async fn create(
29        bucket_id: Uuid,
30        mount_point: &str,
31        auto_mount: bool,
32        read_only: bool,
33        cache_size_mb: Option<i64>,
34        cache_ttl_secs: Option<i64>,
35        db: &Database,
36    ) -> Result<FuseMount, sqlx::Error> {
37        let mount_id = DUuid::new();
38        let bucket_id = DUuid::from(bucket_id);
39        let cache_size = cache_size_mb.unwrap_or(100);
40        let cache_ttl = cache_ttl_secs.unwrap_or(60);
41
42        sqlx::query(
43            r#"
44            INSERT INTO fuse_mounts (
45                mount_id, bucket_id, mount_point, auto_mount, read_only,
46                cache_size_mb, cache_ttl_secs
47            )
48            VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)
49            "#,
50        )
51        .bind(mount_id)
52        .bind(bucket_id)
53        .bind(mount_point)
54        .bind(auto_mount)
55        .bind(read_only)
56        .bind(cache_size)
57        .bind(cache_ttl)
58        .execute(&**db)
59        .await?;
60
61        Self::get(*mount_id, db)
62            .await?
63            .ok_or(sqlx::Error::RowNotFound)
64    }
65
66    /// Get a FUSE mount by ID
67    pub async fn get(mount_id: Uuid, db: &Database) -> Result<Option<FuseMount>, sqlx::Error> {
68        let mount_id = DUuid::from(mount_id);
69        sqlx::query_as::<_, FuseMount>(
70            r#"
71            SELECT
72                mount_id, bucket_id, mount_point, enabled, auto_mount,
73                read_only, cache_size_mb, cache_ttl_secs, status,
74                error_message, created_at, updated_at
75            FROM fuse_mounts
76            WHERE mount_id = ?1
77            "#,
78        )
79        .bind(mount_id)
80        .fetch_optional(&**db)
81        .await
82    }
83
84    /// List all FUSE mounts
85    pub async fn list(db: &Database) -> Result<Vec<FuseMount>, sqlx::Error> {
86        sqlx::query_as::<_, FuseMount>(
87            r#"
88            SELECT
89                mount_id, bucket_id, mount_point, enabled, auto_mount,
90                read_only, cache_size_mb, cache_ttl_secs, status,
91                error_message, created_at, updated_at
92            FROM fuse_mounts
93            ORDER BY created_at DESC
94            "#,
95        )
96        .fetch_all(&**db)
97        .await
98    }
99
100    /// Update a FUSE mount configuration
101    #[allow(clippy::too_many_arguments)]
102    pub async fn update(
103        mount_id: Uuid,
104        mount_point: Option<&str>,
105        enabled: Option<bool>,
106        auto_mount: Option<bool>,
107        read_only: Option<bool>,
108        cache_size_mb: Option<i64>,
109        cache_ttl_secs: Option<i64>,
110        db: &Database,
111    ) -> Result<Option<FuseMount>, sqlx::Error> {
112        let existing = match Self::get(mount_id, db).await? {
113            Some(m) => m,
114            None => return Ok(None),
115        };
116
117        let mount_id = DUuid::from(mount_id);
118        let mount_point = mount_point.unwrap_or(&existing.mount_point);
119        let enabled = enabled.unwrap_or(*existing.enabled);
120        let auto_mount = auto_mount.unwrap_or(*existing.auto_mount);
121        let read_only = read_only.unwrap_or(*existing.read_only);
122        let cache_size = cache_size_mb.unwrap_or(existing.cache_size_mb);
123        let cache_ttl = cache_ttl_secs.unwrap_or(existing.cache_ttl_secs);
124
125        sqlx::query(
126            r#"
127            UPDATE fuse_mounts
128            SET mount_point = ?1, enabled = ?2, auto_mount = ?3, read_only = ?4,
129                cache_size_mb = ?5, cache_ttl_secs = ?6, updated_at = CURRENT_TIMESTAMP
130            WHERE mount_id = ?7
131            "#,
132        )
133        .bind(mount_point)
134        .bind(enabled)
135        .bind(auto_mount)
136        .bind(read_only)
137        .bind(cache_size)
138        .bind(cache_ttl)
139        .bind(mount_id)
140        .execute(&**db)
141        .await?;
142
143        Self::get(*mount_id, db).await
144    }
145
146    /// Delete a FUSE mount
147    pub async fn delete(mount_id: Uuid, db: &Database) -> Result<bool, sqlx::Error> {
148        let mount_id = DUuid::from(mount_id);
149        let result = sqlx::query("DELETE FROM fuse_mounts WHERE mount_id = ?1")
150            .bind(mount_id)
151            .execute(&**db)
152            .await?;
153
154        Ok(result.rows_affected() > 0)
155    }
156
157    /// Update the status of a FUSE mount
158    pub async fn update_status(
159        mount_id: Uuid,
160        status: MountStatus,
161        error_message: Option<&str>,
162        db: &Database,
163    ) -> Result<bool, sqlx::Error> {
164        let mount_id = DUuid::from(mount_id);
165        let result = sqlx::query(
166            r#"
167            UPDATE fuse_mounts
168            SET status = ?1, error_message = ?2, updated_at = CURRENT_TIMESTAMP
169            WHERE mount_id = ?3
170            "#,
171        )
172        .bind(status)
173        .bind(error_message)
174        .bind(mount_id)
175        .execute(&**db)
176        .await?;
177
178        Ok(result.rows_affected() > 0)
179    }
180
181    /// Get all mounts configured for auto-mount
182    pub async fn auto_list(db: &Database) -> Result<Vec<FuseMount>, sqlx::Error> {
183        sqlx::query_as::<_, FuseMount>(
184            r#"
185            SELECT
186                mount_id, bucket_id, mount_point, enabled, auto_mount,
187                read_only, cache_size_mb, cache_ttl_secs, status,
188                error_message, created_at, updated_at
189            FROM fuse_mounts
190            WHERE auto_mount = 1 AND enabled = 1
191            ORDER BY created_at ASC
192            "#,
193        )
194        .fetch_all(&**db)
195        .await
196    }
197
198    /// Get mounts by bucket ID
199    pub async fn by_bucket(bucket_id: Uuid, db: &Database) -> Result<Vec<FuseMount>, sqlx::Error> {
200        let bucket_id = DUuid::from(bucket_id);
201        sqlx::query_as::<_, FuseMount>(
202            r#"
203            SELECT
204                mount_id, bucket_id, mount_point, enabled, auto_mount,
205                read_only, cache_size_mb, cache_ttl_secs, status,
206                error_message, created_at, updated_at
207            FROM fuse_mounts
208            WHERE bucket_id = ?1
209            ORDER BY created_at DESC
210            "#,
211        )
212        .bind(bucket_id)
213        .fetch_all(&**db)
214        .await
215    }
216}