feat: add article_history DB module (check, insert, cleanup)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>master
parent
c271c240a2
commit
5a928aa990
@ -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<HashSet<String>, 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<u64, AppError> {
|
||||
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())
|
||||
}
|
||||
Loading…
Reference in New Issue