use axum::{
Extension, Json, Router,
extract::{Path, State},
response::IntoResponse,
routing::{get, put},
};
use axum_extra::extract::WithRejection;
use serde::{Deserialize, Serialize};
use serde_json::json;
use tracing::{debug, info};
use crate::{
api::{error::ApiError, middleware::auth::AuthenticatedUser, state::AppState},
model::Progress,
};
pub fn create_route() -> Router<AppState> {
Router::new()
.route("/syncs/progress", put(update_progress))
.route("/syncs/progress/{doc}", get(get_progress))
}
#[derive(Debug, Deserialize)]
struct UpdateProgressRequest {
pub device_id: String,
pub device: String,
pub document: String,
pub percentage: f32,
pub progress: String,
}
#[derive(Serialize)]
struct ProgressResponse {
pub device_id: String,
pub device: String,
pub document: String,
pub percentage: f32,
pub progress: String,
pub timestamp: u64,
}
#[tracing::instrument(level = tracing::Level::DEBUG, skip(state))]
async fn update_progress(
State(state): State<AppState>,
Extension(AuthenticatedUser(user, _)): Extension<AuthenticatedUser>,
WithRejection(Json(payload), _): WithRejection<Json<UpdateProgressRequest>, ApiError>,
) -> Result<impl IntoResponse, ApiError> {
debug!("Updating sync progress");
let (doc, ts) = state
.sync
.update_progress(user, payload.document.clone(), payload.into())?;
Ok(Json(json!({
"document": doc,
"timestamp": ts,
}))
.into_response())
}
#[tracing::instrument(level = tracing::Level::DEBUG, skip(state))]
async fn get_progress(
State(state): State<AppState>,
Extension(AuthenticatedUser(user, _)): Extension<AuthenticatedUser>,
WithRejection(Path(doc), _): WithRejection<Path<String>, ApiError>,
) -> Result<impl IntoResponse, ApiError> {
info!("Getting sync progress for doc: {}", doc);
let progress = state.sync.get_progress(user, doc.clone());
match progress {
Ok(Some(progress)) => Ok(Json(ProgressResponse {
document: doc,
..progress.into()
})
.into_response()),
Ok(None) => Ok(Json(json!({})).into_response()),
Err(e) => Err(e.into()),
}
}
impl From<UpdateProgressRequest> for Progress {
fn from(value: UpdateProgressRequest) -> Self {
Self {
device_id: value.device_id,
device: value.device,
percentage: value.percentage,
progress: value.progress,
timestamp: chrono::Utc::now().timestamp_millis() as u64,
}
}
}
impl From<Progress> for ProgressResponse {
fn from(value: Progress) -> Self {
Self {
device_id: value.device_id,
device: value.device,
document: "".to_string(),
percentage: value.percentage,
progress: value.progress,
timestamp: value.timestamp,
}
}
}