flams_router_buildqueue_base/
server_fns.rs

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