use axum::{
extract::{Path, Query, State},
http::header,
response::IntoResponse,
Json,
};
use serde::Deserialize;
use crate::error::{run_blocking, AppError};
use super::ical::{svc_ical, svc_ical_routine};
use super::model::{FleetRunSummary, IcalFeedQuery, Routine, RoutineStore};
use super::service::{
svc_get_prompt_preview, svc_list_all_runs, svc_logs, svc_run_log, svc_run_summary,
svc_trigger_scheduled,
};
#[utoipa::path(get, path = "/routines/{id}/prompt-preview",
params(("id" = String, Path, description = "Routine UUID")),
responses((status = 200, description = "Composed prompt body as plain text"), (status = 404, description = "Not found")))]
pub async fn get_prompt_preview(
State(store): State<RoutineStore>,
Path(id): Path<String>,
) -> Result<String, AppError> {
svc_get_prompt_preview(&store, &id)
}
#[utoipa::path(post, path = "/routines/{id}/scheduled-trigger",
params(("id" = String, Path, description = "Routine UUID")),
responses((status = 200, body = Routine), (status = 404, description = "Not found")))]
pub async fn scheduled_trigger(
State(store): State<RoutineStore>,
Path(id): Path<String>,
) -> Result<Json<Routine>, AppError> {
let resp = run_blocking(move || svc_trigger_scheduled(&store, &id)).await?;
Ok(Json(resp))
}
#[utoipa::path(get, path = "/routines.ics",
params(IcalFeedQuery),
responses((status = 200, description = "iCalendar (text/calendar) feed of upcoming routine fire times")))]
pub async fn ical_feed(
State(state): State<crate::routes::http::AppState>,
Query(query): Query<IcalFeedQuery>,
) -> impl IntoResponse {
let body = match query.routine.as_deref() {
Some(id) => svc_ical_routine(&state.routines, &state.routines_dir, id),
None => svc_ical(&state.routines, &state.routines_dir),
};
(
[(header::CONTENT_TYPE, "text/calendar; charset=utf-8")],
body,
)
}
#[utoipa::path(get, path = "/routines/{id}/logs",
params(("id" = String, Path, description = "Routine UUID")),
responses((status = 200, description = "Log file contents as plain text"), (status = 404, description = "Not found")))]
pub async fn get_logs(
State(store): State<RoutineStore>,
Path(id): Path<String>,
) -> Result<String, AppError> {
svc_logs(&store, &id).map(|logs| logs.content)
}
#[derive(Deserialize, utoipa::IntoParams)]
pub struct FleetRunsQuery {
pub limit: Option<usize>,
}
#[utoipa::path(get, path = "/routines/runs",
params(FleetRunsQuery),
responses((status = 200, body = [FleetRunSummary])))]
pub async fn get_all_runs(
State(store): State<RoutineStore>,
Query(query): Query<FleetRunsQuery>,
) -> Json<Vec<FleetRunSummary>> {
Json(svc_list_all_runs(&store, query.limit))
}
#[utoipa::path(get, path = "/routines/{id}/runs/{workbench}/log",
params(
("id" = String, Path, description = "Routine UUID"),
("workbench" = String, Path, description = "Workbench directory name (`{slug}-{unix_secs}`), from `GET /routines/{id}/runs`"),
),
responses((status = 200, description = "Log file contents as plain text"), (status = 404, description = "Not found")))]
pub async fn get_run_log(
State(store): State<RoutineStore>,
Path((id, workbench)): Path<(String, String)>,
) -> Result<String, AppError> {
svc_run_log(&store, &id, &workbench)
}
#[utoipa::path(get, path = "/routines/{id}/runs/{workbench}/summary",
params(
("id" = String, Path, description = "Routine UUID"),
("workbench" = String, Path, description = "Workbench directory name (`{slug}-{unix_secs}`), from `GET /routines/{id}/runs`"),
),
responses((status = 200, description = "Summary file contents as plain text"), (status = 404, description = "Not found")))]
pub async fn get_run_summary(
State(store): State<RoutineStore>,
Path((id, workbench)): Path<(String, String)>,
) -> Result<String, AppError> {
svc_run_summary(&store, &id, &workbench)
}