use super::*;
use boatramp_core::project::{Project, ProjectConfig, ProjectMeta, DEFAULT_PROJECT};
use boatramp_core::time::now_unix;
#[derive(Debug, Deserialize)]
pub(super) struct CreateProjectRequest {
name: String,
#[serde(default)]
display: String,
#[serde(default)]
description: String,
#[serde(default)]
region: Option<String>,
}
pub(super) async fn list_projects(State(deploy): State<DeployStore>) -> Response {
match deploy.list_projects().await {
Ok(projects) => Json(projects).into_response(),
Err(err) => deploy_error_response(err),
}
}
pub(super) async fn create_project(
State(deploy): State<DeployStore>,
Json(req): Json<CreateProjectRequest>,
) -> Response {
let name = req.name.trim();
if let Err(err) = boatramp_core::project::validate_resource_name("project", name) {
return (StatusCode::UNPROCESSABLE_ENTITY, format!("{err}\n")).into_response();
}
if name == boatramp_core::project::DEFAULT_PROJECT {
return (
StatusCode::CONFLICT,
format!("project {name:?} is reserved\n"),
)
.into_response();
}
match deploy.get_project(name).await {
Ok(Some(_)) => {
return (
StatusCode::CONFLICT,
format!("project `{name}` already exists\n"),
)
.into_response()
}
Ok(None) => {}
Err(err) => return deploy_error_response(err),
}
let project = Project {
version: boatramp_core::SCHEMA_VERSION,
name: name.to_string(),
created_at: now_unix(),
meta: ProjectMeta {
display: req.display,
description: req.description,
..Default::default()
},
config: ProjectConfig { region: req.region },
secrets_ref: None,
};
match deploy.put_project(&project).await {
Ok(_) => (StatusCode::CREATED, Json(project)).into_response(),
Err(err) => deploy_error_response(err),
}
}
pub(super) async fn get_project(
State(deploy): State<DeployStore>,
Path(proj): Path<String>,
) -> Response {
match deploy.get_project(&proj).await {
Ok(Some(project)) => Json(project).into_response(),
Ok(None) => (StatusCode::NOT_FOUND, format!("no project `{proj}`\n")).into_response(),
Err(err) => deploy_error_response(err),
}
}
pub(super) async fn delete_project(
State(deploy): State<DeployStore>,
Path(proj): Path<String>,
) -> Response {
if proj == DEFAULT_PROJECT {
return (
StatusCode::CONFLICT,
"the `default` project cannot be deleted\n",
)
.into_response();
}
match deploy.delete_project(&proj).await {
Ok(true) => StatusCode::NO_CONTENT.into_response(),
Ok(false) => (StatusCode::NOT_FOUND, format!("no project `{proj}`\n")).into_response(),
Err(DeployError::Conflict(msg)) => {
(StatusCode::CONFLICT, format!("{msg}\n")).into_response()
}
Err(err) => deploy_error_response(err),
}
}