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