feat: implemented sync device API routes
parent
f42c708cc6
commit
0e543539cf
|
@ -2,6 +2,7 @@ mod auth;
|
|||
mod devices;
|
||||
mod episodes;
|
||||
mod subscriptions;
|
||||
mod sync;
|
||||
|
||||
use axum::Router;
|
||||
|
||||
|
@ -13,4 +14,5 @@ pub fn router(ctx: Context) -> Router<Context> {
|
|||
.nest("/devices", devices::router(ctx.clone()))
|
||||
.nest("/subscriptions", subscriptions::router(ctx.clone()))
|
||||
.nest("/episodes", episodes::router(ctx.clone()))
|
||||
.nest("/sync-devices", sync::router(ctx.clone()))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
use axum::{
|
||||
extract::{Path, State},
|
||||
middleware,
|
||||
routing::get,
|
||||
Extension, Json, Router,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
gpodder,
|
||||
server::{
|
||||
error::{AppError, AppResult},
|
||||
gpodder::{
|
||||
auth_middleware,
|
||||
format::{Format, StringWithFormat},
|
||||
models::{SyncStatus, SyncStatusDelta},
|
||||
},
|
||||
Context,
|
||||
},
|
||||
};
|
||||
|
||||
pub fn router(ctx: Context) -> Router<Context> {
|
||||
Router::new()
|
||||
.route(
|
||||
"/{username}",
|
||||
get(get_sync_status).post(post_sync_status_changes),
|
||||
)
|
||||
.layer(middleware::from_fn_with_state(ctx.clone(), auth_middleware))
|
||||
}
|
||||
|
||||
pub async fn get_sync_status(
|
||||
State(ctx): State<Context>,
|
||||
Path(username): Path<StringWithFormat>,
|
||||
Extension(user): Extension<gpodder::User>,
|
||||
) -> AppResult<Json<SyncStatus>> {
|
||||
if username.format != Format::Json {
|
||||
return Err(AppError::NotFound);
|
||||
}
|
||||
|
||||
if *username != user.username {
|
||||
return Err(AppError::BadRequest);
|
||||
}
|
||||
|
||||
Ok(
|
||||
tokio::task::spawn_blocking(move || ctx.store.devices_by_sync_group(&user))
|
||||
.await
|
||||
.unwrap()
|
||||
.map(|(not_synchronized, synchronized)| {
|
||||
Json(SyncStatus {
|
||||
synchronized,
|
||||
not_synchronized,
|
||||
})
|
||||
})?,
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn post_sync_status_changes(
|
||||
State(ctx): State<Context>,
|
||||
Path(username): Path<StringWithFormat>,
|
||||
Extension(user): Extension<gpodder::User>,
|
||||
Json(delta): Json<SyncStatusDelta>,
|
||||
) -> AppResult<Json<SyncStatus>> {
|
||||
if username.format != Format::Json {
|
||||
return Err(AppError::NotFound);
|
||||
}
|
||||
|
||||
if *username != user.username {
|
||||
return Err(AppError::BadRequest);
|
||||
}
|
||||
|
||||
Ok(tokio::task::spawn_blocking(move || {
|
||||
ctx.store.update_device_sync_status(
|
||||
&user,
|
||||
delta
|
||||
.synchronize
|
||||
.iter()
|
||||
.map(|v| v.iter().map(|s| s.as_ref()).collect())
|
||||
.collect(),
|
||||
delta.stop_synchronize.iter().map(|s| s.as_ref()).collect(),
|
||||
)?;
|
||||
|
||||
ctx.store.devices_by_sync_group(&user)
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.map(|(not_synchronized, synchronized)| {
|
||||
Json(SyncStatus {
|
||||
synchronized,
|
||||
not_synchronized,
|
||||
})
|
||||
})?)
|
||||
}
|
|
@ -73,6 +73,20 @@ pub struct EpisodeAction {
|
|||
pub action: EpisodeActionType,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct SyncStatus {
|
||||
pub synchronized: Vec<Vec<String>>,
|
||||
pub not_synchronized: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct SyncStatusDelta {
|
||||
pub synchronize: Vec<Vec<String>>,
|
||||
pub stop_synchronize: Vec<String>,
|
||||
}
|
||||
|
||||
impl From<gpodder::DeviceType> for DeviceType {
|
||||
fn from(value: gpodder::DeviceType) -> Self {
|
||||
match value {
|
||||
|
|
Loading…
Reference in New Issue