likwid/backend/src/api/workflows.rs

160 lines
4.8 KiB
Rust
Raw Normal View History

//! Decision Workflows API endpoints.
use axum::{
extract::{Path, State},
http::StatusCode,
routing::{get, post},
Json, Router,
};
use serde::Deserialize;
use serde_json::{json, Value};
use sqlx::PgPool;
use uuid::Uuid;
use crate::auth::AuthUser;
use crate::plugins::builtin::decision_workflows::{
WorkflowInstance, WorkflowPhase, WorkflowService, WorkflowTemplate,
};
// ============================================================================
// Request Types
// ============================================================================
#[derive(Debug, Deserialize)]
pub struct CreateTemplateRequest {
pub name: String,
pub description: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct AddPhaseRequest {
pub name: String,
pub phase_type: String,
pub sequence_order: i32,
pub duration_hours: i32,
pub quorum_value: f64,
}
// ============================================================================
// Handlers
// ============================================================================
/// List workflow templates for a community
async fn list_templates(
Path(community_id): Path<Uuid>,
State(pool): State<PgPool>,
) -> Result<Json<Vec<WorkflowTemplate>>, (StatusCode, String)> {
let templates = WorkflowService::list_templates(&pool, Some(community_id))
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
Ok(Json(templates))
}
/// Create a new workflow template
async fn create_template(
auth: AuthUser,
Path(community_id): Path<Uuid>,
State(pool): State<PgPool>,
Json(req): Json<CreateTemplateRequest>,
) -> Result<Json<Value>, (StatusCode, String)> {
let template_id = WorkflowService::create_template(
&pool,
community_id,
&req.name,
req.description.as_deref(),
auth.user_id,
)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
Ok(Json(json!({"id": template_id})))
}
/// Get phases for a template
async fn get_template_phases(
Path(template_id): Path<Uuid>,
State(pool): State<PgPool>,
) -> Result<Json<Vec<WorkflowPhase>>, (StatusCode, String)> {
let phases = WorkflowService::get_template_phases(&pool, template_id)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
Ok(Json(phases))
}
/// Add a phase to a template
async fn add_phase(
_auth: AuthUser,
Path(template_id): Path<Uuid>,
State(pool): State<PgPool>,
Json(req): Json<AddPhaseRequest>,
) -> Result<Json<Value>, (StatusCode, String)> {
let phase_id = WorkflowService::add_phase(
&pool,
template_id,
&req.name,
&req.phase_type,
req.sequence_order,
req.duration_hours,
req.quorum_value,
)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
Ok(Json(json!({"id": phase_id})))
}
/// Get workflow for a proposal
async fn get_workflow_for_proposal(
Path(proposal_id): Path<Uuid>,
State(pool): State<PgPool>,
) -> Result<Json<Option<WorkflowInstance>>, (StatusCode, String)> {
let workflow = WorkflowService::get_workflow_for_proposal(&pool, proposal_id)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
Ok(Json(workflow))
}
/// Get workflow progress
async fn get_workflow_progress(
Path(proposal_id): Path<Uuid>,
State(pool): State<PgPool>,
) -> Result<Json<Value>, (StatusCode, String)> {
let progress = WorkflowService::get_workflow_progress(&pool, proposal_id)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
Ok(Json(progress))
}
/// Manually advance workflow phase
async fn advance_phase(
auth: AuthUser,
Path(proposal_id): Path<Uuid>,
State(pool): State<PgPool>,
) -> Result<Json<Value>, (StatusCode, String)> {
let success = WorkflowService::advance_phase(&pool, proposal_id, Some(auth.user_id), None)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
Ok(Json(json!({"success": success})))
}
// ============================================================================
// Router
// ============================================================================
pub fn router(pool: PgPool) -> Router {
Router::new()
// Templates
.route("/api/communities/{community_id}/workflow-templates", get(list_templates).post(create_template))
.route("/api/workflow-templates/{template_id}/phases", get(get_template_phases).post(add_phase))
// Proposal workflows
.route("/api/proposals/{proposal_id}/workflow", get(get_workflow_for_proposal))
.route("/api/proposals/{proposal_id}/workflow/progress", get(get_workflow_progress))
.route("/api/proposals/{proposal_id}/workflow/advance", post(advance_phase))
.with_state(pool)
}