use axum::{ extract::{Path, State}, middleware, routing::{get, post}, Extension, Json, Router, }; use crate::{ db, server::{ error::{AppError, AppResult}, gpodder::{ auth_middleware, format::{Format, StringWithFormat}, models::{Device, DevicePatch, DeviceType}, }, Context, }, }; pub fn router(ctx: Context) -> Router { Router::new() .route("/{username}", get(get_devices)) .route("/{username}/{id}", post(post_device)) .layer(middleware::from_fn_with_state(ctx.clone(), auth_middleware)) } async fn get_devices( State(ctx): State, Path(username): Path, Extension(user): Extension, ) -> AppResult>> { if username.format != Format::Json { return Err(AppError::NotFound); } if *username != user.username { return Err(AppError::BadRequest); } let devices = tokio::task::spawn_blocking(move || db::Device::for_user(&ctx.pool, user.id)) .await .unwrap()? .into_iter() .map(|d| Device { id: d.device_id, caption: d.caption, r#type: d.type_.into(), // TODO implement subscription count subscriptions: 0, }) .collect(); Ok(Json(devices)) } async fn post_device( State(ctx): State, Path((_username, id)): Path<(String, StringWithFormat)>, Extension(user): Extension, Json(patch): Json, ) -> AppResult<()> { if id.format != Format::Json { return Err(AppError::NotFound); } tokio::task::spawn_blocking(move || { if let Some(mut device) = db::Device::by_device_id(&ctx.pool, user.id, &id)? { if let Some(caption) = patch.caption { device.caption = caption; } if let Some(type_) = patch.r#type { device.type_ = type_.into(); } device.update(&ctx.pool) } else { db::NewDevice::new( user.id, id.to_string(), patch.caption.unwrap_or(String::new()), patch.r#type.unwrap_or(DeviceType::Other).into(), ) .insert(&ctx.pool) .map(|_| ()) } }) .await .unwrap()?; Ok(()) }