use crate::{Db, SqlError, Timestamp, auth::BasicAuth}; use chrono::Utc; use rocket::{ Route, get, post, put, routes, serde::{Deserialize, Serialize, json::Json}, trace::debug, }; use rocket_db_pools::Connection; use sqlx::{Acquire, Execute}; #[put("/subscriptions/<_user>/")] pub fn set(_user: &str, device: &str, auth: BasicAuth) -> &'static str { debug!("Attempting to set {}/{device}", auth.username()); "" } #[get("/subscriptions/<_user>/.opml")] pub fn list_opml(_user: &str, device: &str, auth: BasicAuth) -> &'static str { debug!("Attempting to list {}/{device}", auth.username()); "" } #[derive(Debug, Serialize, Deserialize)] #[serde(crate = "rocket::serde")] pub struct Subscriptions { add: Vec, remove: Vec, timestamp: i64, } #[get("/subscriptions/<_user>/.json?")] pub async fn list_json( _user: &str, device: &str, since: Option, mut db: Connection, auth: BasicAuth, ) -> Result, SqlError> { debug!( "Attempting to list {}/{device}, since {since:?}", auth.username() ); let time = Utc::now().timestamp(); let username = auth.username(); let since = since.unwrap_or(0); let results = sqlx::query!( "SELECT * FROM subscriptions WHERE device = ? AND user = ? AND updated >= ?", device, username, since, ) .fetch_all(&mut **db) .await?; let mut res = Subscriptions { add: vec![], remove: vec![], timestamp: time, }; for record in results { if record.current == 1 { res.add.push(record.url); } else if record.current == 0 { res.remove.push(record.url); } else { debug!("Found current value of `{}`", record.current); } } Ok(Json(res)) } #[derive(Debug, Serialize, Deserialize)] #[serde(crate = "rocket::serde")] pub struct SubscriptionChanges { add: Vec, remove: Vec, } #[derive(Debug, Serialize, Deserialize)] #[serde(crate = "rocket::serde")] pub struct SubscriptionChangesResult { pub update_urls: Vec<(String, String)>, pub timestamp: i64, } #[post("/subscriptions/<_user>/.json", data = "")] pub async fn update_json( _user: &str, device: &str, updates: Json, mut db: Connection, auth: BasicAuth, ) -> Result, SqlError> { let time = Utc::now().timestamp(); let updates = updates.into_inner(); debug!( "Attempting to update {}/{device}, new: {:?}", auth.username(), updates ); let mut trans = db.begin().await?; for removed in &updates.remove { sqlx::query("INSERT INTO subscriptions (url, device, user, current, updated) VALUES (?, ?, ?, ?, ?)") .bind(removed) .bind(device) .bind(auth.username()) .bind(0) .bind(time) .execute(&mut *trans) .await?; } for added in &updates.add { sqlx::query("INSERT INTO subscriptions (url, device, user, current, updated) VALUES (?, ?, ?, ?, ?)") .bind(added) .bind(device) .bind(auth.username()) .bind(1) .bind(time) .execute(&mut *trans) .await?; } trans.commit().await?; // sqlx::qu Ok(Json(SubscriptionChangesResult { update_urls: vec![], timestamp: time, })) } #[get("/subscriptions/<_user>.json")] pub fn list_all_json(_user: &str, auth: BasicAuth) -> Json> { debug!("Attempting to list {}", auth.username()); Json(vec![]) } pub fn routes() -> Vec { routes![set, list_json, list_opml, list_all_json, update_json] }