otter/src/server/gpodder/advanced/subscriptions.rs

91 lines
2.4 KiB
Rust

use axum::{
extract::{Path, Query, State},
middleware,
routing::post,
Extension, Json, Router,
};
use serde::Deserialize;
use crate::server::{
error::{AppError, AppResult},
gpodder::{
auth_api_middleware,
format::{Format, StringWithFormat},
models::{SubscriptionDelta, SubscriptionDeltaResponse, UpdatedUrlsResponse},
},
Context,
};
pub fn router(ctx: Context) -> Router<Context> {
Router::new()
.route(
"/{username}/{id}",
post(post_subscription_changes).get(get_subscription_changes),
)
.layer(middleware::from_fn_with_state(ctx.clone(), auth_api_middleware))
}
pub async fn post_subscription_changes(
State(ctx): State<Context>,
Path((username, id)): Path<(String, StringWithFormat)>,
Extension(user): Extension<gpodder::User>,
Json(delta): Json<SubscriptionDelta>,
) -> AppResult<Json<UpdatedUrlsResponse>> {
if id.format != Format::Json {
return Err(AppError::NotFound);
}
if username != user.username {
return Err(AppError::BadRequest);
}
Ok(tokio::task::spawn_blocking(move || {
ctx.store
.update_subscriptions_for_device(&user, &id, delta.add, delta.remove)
})
.await
.unwrap()
.map(|time_changed| {
Json(UpdatedUrlsResponse {
timestamp: time_changed.timestamp(),
update_urls: Vec::new(),
})
})?)
}
#[derive(Deserialize)]
pub struct SinceQuery {
#[serde(default)]
since: i64,
}
pub async fn get_subscription_changes(
State(ctx): State<Context>,
Path((username, id)): Path<(String, StringWithFormat)>,
Extension(user): Extension<gpodder::User>,
Query(query): Query<SinceQuery>,
) -> AppResult<Json<SubscriptionDeltaResponse>> {
if id.format != Format::Json {
return Err(AppError::NotFound);
}
if username != user.username {
return Err(AppError::BadRequest);
}
let since = chrono::DateTime::from_timestamp(query.since, 0).ok_or(AppError::BadRequest)?;
Ok(tokio::task::spawn_blocking(move || {
ctx.store.subscription_updates_for_device(&user, &id, since)
})
.await
.unwrap()
.map(|(next_time_changed, add, remove)| {
Json(SubscriptionDeltaResponse {
add: add.into_iter().map(|s| s.url).collect(),
remove: remove.into_iter().map(|s| s.url).collect(),
timestamp: next_time_changed.timestamp(),
})
})?)
}