Initial implementation

This commit is contained in:
2025-09-27 00:29:57 -05:00
commit 957bf1b549
13 changed files with 4671 additions and 0 deletions

133
src/devices.rs Normal file
View File

@@ -0,0 +1,133 @@
use std::borrow::Cow;
use crate::{
Db, DotJson, SqlError, Timestamp,
auth::BasicAuth,
directory::{Episode, Podcast},
};
use rocket::{
Route, TypedError, get, post, routes,
serde::{Deserialize, Serialize, json::Json},
trace::debug,
};
use rocket_db_pools::{
Connection,
sqlx::{self, Connection as _},
};
#[derive(Debug, Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct Device {
id: String,
caption: String,
r#type: String,
// subscriptions: usize,
}
#[get("/devices/<_user>.json")]
pub async fn get(
_user: &str,
auth: BasicAuth,
mut db: Connection<Db>,
) -> Result<Json<Vec<Device>>, SqlError> {
let username = auth.username();
debug!("Attempting to get devices for {username}");
let res: Vec<Device> = sqlx::query_as!(
Device,
"SELECT id, caption, type FROM devices WHERE user = ?",
username
)
.fetch_all(&mut **db)
.await?;
Ok(Json(res))
// todo!()
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct DeviceUpdate<'a> {
caption: Option<&'a str>,
r#type: Option<&'a str>,
}
#[post("/devices/<_user>/<device>.json", data = "<dev>")]
pub async fn update(
_user: &str,
device: &str,
dev: Json<DeviceUpdate<'_>>,
mut db: Connection<Db>,
auth: BasicAuth,
) -> Result<Json<Device>, SqlError> {
let username = auth.username();
debug!("Attempting to update {device} for {username}");
let mut trans = db.begin().await?;
let existing = sqlx::query_as!(
Device,
"SELECT id, caption, type FROM devices WHERE user = ? AND id = ?",
username,
device
)
.fetch_optional(&mut *trans)
.await?;
// debug!("Users: {:?}", sqlx::query!("SELECT * FROM users").fetch_all(&mut **db).await);
let res = sqlx::query("INSERT INTO devices (id, user, caption, type) VALUES (?, ?, ?, ?)")
.bind(device)
.bind(username)
.bind(
dev.caption
.or(existing.as_ref().map(|d| d.caption.as_str()))
.unwrap_or(""),
)
.bind(
dev.r#type
.or(existing.as_ref().map(|d| d.r#type.as_str()))
.unwrap_or(""),
)
.execute(&mut *trans)
.await?;
debug!(
"Attempting to update {} for {username}, {dev:?}, {res:?}",
device
);
let res = sqlx::query_as!(
Device,
"SELECT id, caption, type FROM devices WHERE user = ? AND id = ?",
username,
device
)
.fetch_one(&mut *trans)
.await?;
trans.commit().await?;
Ok(Json(res))
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct DeviceUpdates<'a> {
add: Vec<Podcast<'a>>,
remove: Vec<Cow<'a, str>>,
updates: Vec<Episode<'a>>,
timestamp: Timestamp,
}
#[get("/updates/<username>/<device>?<since>&<include_actions>")]
pub fn updates(
username: &str,
device: DotJson<&str>,
since: Option<Timestamp>,
include_actions: Option<bool>,
_auth: BasicAuth,
) -> Json<DeviceUpdates<'static>> {
debug!("Attempting to update {} for {username}, {since:?}", *device);
Json(DeviceUpdates {
add: vec![],
remove: vec![],
updates: vec![],
timestamp: Timestamp::now(),
})
}
pub fn routes() -> Vec<Route> {
routes![get, update, updates]
}