feat: API endpoints for article history listing and provenance
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>master
parent
b9003cde54
commit
55fe828e58
@ -0,0 +1,78 @@
|
|||||||
|
//! Handlers for article history and provenance endpoints.
|
||||||
|
|
||||||
|
use axum::extract::{Path, Query, State};
|
||||||
|
use axum::response::IntoResponse;
|
||||||
|
use axum::Json;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::app_state::AppState;
|
||||||
|
use crate::db;
|
||||||
|
use crate::errors::AppError;
|
||||||
|
use crate::middleware::auth::AuthUser;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct HistoryQuery {
|
||||||
|
pub limit: Option<i64>,
|
||||||
|
pub offset: Option<i64>,
|
||||||
|
pub status: Option<String>,
|
||||||
|
pub source_type: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GET /api/v1/article-history
|
||||||
|
///
|
||||||
|
/// Returns paginated article history with optional filters.
|
||||||
|
pub async fn list_history(
|
||||||
|
auth_user: AuthUser,
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Query(params): Query<HistoryQuery>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let limit = params.limit.unwrap_or(50).clamp(1, 200);
|
||||||
|
let offset = params.offset.unwrap_or(0).max(0);
|
||||||
|
|
||||||
|
let items = db::article_history::list_history(
|
||||||
|
&state.pool,
|
||||||
|
auth_user.id,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
params.status.as_deref(),
|
||||||
|
params.source_type.as_deref(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let total = db::article_history::count_history(
|
||||||
|
&state.pool,
|
||||||
|
auth_user.id,
|
||||||
|
params.status.as_deref(),
|
||||||
|
params.source_type.as_deref(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Json(serde_json::json!({
|
||||||
|
"items": items,
|
||||||
|
"total": total
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GET /api/v1/syntheses/:id/provenance
|
||||||
|
///
|
||||||
|
/// Returns all article history entries for the generation run
|
||||||
|
/// that produced the given synthesis.
|
||||||
|
pub async fn get_provenance(
|
||||||
|
auth_user: AuthUser,
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Path(synthesis_id): Path<Uuid>,
|
||||||
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
// Verify the synthesis belongs to this user and get its job_id
|
||||||
|
let synthesis = db::syntheses::get_by_id_for_user(&state.pool, synthesis_id, auth_user.id)
|
||||||
|
.await?
|
||||||
|
.ok_or_else(|| AppError::NotFound("Synthesis not found".into()))?;
|
||||||
|
|
||||||
|
let job_id = synthesis.job_id.ok_or_else(|| {
|
||||||
|
AppError::NotFound("No tracing data available for this synthesis".into())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let items = db::article_history::list_by_job_id(&state.pool, auth_user.id, job_id).await?;
|
||||||
|
|
||||||
|
Ok(Json(items))
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue