Separate logic out

This commit is contained in:
Greg Shuflin 2025-02-04 15:51:55 -08:00
parent 2fd1ade3ee
commit 480d686b1c

View File

@ -7,6 +7,7 @@ use rocket::serde::uuid::Uuid;
use rocket::serde::{self, json::Json, Serialize}; use rocket::serde::{self, json::Json, Serialize};
use rocket_db_pools::Connection; use rocket_db_pools::Connection;
use sqlx::{Acquire, SqliteConnection}; use sqlx::{Acquire, SqliteConnection};
use url::Url;
const POLLING_INTERVAL: Duration = Duration::minutes(20); const POLLING_INTERVAL: Duration = Duration::minutes(20);
@ -29,7 +30,11 @@ struct Entry {
link: Option<String>, link: Option<String>,
} }
async fn update_entry_db(entries: &Vec<Entry>, feed_id: &str, db: &mut SqliteConnection) -> Result<(), Status> { async fn update_entry_db(
entries: &Vec<Entry>,
feed_id: &str,
db: &mut SqliteConnection,
) -> Result<(), Status> {
// Start a transaction for batch update // Start a transaction for batch update
let mut tx = db.begin().await.map_err(|e| { let mut tx = db.begin().await.map_err(|e| {
eprintln!("Failed to start transaction: {}", e); eprintln!("Failed to start transaction: {}", e);
@ -89,38 +94,9 @@ async fn update_entry_db(entries: &Vec<Entry>, feed_id: &str, db: &mut SqliteCon
Ok(()) Ok(())
} }
#[post("/poll/<feed_id>")] /// Perform the request to fetch from the remote feed url
pub async fn poll_feed( async fn fetch_new_entries(url: &Url) -> Result<Vec<Entry>, Status> {
mut db: Connection<Db>,
feed_id: Uuid,
user: AuthenticatedUser,
) -> Result<Json<FeedPollResponse>, Status> {
let feed_id = feed_id.to_string();
let user_id = user.user_id.to_string();
// Get the feed URL from the database, ensuring it belongs to the authenticated user
let feed = sqlx::query!(
r#"SELECT url, last_checked_time as "last_checked_time: chrono::DateTime<chrono::Utc>" FROM feeds WHERE feed_id = ? AND user_id = ?"#,
feed_id,
user_id
)
.fetch_optional(&mut **db)
.await
.map_err(|_| Status::InternalServerError)?
.ok_or(Status::NotFound)?;
let now = Utc::now();
if now - feed.last_checked_time < POLLING_INTERVAL {
println!(
"Feed {} was checked recently at {}",
feed_id, feed.last_checked_time
);
}
// Parse the URL
let url = url::Url::parse(&feed.url).map_err(|_| Status::InternalServerError)?;
let feed_data = fetch_feed(&url).await.map_err(|_| Status::BadGateway)?; let feed_data = fetch_feed(&url).await.map_err(|_| Status::BadGateway)?;
let count = feed_data.entries.len();
fn get(item: Option<Text>, name: &'static str) -> String { fn get(item: Option<Text>, name: &'static str) -> String {
item.map(|t| t.content.to_string()) item.map(|t| t.content.to_string())
@ -140,7 +116,41 @@ pub async fn poll_feed(
link: feed_entry.links.first().map(|l| l.href.clone()), link: feed_entry.links.first().map(|l| l.href.clone()),
}) })
.collect(); .collect();
Ok(entries)
}
#[post("/poll/<feed_id>")]
pub async fn poll_feed(
mut db: Connection<Db>,
feed_id: Uuid,
user: AuthenticatedUser,
) -> Result<Json<FeedPollResponse>, Status> {
let feed_id = feed_id.to_string();
let user_id = user.user_id.to_string();
// Get the feed URL from the database, ensuring it belongs to the authenticated user
let feed = sqlx::query!(
r#"SELECT url, last_checked_time as "last_checked_time: chrono::DateTime<chrono::Utc>" FROM feeds WHERE feed_id = ? AND user_id = ?"#,
feed_id,
user_id
)
.fetch_optional(&mut **db)
.await
.map_err(|_| Status::InternalServerError)?
.ok_or(Status::NotFound)?;
let url = url::Url::parse(&feed.url).map_err(|_| Status::InternalServerError)?;
let now = Utc::now();
if now - feed.last_checked_time < POLLING_INTERVAL {
println!(
"Feed {} was checked recently at {}",
feed_id, feed.last_checked_time
);
}
let entries = fetch_new_entries(&url).await?;
let count = entries.len();
update_entry_db(&entries, &feed_id, &mut db).await?; update_entry_db(&entries, &feed_id, &mut db).await?;
Ok(Json(FeedPollResponse { count, entries })) Ok(Json(FeedPollResponse { count, entries }))