circus/crates/server/src/routes/channels.rs
NotAShelf 38ed7faee2
various: replace silent error discards with logged warnings
Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I465d760b5330980270b64b4a89abc09f6a6a6964
2026-02-15 23:37:51 +03:00

117 lines
3 KiB
Rust

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<AppState>,
) -> Result<Json<Vec<Channel>>, 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<AppState>,
Path(project_id): Path<Uuid>,
) -> Result<Json<Vec<Channel>>, 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<AppState>,
Path(id): Path<Uuid>,
) -> Result<Json<Channel>, 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<AppState>,
Json(input): Json<CreateChannel>,
) -> Result<Json<Channel>, 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<AppState>,
Path(id): Path<Uuid>,
) -> Result<Json<serde_json::Value>, 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<AppState>,
Path((channel_id, eval_id)): Path<(Uuid, Uuid)>,
) -> Result<Json<Channel>, 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<AppState> {
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),
)
}