lmrc-cli 0.3.16

CLI tool for scaffolding LMRC Stack infrastructure projects
Documentation
//! Project handlers

use axum::{
    extract::{Path, State},
    response::IntoResponse,
    Json,
};
use sea_orm::{ActiveModelTrait, ActiveValue, ColumnTrait, EntityTrait, QueryFilter};
use validator::Validate;

use crate::{error::ApiResult, state::AppState};

use super::{CreateProjectRequest, Project, UpdateProjectRequest};

/// List all projects
pub async fn list_projects(State(state): State<AppState>) -> ApiResult<impl IntoResponse> {
    use sea_orm::entity::prelude::*;

    #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
    #[sea_orm(table_name = "projects")]
    pub struct Model {
        #[sea_orm(primary_key)]
        pub id: i64,
        pub name: String,
        pub description: Option<String>,
        pub repository_url: Option<String>,
        pub status: String,
        pub created_at: ChronoDateTimeUtc,
        pub updated_at: ChronoDateTimeUtc,
    }

    #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
    pub enum Relation {}

    impl ActiveModelBehavior for ActiveModel {}

    let projects = Entity::find()
        .all(&state.db)
        .await?
        .into_iter()
        .map(|p| Project {
            id: p.id,
            name: p.name,
            description: p.description,
            repository_url: p.repository_url,
            status: p.status,
            created_at: p.created_at,
            updated_at: p.updated_at,
        })
        .collect::<Vec<_>>();

    Ok(Json(projects))
}

/// Get project by ID
pub async fn get_project(
    State(state): State<AppState>,
    Path(id): Path<i64>,
) -> ApiResult<impl IntoResponse> {
    use sea_orm::entity::prelude::*;

    #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
    #[sea_orm(table_name = "projects")]
    pub struct Model {
        #[sea_orm(primary_key)]
        pub id: i64,
        pub name: String,
        pub description: Option<String>,
        pub repository_url: Option<String>,
        pub status: String,
        pub created_at: ChronoDateTimeUtc,
        pub updated_at: ChronoDateTimeUtc,
    }

    #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
    pub enum Relation {}

    impl ActiveModelBehavior for ActiveModel {}

    let project = Entity::find_by_id(id)
        .one(&state.db)
        .await?
        .ok_or_else(|| crate::error::AppError::NotFound(format!("Project {} not found", id)))?;

    Ok(Json(Project {
        id: project.id,
        name: project.name,
        description: project.description,
        repository_url: project.repository_url,
        status: project.status,
        created_at: project.created_at,
        updated_at: project.updated_at,
    }))
}

/// Create new project
pub async fn create_project(
    State(state): State<AppState>,
    Json(req): Json<CreateProjectRequest>,
) -> ApiResult<impl IntoResponse> {
    req.validate()?;

    use sea_orm::entity::prelude::*;

    #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
    #[sea_orm(table_name = "projects")]
    pub struct Model {
        #[sea_orm(primary_key)]
        pub id: i64,
        pub name: String,
        pub description: Option<String>,
        pub repository_url: Option<String>,
        pub status: String,
        pub created_at: ChronoDateTimeUtc,
        pub updated_at: ChronoDateTimeUtc,
    }

    #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
    pub enum Relation {}

    impl ActiveModelBehavior for ActiveModel {}

    let now = chrono::Utc::now();

    let project = ActiveModel {
        name: ActiveValue::Set(req.name),
        description: ActiveValue::Set(req.description),
        repository_url: ActiveValue::Set(req.repository_url),
        status: ActiveValue::Set("active".to_string()),
        created_at: ActiveValue::Set(now),
        updated_at: ActiveValue::Set(now),
        ..Default::default()
    };

    let project = project.insert(&state.db).await?;

    Ok(Json(Project {
        id: project.id,
        name: project.name,
        description: project.description,
        repository_url: project.repository_url,
        status: project.status,
        created_at: project.created_at,
        updated_at: project.updated_at,
    }))
}

/// Update project
pub async fn update_project(
    State(state): State<AppState>,
    Path(id): Path<i64>,
    Json(req): Json<UpdateProjectRequest>,
) -> ApiResult<impl IntoResponse> {
    req.validate()?;

    use sea_orm::entity::prelude::*;

    #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
    #[sea_orm(table_name = "projects")]
    pub struct Model {
        #[sea_orm(primary_key)]
        pub id: i64,
        pub name: String,
        pub description: Option<String>,
        pub repository_url: Option<String>,
        pub status: String,
        pub created_at: ChronoDateTimeUtc,
        pub updated_at: ChronoDateTimeUtc,
    }

    #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
    pub enum Relation {}

    impl ActiveModelBehavior for ActiveModel {}

    let project = Entity::find_by_id(id)
        .one(&state.db)
        .await?
        .ok_or_else(|| crate::error::AppError::NotFound(format!("Project {} not found", id)))?;

    let mut project: ActiveModel = project.into();

    if let Some(name) = req.name {
        project.name = ActiveValue::Set(name);
    }
    if let Some(description) = req.description {
        project.description = ActiveValue::Set(Some(description));
    }
    if let Some(repository_url) = req.repository_url {
        project.repository_url = ActiveValue::Set(Some(repository_url));
    }
    if let Some(status) = req.status {
        project.status = ActiveValue::Set(status);
    }
    project.updated_at = ActiveValue::Set(chrono::Utc::now());

    let project = project.update(&state.db).await?;

    Ok(Json(Project {
        id: project.id,
        name: project.name,
        description: project.description,
        repository_url: project.repository_url,
        status: project.status,
        created_at: project.created_at,
        updated_at: project.updated_at,
    }))
}

/// Delete project
pub async fn delete_project(
    State(state): State<AppState>,
    Path(id): Path<i64>,
) -> ApiResult<impl IntoResponse> {
    use sea_orm::entity::prelude::*;

    #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
    #[sea_orm(table_name = "projects")]
    pub struct Model {
        #[sea_orm(primary_key)]
        pub id: i64,
        pub name: String,
        pub description: Option<String>,
        pub repository_url: Option<String>,
        pub status: String,
        pub created_at: ChronoDateTimeUtc,
        pub updated_at: ChronoDateTimeUtc,
    }

    #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
    pub enum Relation {}

    impl ActiveModelBehavior for ActiveModel {}

    let project = Entity::find_by_id(id)
        .one(&state.db)
        .await?
        .ok_or_else(|| crate::error::AppError::NotFound(format!("Project {} not found", id)))?;

    let project: ActiveModel = project.into();
    project.delete(&state.db).await?;

    Ok(Json(serde_json::json!({
        "message": "Project deleted successfully"
    })))
}