jax_daemon/database/models/
fuse_mount.rs1use 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#[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 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 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 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 #[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 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 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 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 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}