156 lines
3.9 KiB
Rust
156 lines
3.9 KiB
Rust
|
use chrono;
|
||
|
use rocket::http::Status;
|
||
|
use rocket::serde::{json::Json, Deserialize, Serialize};
|
||
|
use rocket_db_pools::Connection;
|
||
|
use uuid::Uuid;
|
||
|
|
||
|
use crate::Db;
|
||
|
use crate::user::AuthenticatedUser;
|
||
|
|
||
|
#[derive(Debug, Serialize)]
|
||
|
#[serde(crate = "rocket::serde")]
|
||
|
pub struct Feed {
|
||
|
feed_id: Uuid,
|
||
|
name: String,
|
||
|
url: String,
|
||
|
user_id: Uuid,
|
||
|
added_time: chrono::DateTime<chrono::Utc>,
|
||
|
last_checked_time: chrono::DateTime<chrono::Utc>,
|
||
|
}
|
||
|
|
||
|
impl Feed {
|
||
|
pub fn new(name: String, url: String, user_id: Uuid) -> Self {
|
||
|
let now = chrono::Utc::now();
|
||
|
Feed {
|
||
|
feed_id: Uuid::new_v4(),
|
||
|
name,
|
||
|
url,
|
||
|
user_id,
|
||
|
added_time: now,
|
||
|
last_checked_time: now,
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, Deserialize)]
|
||
|
#[serde(crate = "rocket::serde")]
|
||
|
pub struct NewFeed {
|
||
|
pub name: String,
|
||
|
pub url: String,
|
||
|
}
|
||
|
|
||
|
#[post("/feeds", data = "<new_feed>")]
|
||
|
pub async fn create_feed(
|
||
|
mut db: Connection<Db>,
|
||
|
new_feed: Json<NewFeed>,
|
||
|
user: AuthenticatedUser,
|
||
|
) -> Result<Json<Feed>, Status> {
|
||
|
let new_feed = new_feed.into_inner();
|
||
|
let feed = Feed::new(new_feed.name, new_feed.url, user.user_id);
|
||
|
|
||
|
let query = sqlx::query(
|
||
|
"INSERT INTO feeds (feed_id, name, url, user_id, added_time, last_checked_time)
|
||
|
VALUES (?1, ?2, ?3, ?4, ?5, ?6)"
|
||
|
)
|
||
|
.bind(feed.feed_id.to_string())
|
||
|
.bind(&feed.name)
|
||
|
.bind(&feed.url)
|
||
|
.bind(feed.user_id.to_string())
|
||
|
.bind(feed.added_time.to_rfc3339())
|
||
|
.bind(feed.last_checked_time.to_rfc3339())
|
||
|
.execute(&mut **db)
|
||
|
.await;
|
||
|
|
||
|
match query {
|
||
|
Ok(_) => Ok(Json(feed)),
|
||
|
Err(e) => {
|
||
|
eprintln!("Database error: {}", e);
|
||
|
match e {
|
||
|
sqlx::Error::Database(db_err) if db_err.is_unique_violation() => {
|
||
|
Err(Status::Conflict)
|
||
|
}
|
||
|
_ => Err(Status::InternalServerError),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[get("/feeds")]
|
||
|
pub async fn list_feeds(
|
||
|
mut db: Connection<Db>,
|
||
|
user: AuthenticatedUser,
|
||
|
) -> Result<Json<Vec<Feed>>, Status> {
|
||
|
let user_id = user.user_id.to_string();
|
||
|
|
||
|
let query = sqlx::query!(
|
||
|
r#"
|
||
|
SELECT
|
||
|
feed_id as "feed_id: String",
|
||
|
name,
|
||
|
url,
|
||
|
user_id as "user_id: String",
|
||
|
added_time as "added_time: chrono::DateTime<chrono::Utc>",
|
||
|
last_checked_time as "last_checked_time: chrono::DateTime<chrono::Utc>"
|
||
|
FROM feeds
|
||
|
WHERE user_id = ?
|
||
|
ORDER BY added_time DESC
|
||
|
"#,
|
||
|
user_id
|
||
|
)
|
||
|
.fetch_all(&mut **db)
|
||
|
.await
|
||
|
.map_err(|e| {
|
||
|
eprintln!("Database error: {}", e);
|
||
|
Status::InternalServerError
|
||
|
})?;
|
||
|
|
||
|
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,
|
||
|
})
|
||
|
.collect();
|
||
|
|
||
|
Ok(Json(feeds))
|
||
|
}
|
||
|
|
||
|
#[delete("/feeds/<feed_id>")]
|
||
|
pub async fn delete_feed(
|
||
|
mut db: Connection<Db>,
|
||
|
feed_id: &str,
|
||
|
user: AuthenticatedUser,
|
||
|
) -> Status {
|
||
|
// Validate UUID format
|
||
|
let feed_uuid = match Uuid::parse_str(feed_id) {
|
||
|
Ok(uuid) => uuid,
|
||
|
Err(_) => return Status::BadRequest,
|
||
|
};
|
||
|
|
||
|
let query = sqlx::query(
|
||
|
"DELETE FROM feeds WHERE feed_id = ? AND user_id = ?"
|
||
|
)
|
||
|
.bind(feed_uuid.to_string())
|
||
|
.bind(user.user_id.to_string())
|
||
|
.execute(&mut **db)
|
||
|
.await;
|
||
|
|
||
|
match query {
|
||
|
Ok(result) => {
|
||
|
if result.rows_affected() > 0 {
|
||
|
Status::NoContent
|
||
|
} else {
|
||
|
Status::NotFound
|
||
|
}
|
||
|
}
|
||
|
Err(e) => {
|
||
|
eprintln!("Database error: {}", e);
|
||
|
Status::InternalServerError
|
||
|
}
|
||
|
}
|
||
|
}
|