Feed polling

This commit is contained in:
Greg Shuflin 2025-02-02 15:24:33 -08:00
parent f8b81d8382
commit 5c103507c1
7 changed files with 87 additions and 34 deletions

2
Cargo.lock generated
View File

@ -2432,6 +2432,7 @@ dependencies = [
"tokio-stream", "tokio-stream",
"tokio-util", "tokio-util",
"ubyte", "ubyte",
"uuid",
"version_check", "version_check",
"yansi", "yansi",
] ]
@ -2513,6 +2514,7 @@ dependencies = [
"time", "time",
"tokio", "tokio",
"uncased", "uncased",
"uuid",
] ]
[[package]] [[package]]

View File

@ -7,7 +7,7 @@ edition = "2021"
argon2 = "0.5.3" argon2 = "0.5.3"
atom_syndication = "0.12.6" atom_syndication = "0.12.6"
chrono = { version = "0.4.34", features = ["serde"] } chrono = { version = "0.4.34", features = ["serde"] }
rocket = { version = "0.5.1", features = ["json", "secrets"] } rocket = { version = "0.5.1", features = ["json", "secrets", "uuid"] }
rocket_db_pools = { version = "0.2.0", features = ["sqlx_sqlite"] } rocket_db_pools = { version = "0.2.0", features = ["sqlx_sqlite"] }
rocket_dyn_templates = { version = "0.2.0", features = ["tera"] } rocket_dyn_templates = { version = "0.2.0", features = ["tera"] }
rss = "2.0.11" rss = "2.0.11"

31
src/feed_utils.rs Normal file
View File

@ -0,0 +1,31 @@
use feed_rs;
use url::Url;
#[derive(Debug)]
pub struct FeedError;
pub async fn fetch_feed(url: &Url) -> Result<feed_rs::model::Feed, FeedError> {
// Fetch the feed content
let response = reqwest::get(url.as_ref()).await.map_err(|e| {
eprintln!("Failed to fetch feed: {}", e);
FeedError
})?;
let content = response.text().await.map_err(|e| {
eprintln!("Failed to read response body: {}", e);
FeedError
})?;
// Parse the feed
let feed_data = feed_rs::parser::parse(content.as_bytes()).map_err(|e| {
eprintln!("Failed to parse feed content: {}", e);
FeedError
})?;
println!("Fetched feed: {}", url.as_ref());
for item in &feed_data.entries {
println!("{:?}", item);
}
Ok(feed_data)
}

View File

@ -5,6 +5,7 @@ use sqlx::types::JsonValue;
use url::Url; use url::Url;
use uuid::Uuid; use uuid::Uuid;
use crate::feed_utils::fetch_feed;
use crate::user::AuthenticatedUser; use crate::user::AuthenticatedUser;
use crate::Db; use crate::Db;
@ -43,36 +44,6 @@ pub struct NewFeed {
pub categorization: Vec<String>, pub categorization: Vec<String>,
} }
#[derive(Debug)]
struct FeedError;
async fn fetch_feed(url: &Url) -> Result<feed_rs::model::Feed, FeedError> {
// Fetch the feed content
let response = reqwest::get(url.as_ref()).await.map_err(|e| {
eprintln!("Failed to fetch feed: {}", e);
FeedError
})?;
let content = response.text().await.map_err(|e| {
eprintln!("Failed to read response body: {}", e);
FeedError
})?;
// Parse the feed
let feed_data = feed_rs::parser::parse(content.as_bytes()).map_err(|e| {
eprintln!("Failed to parse feed content: {}", e);
FeedError
})?;
println!("Fetched feed: {}", url.as_ref());
for item in &feed_data.entries {
println!("{:?}", item);
}
Ok(feed_data)
}
#[post("/feeds", data = "<new_feed>")] #[post("/feeds", data = "<new_feed>")]
pub async fn create_feed( pub async fn create_feed(
mut db: Connection<Db>, mut db: Connection<Db>,

View File

@ -1,7 +1,9 @@
#[macro_use] #[macro_use]
extern crate rocket; extern crate rocket;
mod feed_utils;
mod feeds; mod feeds;
mod poll;
mod user; mod user;
use rocket::fs::FileServer; use rocket::fs::FileServer;
@ -64,6 +66,7 @@ fn rocket() -> _ {
feeds::get_feed, feeds::get_feed,
feeds::list_feeds, feeds::list_feeds,
feeds::delete_feed, feeds::delete_feed,
poll::poll_feed,
], ],
) )
.mount("/static", FileServer::from("static")) .mount("/static", FileServer::from("static"))

35
src/poll.rs Normal file
View File

@ -0,0 +1,35 @@
use crate::{feed_utils::fetch_feed, Db};
use rocket::http::Status;
use rocket::serde::uuid::Uuid;
use rocket::serde::{json::Json, Serialize};
use rocket_db_pools::Connection;
#[derive(Debug, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct FeedPollResponse {
count: usize,
}
#[post("/poll/<feed_id>")]
pub async fn poll_feed(
mut db: Connection<Db>,
feed_id: Uuid,
) -> Result<Json<FeedPollResponse>, Status> {
let feed_id = feed_id.to_string();
// Get the feed URL from the database
let feed_url = sqlx::query!("SELECT url FROM feeds WHERE feed_id = ?", feed_id)
.fetch_optional(&mut **db)
.await
.map_err(|_| Status::InternalServerError)?
.ok_or(Status::NotFound)?
.url;
// 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)?;
Ok(Json(FeedPollResponse {
count: feed_data.entries.len(),
}))
}

View File

@ -33,9 +33,20 @@ function renderFeedItem(feed) {
const name = document.createElement('span'); const name = document.createElement('span');
name.className = 'feed-name'; name.className = 'feed-name';
name.textContent = feed.name; name.textContent = feed.name;
name.onclick = () => { name.onclick = async () => {
// TODO: Handle feed click try {
console.log('Feed clicked:', feed); const response = await fetch(`/poll/${feed.feed_id}`, {
method: 'POST'
});
if (response.ok) {
const data = await response.json();
console.log('Feed poll response:', data);
} else {
console.error('Failed to poll feed:', response.status);
}
} catch (error) {
console.error('Error polling feed:', error);
}
}; };
const menuButton = document.createElement('button'); const menuButton = document.createElement('button');