flams_router_buildqueue_base/
server_fns.rs

1use std::num::NonZeroU32;
2
3use crate::{FormatOrTarget, QueueInfo};
4use flams_ontology::uris::ArchiveId;
5use leptos::prelude::*;
6
7#[server(prefix = "/api/buildqueue", endpoint = "get_queues")]
8pub async fn get_queues() -> Result<Vec<QueueInfo>, ServerFnError<String>> {
9    server::get_queues().await
10}
11
12#[server(prefix = "/api/buildqueue", endpoint = "run")]
13pub async fn run(id: NonZeroU32) -> Result<(), ServerFnError<String>> {
14    server::run(id).await
15}
16
17#[server(prefix = "/api/buildqueue", endpoint = "requeue")]
18pub async fn requeue(id: NonZeroU32) -> Result<(), ServerFnError<String>> {
19    server::requeue(id).await
20}
21
22#[server(prefix = "/api/buildqueue", endpoint = "enqueue")]
23pub async fn enqueue(
24    archive: Option<ArchiveId>,
25    target: FormatOrTarget,
26    path: Option<String>,
27    stale_only: Option<bool>,
28    queue: Option<NonZeroU32>,
29    clean: bool,
30) -> Result<usize, ServerFnError<String>> {
31    server::enqueue(archive, target, path, stale_only, queue, clean).await
32}
33
34#[server(prefix = "/api/buildqueue", endpoint = "log")]
35pub async fn get_log(
36    queue: NonZeroU32,
37    archive: ArchiveId,
38    rel_path: String,
39    target: String,
40) -> Result<String, ServerFnError<String>> {
41    server::get_log(queue, archive, rel_path, target).await
42}
43
44#[server(prefix = "/api/buildqueue", endpoint = "migrate")]
45pub async fn migrate(queue: NonZeroU32) -> Result<usize, ServerFnError<String>> {
46    server::migrate(queue).await
47}
48
49#[server(prefix = "/api/buildqueue", endpoint = "delete")]
50pub async fn delete(queue: NonZeroU32) -> Result<(), ServerFnError<String>> {
51    server::delete(queue).await
52}
53
54#[cfg(feature = "ssr")]
55pub mod server {
56    use std::num::NonZeroU32;
57
58    use crate::{FormatOrTarget, LoginQueue, QueueInfo, RepoInfo};
59    use flams_ontology::uris::ArchiveId;
60    use flams_router_base::LoginState;
61    use flams_system::backend::SandboxedRepository;
62    use flams_system::building::Queue;
63    use flams_system::building::queue_manager::QueueManager;
64    use flams_web_utils::blocking_server_fn;
65    use leptos::prelude::*;
66
67    /// #### Errors
68    pub(super) async fn get_queues() -> Result<Vec<QueueInfo>, ServerFnError<String>> {
69        let login = LoginState::get_server();
70        //let oauth = get_oauth().ok();
71        blocking_server_fn(move || {
72            let ls = match login {
73                LoginState::None | LoginState::Loading => {
74                    return Err(format!("Not logged in: {login:?}"));
75                }
76                LoginState::NoAccounts
77                | LoginState::Admin
78                | LoginState::User { is_admin: true, .. } => QueueManager::get().all_queues(),
79                LoginState::User { name, .. } => QueueManager::get().queues_for_user(&name),
80            };
81            let mut ret = Vec::new();
82            for (k, v, d) in ls {
83                let archives = d.map(|d| {
84                    let mut archives = Vec::new();
85                    for ri in d {
86                        match ri {
87                            SandboxedRepository::Copy(id) => archives.push(RepoInfo::Copy(id)),
88                            SandboxedRepository::Git {
89                                id,
90                                branch,
91                                commit,
92                                remote,
93                            } => {
94                                archives.push(RepoInfo::Git {
95                                    id,
96                                    branch: branch.to_string(),
97                                    commit,
98                                    remote: remote.to_string(), //,updates
99                                });
100                            }
101                        }
102                    }
103                    archives
104                });
105
106                ret.push(QueueInfo {
107                    id: k.into(),
108                    name: v.to_string(),
109                    archives,
110                });
111            }
112            Ok(ret)
113        })
114        .await
115    }
116
117    /// #### Errors
118    pub(super) async fn run(id: NonZeroU32) -> Result<(), ServerFnError<String>> {
119        use flams_system::building::queue_manager::QueueManager;
120        let login = LoginState::get_server();
121        blocking_server_fn(move || {
122            login.with_queue(id, |_| ())?;
123            QueueManager::get()
124                .start_queue(id.into())
125                .map_err(|()| "Queue does not exist".to_string())?;
126            Ok(())
127        })
128        .await
129    }
130
131    pub(super) async fn requeue(id: NonZeroU32) -> Result<(), ServerFnError<String>> {
132        let login = LoginState::get_server();
133        blocking_server_fn(move || login.with_queue(id, Queue::requeue_failed)).await
134    }
135
136    pub(super) async fn enqueue(
137        archive: Option<ArchiveId>,
138        target: FormatOrTarget,
139        path: Option<String>,
140        stale_only: Option<bool>,
141        queue: Option<NonZeroU32>,
142        clean: bool,
143    ) -> Result<usize, ServerFnError<String>> {
144        use flams_system::backend::archives::ArchiveOrGroup as AoG;
145        use flams_system::formats::FormatOrTargets;
146        use flams_system::formats::{BuildTarget, SourceFormat};
147
148        let login = LoginState::get_server();
149
150        blocking_server_fn(move || {
151            login.with_opt_queue(queue, |_, queue| {
152                let stale_only = stale_only.unwrap_or(true);
153
154                #[allow(clippy::option_if_let_else)]
155                let tgts: Vec<_> = match &target {
156                    FormatOrTarget::Targets(t) => {
157                        let Some(v) = t
158                            .iter()
159                            .map(|s| BuildTarget::get_from_str(s))
160                            .collect::<Option<Vec<_>>>()
161                        else {
162                            return Err("Invalid target".to_string());
163                        };
164                        v
165                    }
166                    FormatOrTarget::Format(_) => Vec::new(),
167                };
168
169                let fot = match target {
170                    FormatOrTarget::Format(f) => FormatOrTargets::Format(
171                        SourceFormat::get_from_str(&f)
172                            .map_or_else(|| Err("Invalid format".to_string()), Ok)?,
173                    ),
174                    FormatOrTarget::Targets(_) => FormatOrTargets::Targets(tgts.as_slice()),
175                };
176
177                let Some(archive) = archive else {
178                    return Ok(queue.enqueue_all(fot, stale_only, clean));
179                };
180
181                let group = flams_system::backend::GlobalBackend::get().with_archive_tree(
182                    |tree| -> Result<bool, String> {
183                        match tree.find(&archive) {
184                            Some(AoG::Archive(_)) => Ok(false),
185                            Some(AoG::Group(_)) => Ok(true),
186                            None => Err(format!("Archive {archive} not found")),
187                        }
188                    },
189                )?;
190
191                if group && path.is_some() {
192                    return Err(
193                        "Must specify either an archive with optional path or a group".to_string(),
194                    );
195                }
196
197                if group {
198                    Ok(queue.enqueue_group(&archive, fot, stale_only, clean))
199                } else {
200                    Ok(queue.enqueue_archive(
201                        &archive,
202                        fot,
203                        stale_only,
204                        path.as_deref(),
205                        clean && path.is_none(),
206                    ))
207                }
208            })?
209        })
210        .await
211    }
212
213    pub(super) async fn get_log(
214        queue: NonZeroU32,
215        archive: ArchiveId,
216        rel_path: String,
217        target: String,
218    ) -> Result<String, ServerFnError<String>> {
219        use flams_system::backend::Backend;
220
221        let Some(target) = flams_system::formats::BuildTarget::get_from_str(&target) else {
222            return Err(format!("Target {target} not found").into());
223        };
224        let login = LoginState::get_server();
225        let id = archive.clone();
226        let Some(path) = tokio::task::spawn_blocking(move || {
227            login.with_queue(queue, |q| {
228                q.backend()
229                    .with_archive(&id, |a| a.map(|a| a.get_log(&rel_path, target)))
230            })
231        })
232        .await
233        .map_err(|e| e.to_string())??
234        else {
235            return Err(format!("Archive {archive} not found").into());
236        };
237        let v = tokio::fs::read(path).await.map_err(|e| e.to_string())?;
238        Ok(String::from_utf8_lossy(&v).to_string())
239    }
240
241    pub(super) async fn migrate(queue: NonZeroU32) -> Result<usize, ServerFnError<String>> {
242        let login = LoginState::get_server();
243        if matches!(login, LoginState::NoAccounts) {
244            return Err("Migration only makes sense in public mode"
245                .to_string()
246                .into());
247        }
248        //let oauth = get_oauth().ok();
249        blocking_server_fn(move || {
250            login.with_queue(queue, |_| ())?;
251            let ((), n) = flams_system::building::queue_manager::QueueManager::get()
252                .migrate::<()>(queue.into(), |_| Ok(()))
253                .map_err(|r| format!("{r:#}"))?;
254            Ok(n)
255        })
256        .await
257    }
258
259    pub(super) async fn delete(queue: NonZeroU32) -> Result<(), ServerFnError<String>> {
260        use flams_system::building::queue_manager::QueueManager;
261        let login = LoginState::get_server();
262        blocking_server_fn(move || {
263            login.with_queue(queue, |_| ())?;
264            QueueManager::get().delete(queue.into());
265            Ok(())
266        })
267        .await
268    }
269}