feat: implement basic devices list endpoint
parent
22e01d10dc
commit
d6fb4573d0
|
@ -33,7 +33,7 @@ impl ServeCommand {
|
|||
let pool = db::initialize_db(cli.data_dir.join(crate::DB_FILENAME), true).unwrap();
|
||||
|
||||
let ctx = server::Context { pool };
|
||||
let app = server::app().with_state(ctx);
|
||||
let app = server::app(ctx);
|
||||
|
||||
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
|
|
|
@ -16,21 +16,21 @@ use crate::db::{schema::*, DbPool, DbResult};
|
|||
#[diesel(table_name = devices)]
|
||||
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
|
||||
pub struct Device {
|
||||
id: i64,
|
||||
device_id: String,
|
||||
user_id: i64,
|
||||
caption: String,
|
||||
type_: DeviceType,
|
||||
pub id: i64,
|
||||
pub device_id: String,
|
||||
pub user_id: i64,
|
||||
pub caption: String,
|
||||
pub type_: DeviceType,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Insertable)]
|
||||
#[diesel(table_name = devices)]
|
||||
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
|
||||
pub struct NewDevice {
|
||||
device_id: String,
|
||||
user_id: i64,
|
||||
caption: String,
|
||||
type_: DeviceType,
|
||||
pub device_id: String,
|
||||
pub user_id: i64,
|
||||
pub caption: String,
|
||||
pub type_: DeviceType,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, FromSqlRow, Debug, AsExpression, Clone)]
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
use axum::{
|
||||
extract::{Path, State},
|
||||
middleware,
|
||||
routing::get,
|
||||
Extension, Json, Router,
|
||||
};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
db::{self, User},
|
||||
server::{
|
||||
error::{AppError, AppResult},
|
||||
Context,
|
||||
},
|
||||
};
|
||||
|
||||
use super::auth::auth_middleware;
|
||||
|
||||
pub fn router(ctx: Context) -> Router<Context> {
|
||||
Router::new()
|
||||
.route("/{username}", get(get_devices))
|
||||
.layer(middleware::from_fn_with_state(ctx.clone(), auth_middleware))
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Device {
|
||||
id: String,
|
||||
caption: String,
|
||||
r#type: String,
|
||||
subscriptions: i64,
|
||||
}
|
||||
|
||||
async fn get_devices(
|
||||
State(ctx): State<Context>,
|
||||
Path(username): Path<String>,
|
||||
Extension(user): Extension<User>,
|
||||
) -> AppResult<Json<Vec<Device>>> {
|
||||
// Check suffix is present and return 404 otherwise; axum doesn't support matching part of a
|
||||
// route segment
|
||||
let username = username.strip_suffix(".json").ok_or(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_.to_string(),
|
||||
// TODO implement subscription count
|
||||
subscriptions: 0,
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(Json(devices))
|
||||
// let devices: Vec<Device> = devices.iter
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
mod auth;
|
||||
mod devices;
|
||||
|
||||
use axum::{
|
||||
http::{HeaderName, HeaderValue},
|
||||
|
@ -8,9 +9,10 @@ use tower_http::set_header::SetResponseHeaderLayer;
|
|||
|
||||
use super::Context;
|
||||
|
||||
pub fn router() -> Router<Context> {
|
||||
pub fn router(ctx: Context) -> Router<Context> {
|
||||
Router::new()
|
||||
.nest("/auth", auth::router())
|
||||
.nest("/devices", devices::router(ctx))
|
||||
// https://gpoddernet.readthedocs.io/en/latest/api/reference/general.html#cors
|
||||
// All endpoints should send this CORS header value so the endpoints can be used from web
|
||||
// applications
|
||||
|
|
|
@ -9,8 +9,9 @@ pub struct Context {
|
|||
pub pool: crate::db::DbPool,
|
||||
}
|
||||
|
||||
pub fn app() -> Router<Context> {
|
||||
pub fn app(ctx: Context) -> Router {
|
||||
Router::new()
|
||||
.nest("/api/2", gpodder::router())
|
||||
.nest("/api/2", gpodder::router(ctx.clone()))
|
||||
.layer(TraceLayer::new_for_http())
|
||||
.with_state(ctx)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue