···11//! Manages retrieving and updating project compose files.
2233-use eyre::eyre;
43use salvo::Router;
54use salvo::Writer;
65use salvo::oapi::ToSchema;
···5857) -> LuminaryResponse<LuminaryProjectWithCompose> {
5958 let engine = obtain!(depot, LuminaryEngine);
60596161- if project.len() == 0 || payload.to.as_ref().is_some_and(|name| name.len() == 0) {
6262- Err(eyre!("Project name cannot be empty"))?;
6060+ if !payload.creating {
6161+ engine.wait_until_idle(&project, None).await?;
6362 }
64636565- if let Some(compose) = &payload.compose {
6666- engine.validate_compose(compose)?;
6767- }
6868-6969- engine.wait_until_idle(&project, None).await?;
7064 engine.patch_project(&project, &payload.0).await?;
7165 return get_project(engine, &payload.to.take().unwrap_or(project.0)).await;
7266}
+3
packages/node/src/core/model.rs
···193193194194 /// If [Some], renames the current to the given name. If [None], no rename will take place.
195195 pub to: Option<String>,
196196+197197+ /// Wether this request is for creating a new project.
198198+ pub creating: bool,
196199}
+24-4
packages/node/src/core/project.rs
···11use std::path::{Path, PathBuf};
2233use docker_compose_types::Compose;
44-use eyre::{Context, Ok, Result};
44+use eyre::{Context, Ok, Result, bail};
55use futures_util::StreamExt;
66use luminary_macros::wrap_err;
77use tokio::fs::{self, read_to_string};
···33333434 /// Validates a compose file by attempting to parse it and performing basic checks on the structure.
3535 #[wrap_err("Invalid compose file")]
3636- pub fn validate_compose(&self, compose: &str) -> Result<()> {
3636+ fn validate_compose(&self, compose: &str) -> Result<()> {
3737 let compose = serde_saphyr::from_str::<Compose>(compose).wrap_err("Failed to parse compose file")?;
38383939 if compose.services.is_empty() {
···45454646 /// Updates the given project by applying the provided patch
4747 pub async fn patch_project(&self, project: &str, patch: &LuminaryProjectPatch) -> Result<()> {
4848- self.set_action(project, None, LuminaryAction::Patching).await?;
4848+ // Validate request
4949+ if project.len() == 0 || patch.to.as_ref().is_some_and(|name| name.len() == 0) {
5050+ bail!("Project name cannot be empty");
5151+ }
49525353+ if let Some(compose) = &patch.compose {
5454+ self.validate_compose(compose)?;
5555+ }
5656+5757+ if patch.creating {
5858+ if self.get_project(&project).await.is_ok() {
5959+ eyre::bail!("Project '{}' already exists", project);
6060+ }
6161+ } else {
6262+ self.set_action(project, None, LuminaryAction::Patching)
6363+ .await
6464+ .ok();
6565+ }
6666+6767+ // Perform requested changes
5068 let (project_path, compose_path) = self.get_path(project);
5169 let mut changed = false;
5270···6078 changed = true;
6179 }
62806363- self.set_action(project, None, LuminaryAction::Idle).await?;
8181+ if !patch.creating {
8282+ self.set_action(project, None, LuminaryAction::Idle).await.ok();
8383+ }
64846585 if changed {
6686 self.refresh().await?;