diff --git a/backend/src/db/article_history.rs b/backend/src/db/article_history.rs new file mode 100644 index 0000000..be78650 --- /dev/null +++ b/backend/src/db/article_history.rs @@ -0,0 +1,76 @@ +//! Article history: tracks which article URLs have been used in past syntheses. +//! +//! Prevents the same article from appearing in multiple syntheses. + +use std::collections::HashSet; +use sqlx::PgPool; +use uuid::Uuid; +use crate::errors::AppError; + +/// Check which URL hashes already exist in history for this user. +/// +/// Returns the set of url_hashes that were found (i.e., already used). +pub async fn check_urls_exist( + pool: &PgPool, + user_id: Uuid, + url_hashes: &[String], +) -> Result, AppError> { + if url_hashes.is_empty() { + return Ok(HashSet::new()); + } + + let rows = sqlx::query_scalar::<_, String>( + "SELECT url_hash FROM article_history WHERE user_id = $1 AND url_hash = ANY($2)", + ) + .bind(user_id) + .bind(url_hashes) + .fetch_all(pool) + .await?; + + Ok(rows.into_iter().collect()) +} + +/// Insert article URLs into history. +/// +/// Uses ON CONFLICT DO NOTHING to silently skip duplicates. +pub async fn insert_urls( + pool: &PgPool, + user_id: Uuid, + urls: &[(String, String)], // Vec<(url, url_hash)> +) -> Result<(), AppError> { + if urls.is_empty() { + return Ok(()); + } + + for (url, url_hash) in urls { + sqlx::query( + "INSERT INTO article_history (user_id, url_hash, url) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING", + ) + .bind(user_id) + .bind(url_hash) + .bind(url) + .execute(pool) + .await?; + } + + Ok(()) +} + +/// Delete history entries older than N days for this user. +/// +/// Returns the number of deleted rows. +pub async fn cleanup_old( + pool: &PgPool, + user_id: Uuid, + days: i32, +) -> Result { + let result = sqlx::query( + "DELETE FROM article_history WHERE user_id = $1 AND created_at < now() - make_interval(days => $2)", + ) + .bind(user_id) + .bind(days) + .execute(pool) + .await?; + + Ok(result.rows_affected()) +} diff --git a/backend/src/db/mod.rs b/backend/src/db/mod.rs index b16e853..d704916 100644 --- a/backend/src/db/mod.rs +++ b/backend/src/db/mod.rs @@ -1,3 +1,4 @@ +pub mod article_history; pub mod api_keys; pub mod audit; pub mod magic_links;