use axum::{ Json, Router, extract::{Path, State}, routing::{get, post}, }; use fc_common::{ Validate, models::{Channel, CreateChannel}, }; use uuid::Uuid; use crate::{auth_middleware::RequireAdmin, error::ApiError, state::AppState}; async fn list_channels( State(state): State, ) -> Result>, ApiError> { let channels = fc_common::repo::channels::list_all(&state.pool) .await .map_err(ApiError)?; Ok(Json(channels)) } async fn list_project_channels( State(state): State, Path(project_id): Path, ) -> Result>, ApiError> { let channels = fc_common::repo::channels::list_for_project(&state.pool, project_id) .await .map_err(ApiError)?; Ok(Json(channels)) } async fn get_channel( State(state): State, Path(id): Path, ) -> Result, ApiError> { let channel = fc_common::repo::channels::get(&state.pool, id) .await .map_err(ApiError)?; Ok(Json(channel)) } async fn create_channel( _auth: RequireAdmin, State(state): State, Json(input): Json, ) -> Result, ApiError> { input .validate() .map_err(|msg| ApiError(fc_common::CiError::Validation(msg)))?; let jobset_id = input.jobset_id; let channel = fc_common::repo::channels::create(&state.pool, input) .await .map_err(ApiError)?; // Catch-up: if the jobset already has a completed evaluation, promote now if let Ok(Some(eval)) = fc_common::repo::evaluations::get_latest(&state.pool, jobset_id).await { if eval.status == fc_common::models::EvaluationStatus::Completed { if let Err(e) = fc_common::repo::channels::auto_promote_if_complete( &state.pool, jobset_id, eval.id, ) .await { tracing::warn!(jobset_id = %jobset_id, "Failed to auto-promote channel: {e}"); } } } // Re-fetch to include any promotion let channel = fc_common::repo::channels::get(&state.pool, channel.id) .await .map_err(ApiError)?; Ok(Json(channel)) } async fn delete_channel( _auth: RequireAdmin, State(state): State, Path(id): Path, ) -> Result, ApiError> { fc_common::repo::channels::delete(&state.pool, id) .await .map_err(ApiError)?; Ok(Json(serde_json::json!({"deleted": true}))) } async fn promote_channel( _auth: RequireAdmin, State(state): State, Path((channel_id, eval_id)): Path<(Uuid, Uuid)>, ) -> Result, ApiError> { let channel = fc_common::repo::channels::promote(&state.pool, channel_id, eval_id) .await .map_err(ApiError)?; Ok(Json(channel)) } pub fn router() -> Router { Router::new() .route("/channels", get(list_channels).post(create_channel)) .route("/channels/{id}", get(get_channel).delete(delete_channel)) .route( "/channels/{channel_id}/promote/{eval_id}", post(promote_channel), ) .route( "/projects/{project_id}/channels", get(list_project_channels), ) }