From 47de37d6cb956d317c73bdf6e97fc271593434fb Mon Sep 17 00:00:00 2001 From: Greg Shuflin Date: Sun, 2 Feb 2025 02:01:51 -0800 Subject: [PATCH] use url crate --- Cargo.lock | 1 + Cargo.toml | 2 +- src/feeds.rs | 36 ++++++++++++++++++++++++------------ 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ad479d5..84721c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3279,6 +3279,7 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 01f5e63..73e399f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,4 @@ rss = "2.0.11" sqlx = { version = "0.7", features = ["runtime-tokio", "sqlite", "chrono", "uuid"] } uuid = { version = "1.7.0", features = ["v4", "serde"] } bcrypt = "0.15" -url = "2.5.4" +url = { version = "2.5", features = ["serde"] } diff --git a/src/feeds.rs b/src/feeds.rs index 7dda440..47b4cd6 100644 --- a/src/feeds.rs +++ b/src/feeds.rs @@ -3,6 +3,7 @@ use rocket::http::Status; use rocket::serde::{json::Json, Deserialize, Serialize}; use rocket_db_pools::Connection; use uuid::Uuid; +use url::Url; use crate::Db; use crate::user::AuthenticatedUser; @@ -12,14 +13,14 @@ use crate::user::AuthenticatedUser; pub struct Feed { feed_id: Uuid, name: String, - url: String, + url: Url, user_id: Uuid, added_time: chrono::DateTime, last_checked_time: chrono::DateTime, } impl Feed { - pub fn new(name: String, url: String, user_id: Uuid) -> Self { + pub fn new(name: String, url: Url, user_id: Uuid) -> Self { let now = chrono::Utc::now(); Feed { feed_id: Uuid::new_v4(), @@ -36,7 +37,7 @@ impl Feed { #[serde(crate = "rocket::serde")] pub struct NewFeed { pub name: String, - pub url: String, + pub url: Url, // Changed from String to Url } #[post("/feeds", data = "")] @@ -46,6 +47,12 @@ pub async fn create_feed( user: AuthenticatedUser, ) -> Result, Status> { let new_feed = new_feed.into_inner(); + + // URL validation only needs to check scheme now + if !new_feed.url.scheme().eq("http") && !new_feed.url.scheme().eq("https") { + return Err(Status::UnprocessableEntity); + } + let feed = Feed::new(new_feed.name, new_feed.url, user.user_id); let query = sqlx::query( @@ -54,7 +61,7 @@ pub async fn create_feed( ) .bind(feed.feed_id.to_string()) .bind(&feed.name) - .bind(&feed.url) + .bind(feed.url.as_str()) .bind(feed.user_id.to_string()) .bind(feed.added_time.to_rfc3339()) .bind(feed.last_checked_time.to_rfc3339()) @@ -106,15 +113,20 @@ pub async fn list_feeds( let feeds = query .into_iter() - .map(|row| Feed { - feed_id: Uuid::parse_str(&row.feed_id).unwrap(), - name: row.name, - url: row.url, - user_id: Uuid::parse_str(&row.user_id).unwrap(), - added_time: row.added_time, - last_checked_time: row.last_checked_time, + .map(|row| { + // Parse URL from string + let url = Url::parse(&row.url).map_err(|_| Status::InternalServerError)?; + + Ok(Feed { + feed_id: Uuid::parse_str(&row.feed_id).unwrap(), + name: row.name, + url, + user_id: Uuid::parse_str(&row.user_id).unwrap(), + added_time: row.added_time, + last_checked_time: row.last_checked_time, + }) }) - .collect(); + .collect::, Status>>()?; Ok(Json(feeds)) }