flams_router_buildqueue_base/
server_fns.rs1use 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 pub(super) async fn get_queues() -> Result<Vec<QueueInfo>, ServerFnError<String>> {
70 let login = LoginState::get_server();
71 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(), });
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 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 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}