From 8153a216b988eaf3b2c09cd6f84b352ff55e4fce Mon Sep 17 00:00:00 2001 From: rzuasti Date: Wed, 27 May 2026 10:13:56 -0400 Subject: [PATCH] Add mark as new API endpoint for notifications POST /api/notifications/{id}/mark_as_new sets is_new=1, allowing a notification to be flagged as unread after it has been read. Co-Authored-By: Claude Sonnet 4.6 --- backend/src/db/notifications.rs | 45 +++++++++++++++++++++++++ backend/src/web_server.rs | 6 +++- backend/src/web_server/notifications.rs | 14 ++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/backend/src/db/notifications.rs b/backend/src/db/notifications.rs index aaa0624..80e7fdf 100644 --- a/backend/src/db/notifications.rs +++ b/backend/src/db/notifications.rs @@ -93,6 +93,21 @@ pub fn mark_as_old(id: i64) -> Result<(), DbError> { } } +pub fn mark_as_new(id: i64) -> Result<(), DbError> { + let conn = db::get_db_connection(); + + match conn.execute("UPDATE notifications SET is_new=1 WHERE id=?1", params![id]) { + Ok(_) => { + debug!("Notification id={} flagged as new", id); + Ok(()) + } + Err(error) => { + error!("Error marking notification ({id}) as new: {error}"); + Err(DbError::from(error)) + } + } +} + pub fn read(id: i64) -> Option { let conn = db::get_db_connection(); @@ -162,6 +177,36 @@ mod tests { ); } + #[tokio::test] + async fn test_mark_as_new() { + tests_common::setup().await; + + // Mark non-existant notification (should be fine) + mark_as_new(9999999).unwrap(); + + // Create notification with is_new=false + let created_on = Utc::now(); + let inserted_id = insert(Notification::new( + created_on, + NotificationType::DeviceOnlineAfterTime, + "New notification title".to_string(), + "New notification body".to_string(), + false, + )) + .unwrap(); + + // Mark it as new + mark_as_new(inserted_id).unwrap(); + + // Read it and validate + let notification = read(inserted_id).unwrap(); + + assert!( + notification.is_new, + "Notification id={inserted_id} should have is_new=1." + ); + } + #[tokio::test] async fn test_insert() { tests_common::setup().await; diff --git a/backend/src/web_server.rs b/backend/src/web_server.rs index 6543243..8a95024 100644 --- a/backend/src/web_server.rs +++ b/backend/src/web_server.rs @@ -5,7 +5,7 @@ use axum::extract::Request; use axum::http::StatusCode; use axum::middleware::Next; use axum::response::Response; -use axum::routing::{delete, get, put}; +use axum::routing::{delete, get, post, put}; use axum::{Router, http}; use log::{debug, error, info}; use tower::ServiceBuilder; @@ -38,6 +38,10 @@ pub async fn serve() -> Result<(), Box> { "/api/notifications/{id}/read_without_flagging", get(notifications::read_without_flagging), ) + .route( + "/api/notifications/{id}/mark_as_new", + post(notifications::mark_as_new), + ) .route_layer(axum::middleware::from_fn(auth)) .layer(ServiceBuilder::new().layer(cors_layer)) .route( diff --git a/backend/src/web_server/notifications.rs b/backend/src/web_server/notifications.rs index 95d4713..73f77df 100644 --- a/backend/src/web_server/notifications.rs +++ b/backend/src/web_server/notifications.rs @@ -4,6 +4,7 @@ use axum::{ Json, extract::{Path, Query}, http::StatusCode, + response::IntoResponse, }; use log::error; @@ -31,6 +32,19 @@ pub async fn read_without_flagging(Path(id): Path) -> Result) -> impl IntoResponse { + match db::notifications::mark_as_new(id) { + Ok(_) => (StatusCode::OK, "Notification marked as new"), + Err(err) => { + error!("Error marking notification (id={id}) as new: {}", err); + ( + StatusCode::INTERNAL_SERVER_ERROR, + "Error updating notification in the server, check your logs", + ) + } + } +} + pub async fn list( Query(params): Query>, ) -> Result>, StatusCode> {