use std::sync::Arc; use axum::{ extract::{Query, State}, Json, }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use crate::api::error::ApiError; use crate::db; use crate::records::{ShardView, SHARD_COLLECTION}; use crate::AppState; #[derive(Debug, Deserialize)] pub struct ListShardsParams { pub author: Option, pub limit: Option, pub cursor: Option, } #[derive(Debug, Serialize)] pub struct ListShardsResponse { pub shards: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub cursor: Option, } pub async fn list_shards( State(state): State>, Query(params): Query, ) -> Result, ApiError> { let author = params.author.ok_or_else(|| { ApiError::InvalidRequest("Missing required parameter: author".to_string()) })?; let limit = params.limit.unwrap_or(50).min(100).max(1); let cursor: Option> = params .cursor .as_ref() .and_then(|c| DateTime::parse_from_rfc3339(c).ok()) .map(|dt| dt.with_timezone(&Utc)); let records = db::list_records_by_creator(state.db.pool(), SHARD_COLLECTION, &author, cursor, limit) .await?; let next_cursor = records.last().map(|r| r.indexed_at.to_rfc3339()); let views: Vec = records.into_iter().map(ShardView::from).collect(); Ok(Json(ListShardsResponse { shards: views, cursor: next_cursor, })) }