diff --git a/.sqlx/query-ad2e09903009082ca746a9dc75cb04831b016d17cc7ce32fa86681431071fef6.json b/.sqlx/query-ad2e09903009082ca746a9dc75cb04831b016d17cc7ce32fa86681431071fef6.json new file mode 100644 index 0000000..6d85b84 --- /dev/null +++ b/.sqlx/query-ad2e09903009082ca746a9dc75cb04831b016d17cc7ce32fa86681431071fef6.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "UPDATE feed_entries SET marked_read = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "ad2e09903009082ca746a9dc75cb04831b016d17cc7ce32fa86681431071fef6" +} diff --git a/.sqlx/query-c64e0927e594985ee8d2be7190c8f76fea57c4c815981673c92e709b17b9204b.json b/.sqlx/query-c64e0927e594985ee8d2be7190c8f76fea57c4c815981673c92e709b17b9204b.json new file mode 100644 index 0000000..e9c65a8 --- /dev/null +++ b/.sqlx/query-c64e0927e594985ee8d2be7190c8f76fea57c4c815981673c92e709b17b9204b.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "UPDATE feed_entries SET marked_read = NULL WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 1 + }, + "nullable": [] + }, + "hash": "c64e0927e594985ee8d2be7190c8f76fea57c4c815981673c92e709b17b9204b" +} diff --git a/.sqlx/query-ce12e0b02596a4045fb2cd91d566aa633109a65afcecc92564a785282e67e193.json b/.sqlx/query-ce12e0b02596a4045fb2cd91d566aa633109a65afcecc92564a785282e67e193.json new file mode 100644 index 0000000..eb53bf4 --- /dev/null +++ b/.sqlx/query-ce12e0b02596a4045fb2cd91d566aa633109a65afcecc92564a785282e67e193.json @@ -0,0 +1,20 @@ +{ + "db_name": "SQLite", + "query": "\n SELECT 1 as found FROM feed_entries e\n JOIN feeds f ON e.feed_id = f.feed_id\n WHERE e.id = ? AND f.user_id = ?\n ", + "describe": { + "columns": [ + { + "name": "found", + "ordinal": 0, + "type_info": "Int" + } + ], + "parameters": { + "Right": 2 + }, + "nullable": [ + false + ] + }, + "hash": "ce12e0b02596a4045fb2cd91d566aa633109a65afcecc92564a785282e67e193" +} diff --git a/src/feed_utils.rs b/src/feed_utils.rs index 52359eb..1230f3f 100644 --- a/src/feed_utils.rs +++ b/src/feed_utils.rs @@ -1,6 +1,6 @@ use feed_rs; -use url::Url; use tracing::{error, info}; +use url::Url; #[derive(Debug)] pub struct FeedError; diff --git a/src/main.rs b/src/main.rs index 70bc6ad..3686bfd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -160,6 +160,7 @@ fn rocket() -> _ { feeds::list_feeds, feeds::delete_feed, poll::poll_feed, + poll::update_entry_state, ], ) .mount("/static", FileServer::from("static")) diff --git a/src/poll.rs b/src/poll.rs index 5d4ac46..0c169b9 100644 --- a/src/poll.rs +++ b/src/poll.rs @@ -4,7 +4,7 @@ use chrono::{DateTime, Duration, Utc}; use feed_rs::model::Text; use rocket::http::Status; use rocket::serde::uuid::Uuid; -use rocket::serde::{self, json::Json, Serialize}; +use rocket::serde::{self, json::Json, Deserialize, Serialize}; use rocket_db_pools::Connection; use sqlx::{Acquire, SqliteConnection}; use tracing::{error, info}; @@ -33,6 +33,12 @@ struct Entry { link: Option, } +#[derive(Debug, Deserialize)] +#[serde(crate = "rocket::serde")] +pub struct EntryStateUpdate { + read: Option, +} + async fn update_entry_db( entries: &Vec, feed_id: &str, @@ -231,3 +237,63 @@ pub async fn poll_feed( Ok(Json(FeedPollResponse { count, entries })) } + +#[patch("/entries//state", data = "")] +pub async fn update_entry_state( + mut db: Connection, + entry_id: &str, + user: AuthenticatedUser, + state: Json, +) -> Result { + let state = state.into_inner(); + + let user_id = user.user_id.to_string(); + // Verify the entry exists and belongs to a feed owned by this user + let exists = sqlx::query!( + r#" + SELECT 1 as found FROM feed_entries e + JOIN feeds f ON e.feed_id = f.feed_id + WHERE e.id = ? AND f.user_id = ? + "#, + entry_id, + user_id, + ) + .fetch_optional(&mut **db) + .await + .map_err(|e| { + error!("Database error checking entry ownership: {}", e); + Status::InternalServerError + })?; + + if exists.is_none() { + return Ok(Status::NotFound); + } + + // Update read state if provided + if let Some(read) = state.read { + let now = Utc::now(); + let result = if read { + sqlx::query!( + "UPDATE feed_entries SET marked_read = ? WHERE id = ?", + now, + entry_id + ) + .execute(&mut **db) + .await + } else { + sqlx::query!( + "UPDATE feed_entries SET marked_read = NULL WHERE id = ?", + entry_id + ) + .execute(&mut **db) + .await + }; + + if let Err(e) = result { + error!("Failed to update entry read state: {}", e); + return Ok(Status::InternalServerError); + } + } + + Ok(Status::NoContent) +}