diff --git a/bruno/Subscriptions API/Get subscriptions changes for device.bru b/bruno/Subscriptions API/Get subscriptions changes for device.bru new file mode 100644 index 0000000..5c6b7af --- /dev/null +++ b/bruno/Subscriptions API/Get subscriptions changes for device.bru @@ -0,0 +1,20 @@ +meta { + name: Get subscriptions changes for device + type: http + seq: 5 +} + +get { + url: http://localhost:8080/api/2/subscriptions/:username/:device_id?since=1740430923 + body: none + auth: inherit +} + +params:query { + since: 1740430923 +} + +params:path { + username: test + device_id: test.json +} diff --git a/bruno/Subscriptions API/Upload subscription changes for device.bru b/bruno/Subscriptions API/Upload subscription changes for device.bru index 7bd2f4f..44b4f13 100644 --- a/bruno/Subscriptions API/Upload subscription changes for device.bru +++ b/bruno/Subscriptions API/Upload subscription changes for device.bru @@ -4,13 +4,20 @@ meta { seq: 4 } -get { +post { url: http://localhost:8080/api/2/subscriptions/:username/:device_id - body: none - auth: none + body: json + auth: inherit } params:path { - username: - device_id: + username: test + device_id: test.json +} + +body:json { + { + "add": ["test_add_1"], + "remove": ["test_remove_1", "test_add_2"] + } } diff --git a/src/db/models/subscription.rs b/src/db/models/subscription.rs index a6e7ffd..d0bb1be 100644 --- a/src/db/models/subscription.rs +++ b/src/db/models/subscription.rs @@ -200,4 +200,19 @@ impl Subscription { Ok(()) }) } + + pub fn updated_since_for_device( + pool: &DbPool, + device_id: i64, + timestamp: i64, + ) -> DbResult> { + Ok(subscriptions::table + .select(Self::as_select()) + .filter( + subscriptions::device_id + .eq(device_id) + .and(subscriptions::time_changed.ge(timestamp)), + ) + .get_results(&mut pool.get()?)?) + } } diff --git a/src/server/gpodder/advanced/subscriptions.rs b/src/server/gpodder/advanced/subscriptions.rs index 78116db..8969c23 100644 --- a/src/server/gpodder/advanced/subscriptions.rs +++ b/src/server/gpodder/advanced/subscriptions.rs @@ -1,9 +1,10 @@ use axum::{ - extract::{Path, State}, + extract::{Path, Query, State}, middleware, routing::post, Extension, Json, Router, }; +use serde::Deserialize; use crate::{ db, @@ -12,7 +13,10 @@ use crate::{ gpodder::{ auth_middleware, format::{Format, StringWithFormat}, - models::{DeviceType, SubscriptionChangeResponse, SubscriptionDelta}, + models::{ + DeviceType, SubscriptionChangeResponse, SubscriptionDelta, + SubscriptionDeltaResponse, + }, }, Context, }, @@ -20,7 +24,10 @@ use crate::{ pub fn router(ctx: Context) -> Router { Router::new() - .route("/{username}/{id}", post(post_subscription_changes)) + .route( + "/{username}/{id}", + post(post_subscription_changes).get(get_subscription_changes), + ) .layer(middleware::from_fn_with_state(ctx.clone(), auth_middleware)) } @@ -70,3 +77,55 @@ pub async fn post_subscription_changes( update_urls: vec![], })) } + +#[derive(Deserialize)] +pub struct SinceQuery { + #[serde(default)] + since: i64, +} + +pub async fn get_subscription_changes( + State(ctx): State, + Path((username, id)): Path<(String, StringWithFormat)>, + Extension(user): Extension, + Query(query): Query, +) -> AppResult> { + if id.format != Format::Json { + return Err(AppError::NotFound); + } + + if username != user.username { + return Err(AppError::BadRequest); + } + + let subscriptions = tokio::task::spawn_blocking(move || { + let device = + db::Device::by_device_id(&ctx.pool, user.id, &id)?.ok_or(AppError::NotFound)?; + + Ok::<_, AppError>(db::Subscription::updated_since_for_device( + &ctx.pool, + device.id, + query.since, + )?) + }) + .await + .unwrap()?; + + let mut delta = SubscriptionDeltaResponse::default(); + delta.timestamp = query.since; + + for sub in subscriptions.into_iter() { + if sub.deleted { + delta.remove.push(sub.url); + } else { + delta.add.push(sub.url); + } + + delta.timestamp = delta.timestamp.max(sub.time_changed); + } + + // Timestamp should reflect the events *after* the last seen change + delta.timestamp += 1; + + Ok(Json(delta)) +} diff --git a/src/server/gpodder/models.rs b/src/server/gpodder/models.rs index 1cd6b2e..d2b0bdf 100644 --- a/src/server/gpodder/models.rs +++ b/src/server/gpodder/models.rs @@ -56,6 +56,13 @@ pub struct SubscriptionDelta { pub remove: Vec, } +#[derive(Serialize, Default)] +pub struct SubscriptionDeltaResponse { + pub add: Vec, + pub remove: Vec, + pub timestamp: i64, +} + #[derive(Serialize)] pub struct SubscriptionChangeResponse { pub timestamp: i64,