diff --git a/Cargo.lock b/Cargo.lock index ac920cb..a657978 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -293,17 +293,6 @@ dependencies = [ "quick-xml", ] -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -925,19 +914,6 @@ dependencies = [ "url", ] -[[package]] -name = "env_logger" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - [[package]] name = "equivalent" version = "1.0.1" @@ -1129,6 +1105,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "getopts" version = "0.2.21" @@ -1203,15 +1189,6 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.3.2" @@ -1275,12 +1252,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - [[package]] name = "hyper" version = "0.14.27" @@ -1672,7 +1643,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.2", + "hermit-abi", "libc", ] @@ -2283,11 +2254,9 @@ dependencies = [ "diesel", "diesel-connection", "dotenv", - "env_logger", "futures", "hmac", "jwt", - "log", "once_cell", "reqwest", "rss", @@ -2298,9 +2267,11 @@ dependencies = [ "serde_json", "sha2", "tokio", + "tracing", "tracing-actix-web", "tracing-appender", - "tracing-log", + "tracing-bunyan-formatter", + "tracing-log 0.2.0", "tracing-subscriber", "uuid", ] @@ -2709,15 +2680,6 @@ dependencies = [ "utf-8", ] -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - [[package]] name = "thin-slice" version = "0.1.1" @@ -2903,11 +2865,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -2951,15 +2912,44 @@ dependencies = [ ] [[package]] -name = "tracing-core" -version = "0.1.31" +name = "tracing-bunyan-formatter" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "b5c266b9ac83dedf0e0385ad78514949e6d89491269e7065bee51d2bb8ec7373" +dependencies = [ + "ahash", + "gethostname", + "log", + "serde", + "serde_json", + "time", + "tracing", + "tracing-core", + "tracing-log 0.1.4", + "tracing-subscriber", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", ] +[[package]] +name = "tracing-log" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + [[package]] name = "tracing-log" version = "0.2.0" @@ -2986,7 +2976,7 @@ dependencies = [ "thread_local", "tracing", "tracing-core", - "tracing-log", + "tracing-log 0.2.0", ] [[package]] @@ -3198,15 +3188,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 8d95f3f..e3e9479 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,8 +29,6 @@ uuid = {version = "1.2.1", features=["serde", "v4"]} jwt = "0.16.0" hmac = "0.12.1" sha2 = "0.10.6" -log = "0.4.17" -env_logger = "0.9.3" scraper = "0.14.0" actix-cors = "0.6.4" chrono = { version = "0.4.31", features = ["serde"] } @@ -43,6 +41,8 @@ tracing-subscriber = { version = "0.3.18", features = ["registry", "env-filter"] tracing-log = "0.2.0" config = "0.14.0" diesel-connection = "4.1.0" +tracing = { version = "0.1.40", features = ["log"] } +tracing-bunyan-formatter = "0.3.9" [dependencies.serde_json] version = "1.0.86" diff --git a/src/auth/mod.rs b/src/auth/mod.rs index 1c4fb3b..c7bffce 100755 --- a/src/auth/mod.rs +++ b/src/auth/mod.rs @@ -4,6 +4,7 @@ pub mod processes; use crate::auth::processes::check_password; use crate::auth::processes::extract_header_token; +#[tracing::instrument(name = "Process token")] pub fn process_token(request: &ServiceRequest) -> Result { match extract_header_token(request) { Ok(token) => check_password(token), diff --git a/src/auth/processes.rs b/src/auth/processes.rs index 945a1fe..1a56ef6 100755 --- a/src/auth/processes.rs +++ b/src/auth/processes.rs @@ -1,21 +1,19 @@ use super::jwt; use actix_web::dev::ServiceRequest; +use secrecy::{ExposeSecret, Secret}; -pub fn check_password(password: String) -> Result { - match jwt::JwtToken::decode(password) { +pub fn check_password(password: Secret) -> Result { + match jwt::JwtToken::decode(password.expose_secret().to_string()) { Ok(_token) => Ok(String::from("passed")), Err(message) => Err(message), } } -pub fn extract_header_token(request: &ServiceRequest) -> Result { - log::info!("Request: {:?}", request); +#[tracing::instrument(name = "Extract Header Token")] +pub fn extract_header_token(request: &ServiceRequest) -> Result, &'static str> { match request.headers().get("user-token") { Some(token) => match token.to_str() { - Ok(processed_password) => { - log::info!("Token provided: {}", processed_password); - Ok(String::from(processed_password)) - } + Ok(processed_password) => Ok(Secret::new(String::from(processed_password))), Err(_processed_password) => Err("there was an error processing token"), }, None => Err("there is no token"), @@ -25,6 +23,7 @@ pub fn extract_header_token(request: &ServiceRequest) -> Result = Secret::new(JwtToken::encode(32)); let result = check_password(password_string); @@ -44,7 +43,7 @@ mod processes_test { #[test] fn incorrect_check_password() { - let password: String = String::from("test"); + let password: Secret = Secret::new(String::from("test")); match check_password(password) { Err(message) => assert_eq!("could not decode token", message), @@ -59,7 +58,7 @@ mod processes_test { .to_srv_request(); match super::extract_header_token(&request) { - Ok(processed_password) => assert_eq!("token", processed_password), + Ok(processed_password) => assert_eq!("token", processed_password.expose_secret()), _ => panic!("failed extract_header_token"), } } diff --git a/src/json_serialization/new_feed.rs b/src/json_serialization/new_feed.rs index c4f1381..19e27ea 100644 --- a/src/json_serialization/new_feed.rs +++ b/src/json_serialization/new_feed.rs @@ -1,6 +1,6 @@ use serde::Deserialize; -#[derive(Deserialize)] +#[derive(Deserialize, Debug)] pub struct NewFeedSchema { pub title: String, pub url: String, diff --git a/src/json_serialization/new_user.rs b/src/json_serialization/new_user.rs index f80e3b0..c9f158b 100755 --- a/src/json_serialization/new_user.rs +++ b/src/json_serialization/new_user.rs @@ -1,6 +1,6 @@ use serde::Deserialize; -#[derive(Deserialize)] +#[derive(Deserialize, Debug)] pub struct NewUserSchema { pub name: String, pub email: String, diff --git a/src/json_serialization/read_feed_item.rs b/src/json_serialization/read_feed_item.rs index 5d3f37d..a4e0eed 100644 --- a/src/json_serialization/read_feed_item.rs +++ b/src/json_serialization/read_feed_item.rs @@ -1,6 +1,6 @@ use serde_derive::Deserialize; -#[derive(Deserialize)] +#[derive(Deserialize, Debug)] pub struct ReadItem { pub id: i32, } diff --git a/src/json_serialization/url.rs b/src/json_serialization/url.rs index 46a0e95..25366d1 100644 --- a/src/json_serialization/url.rs +++ b/src/json_serialization/url.rs @@ -1,6 +1,6 @@ use serde::Deserialize; -#[derive(Deserialize)] +#[derive(Deserialize, Debug)] pub struct UrlJson { pub url: String, } diff --git a/src/json_serialization/user.rs b/src/json_serialization/user.rs index 4ea75b4..32ebb5f 100644 --- a/src/json_serialization/user.rs +++ b/src/json_serialization/user.rs @@ -1,6 +1,6 @@ use serde_derive::Deserialize; -#[derive(Deserialize)] +#[derive(Deserialize, Debug)] pub struct JsonUser { pub user_id: i32, } diff --git a/src/lib.rs b/src/lib.rs index cf8f2c0..024003f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,4 +9,5 @@ pub mod models; pub mod reader; pub mod schema; pub mod startup; +pub mod telemetry; pub mod views; diff --git a/src/main.rs b/src/main.rs index 8e27b94..e43c065 100755 --- a/src/main.rs +++ b/src/main.rs @@ -4,12 +4,18 @@ use diesel::{ r2d2::{ConnectionManager, Pool}, PgConnection, }; -use rss_reader::{configuration::get_configuration, database::get_connection_pool, startup::run}; +use rss_reader::{ + configuration::get_configuration, + database::get_connection_pool, + startup::run, + telemetry::{get_subscriber, init_subscriber}, +}; use secrecy::ExposeSecret; #[actix_rt::main] async fn main() -> std::io::Result<()> { - env_logger::init(); + let subscriber = get_subscriber("zero2prod".into(), "info".into(), std::io::stdout); + init_subscriber(subscriber); let configuration = get_configuration().expect("Failed to read configuration."); diff --git a/src/models/user/new_user.rs b/src/models/user/new_user.rs index 74fc9b2..9629a87 100755 --- a/src/models/user/new_user.rs +++ b/src/models/user/new_user.rs @@ -2,6 +2,7 @@ extern crate bcrypt; use bcrypt::{hash, DEFAULT_COST}; use diesel::Insertable; +use secrecy::{ExposeSecret, Secret}; use uuid::Uuid; use crate::schema::users; @@ -16,8 +17,9 @@ pub struct NewUser { } impl NewUser { - pub fn new(username: String, email: String, password: String) -> NewUser { - let hashed_password: String = hash(password.as_str(), DEFAULT_COST).unwrap(); + pub fn new(username: String, email: String, password: Secret) -> NewUser { + let hashed_password: String = + hash(password.expose_secret().as_str(), DEFAULT_COST).unwrap(); let uuid = Uuid::new_v4(); NewUser { username, diff --git a/src/reader/add.rs b/src/reader/add.rs index fdc4c43..c5a2048 100644 --- a/src/reader/add.rs +++ b/src/reader/add.rs @@ -10,6 +10,7 @@ use crate::{ use super::feeds; +#[tracing::instrument(name = "Add new feed", skip(pool))] pub async fn add( new_feed: web::Json, pool: web::Data>>, @@ -24,13 +25,11 @@ pub async fn add( let result = feeds::get_feed(&url).await; match result { Ok(channel) => { - log::info!("valid channel"); if channel.items.is_empty() { return HttpResponse::ServiceUnavailable().await.unwrap(); } } - Err(e) => { - log::error!("{:?}", e); + Err(_) => { return HttpResponse::NotFound().await.unwrap(); } } @@ -43,9 +42,6 @@ pub async fn add( match insert_result { Ok(_) => HttpResponse::Created().await.unwrap(), - Err(e) => { - log::error!("{e}"); - HttpResponse::Conflict().await.unwrap() - } + Err(_) => HttpResponse::Conflict().await.unwrap(), } } diff --git a/src/reader/feeds.rs b/src/reader/feeds.rs index 5157e51..8ad4c9c 100755 --- a/src/reader/feeds.rs +++ b/src/reader/feeds.rs @@ -2,9 +2,9 @@ use std::error::Error; use rss::Channel; +#[tracing::instrument(name = "Get Channel Feed")] pub async fn get_feed(feed: &str) -> Result> { let content = reqwest::get(feed).await?.bytes().await?; let channel = Channel::read_from(&content[..])?; - log::debug!("{:?}", channel); Ok(channel) } diff --git a/src/reader/get.rs b/src/reader/get.rs index c8e5b7a..ba4b313 100755 --- a/src/reader/get.rs +++ b/src/reader/get.rs @@ -10,11 +10,12 @@ use crate::{ }; use actix_web::{web, HttpRequest, Responder}; use chrono::Local; -use diesel::prelude::*; use diesel::r2d2::{ConnectionManager, Pool}; +use diesel::{prelude::*, r2d2}; use super::structs::article::Article; +#[tracing::instrument(name = "Get feeds", skip(pool))] pub async fn get( path: web::Path, req: HttpRequest, @@ -22,7 +23,6 @@ pub async fn get( ) -> impl Responder { let request = req.clone(); let req_user_id = path.user_id; - log::info!("Received user_id: {}", req_user_id); // Clone the Arc containing the connection pool let pool_arc = pool.get_ref().clone(); // Acquire a connection from the pool @@ -35,42 +35,7 @@ pub async fn get( let mut feed_aggregates: Vec = Vec::new(); for feed in feeds { - let existing_item: Vec = feed_item::table - .filter(feed_id.eq(feed.id)) - .filter(read.eq(false)) - .order(id.asc()) - .load(&mut connection) - .unwrap(); - - log::info!( - "Load {} feed items for feed: {}", - existing_item.len(), - feed.url - ); - - let article_list: Vec
= existing_item - .into_iter() - .map(|feed_item: FeedItem| { - let time: String = match feed_item.created_ts { - Some(r) => r.to_string(), - None => Local::now().naive_local().to_string(), - }; - Article { - title: feed_item.title, - content: feed_item.content, - url: feed_item.url, - timestamp: time, - id: feed_item.id, - } - }) - .collect(); - - log::info!("article list with {} items generated.", article_list.len()); - - feed_aggregates.push(FeedAggregate { - title: feed.title, - items: article_list, - }) + feed_aggregates.push(get_feed_aggregate(feed, &mut connection)) } let articles: Articles = Articles { @@ -79,3 +44,38 @@ pub async fn get( articles.respond_to(&request) } + +#[tracing::instrument(name = "Get feed aggregate", skip(connection))] +pub fn get_feed_aggregate( + feed: Feed, + connection: &mut r2d2::PooledConnection>, +) -> FeedAggregate { + let existing_item: Vec = feed_item::table + .filter(feed_id.eq(feed.id)) + .filter(read.eq(false)) + .order(id.asc()) + .load(connection) + .unwrap(); + + let article_list: Vec
= existing_item + .into_iter() + .map(|feed_item: FeedItem| { + let time: String = match feed_item.created_ts { + Some(r) => r.to_string(), + None => Local::now().naive_local().to_string(), + }; + Article { + title: feed_item.title, + content: feed_item.content, + url: feed_item.url, + timestamp: time, + id: feed_item.id, + } + }) + .collect(); + + FeedAggregate { + title: feed.title, + items: article_list, + } +} diff --git a/src/reader/mark_read.rs b/src/reader/mark_read.rs index 9c4bd36..fe1c796 100644 --- a/src/reader/mark_read.rs +++ b/src/reader/mark_read.rs @@ -3,36 +3,34 @@ use crate::{ json_serialization::read_feed_item::ReadItem, models::feed_item::rss_feed_item::FeedItem, schema::feed_item, }; -use actix_web::{web, HttpRequest, HttpResponse, Responder}; +use actix_web::{web, HttpRequest, HttpResponse}; use diesel::r2d2::{ConnectionManager, Pool}; use diesel::{ExpressionMethods, QueryDsl}; use diesel::{PgConnection, RunQueryDsl}; +#[tracing::instrument(name = "Mark as read", skip(pool))] pub async fn mark_read( _req: HttpRequest, path: web::Path, pool: web::Data>>, -) -> impl Responder { +) -> HttpResponse { let pool_arc = pool.get_ref().clone(); let mut connection = pool_arc.get().expect("Failed to get database connection"); - log::info!("Id: {}", path.id); let feed_items: Vec = feed_item::table .filter(id.eq(path.id)) .load::(&mut connection) .unwrap(); if feed_items.len() != 1 { - return HttpResponse::NotFound(); + return HttpResponse::NotFound().await.unwrap(); } let feed_item: &FeedItem = feed_items.first().unwrap(); - let result: Result = diesel::update(feed_item) + let _result: Result = diesel::update(feed_item) .set(read.eq(true)) .execute(&mut connection); - log::info!("Mark as read: {:?}", result); - - HttpResponse::Ok() + HttpResponse::Ok().await.unwrap() } diff --git a/src/reader/read.rs b/src/reader/read.rs index f5982ab..50f41a0 100644 --- a/src/reader/read.rs +++ b/src/reader/read.rs @@ -4,15 +4,13 @@ use crate::json_serialization::{readable::Readable, url::UrlJson}; use super::scraper::content::do_throttled_request; +#[tracing::instrument(name = "Read Feed")] pub async fn read(_req: HttpRequest, data: web::Json) -> impl Responder { let result = do_throttled_request(&data.url); let content = match result.await { Ok(cont) => cont, - Err(e) => { - log::error!("Could not scrap url {}", data.url); - e.to_string() - } + Err(e) => e.to_string(), }; Readable { content } diff --git a/src/reader/sync.rs b/src/reader/sync.rs index d751401..b0e9639 100644 --- a/src/reader/sync.rs +++ b/src/reader/sync.rs @@ -8,7 +8,7 @@ use crate::schema::{ feed::{self, user_id}, feed_item, }; -use actix_web::{web, HttpRequest, HttpResponse, Responder}; +use actix_web::{web, HttpRequest, HttpResponse}; use chrono::{DateTime, Local, NaiveDateTime}; use dateparser::parse; use diesel::prelude::*; @@ -16,12 +16,12 @@ use diesel::r2d2::{ConnectionManager, Pool}; use rss::Item; use scraper::{Html, Selector}; +#[tracing::instrument(name = "Get Date")] fn get_date(date_str: &str) -> Result { // let format_string = "%a, %d %b %Y %H:%M:%S %z"; let format_string = "%Y-%m-%dT%H:%M:%S%Z"; let result = parse(date_str).unwrap(); - log::info!("Date: {:?}", result); match NaiveDateTime::parse_from_str(&result.to_string(), format_string) { Ok(r) => Ok(r), @@ -38,9 +38,9 @@ fn get_date(date_str: &str) -> Result { } } +#[tracing::instrument(name = "Create Feed Item", skip(connection))] fn create_feed_item(item: Item, feed: &Feed, connection: &mut PgConnection) { let item_title = item.title.clone().unwrap(); - log::info!("Create feed item: {}", item_title); let base_content: &str = match item.content() { Some(c) => c, @@ -74,15 +74,11 @@ fn create_feed_item(item: Item, feed: &Feed, connection: &mut PgConnection) { .unwrap(); if existing_item.is_empty() { - log::info!("{:?}", item.pub_date()); let mut time: NaiveDateTime = Local::now().naive_local(); if item.pub_date().is_some() { time = match get_date(item.pub_date().unwrap()) { Ok(date) => date, - Err(err) => { - log::error!("could not unwrap pub date: {}", err); - time - } + Err(_err) => time, }; } let new_feed_item = NewFeedItem::new( @@ -92,21 +88,18 @@ fn create_feed_item(item: Item, feed: &Feed, connection: &mut PgConnection) { item.link.unwrap(), Some(time), ); - let insert_result = diesel::insert_into(feed_item::table) + let _insert_result = diesel::insert_into(feed_item::table) .values(&new_feed_item) .execute(connection); - - log::info!("Insert Result: {:?}", insert_result); - } else { - log::info!("Item {} already exists.", item_title); } } +#[tracing::instrument(name = "sync", skip(pool))] pub async fn sync( _req: HttpRequest, data: web::Json, pool: web::Data>>, -) -> impl Responder { +) -> HttpResponse { let pool_arc = pool.get_ref().clone(); let mut connection = pool_arc.get().expect("Failed to get database connection"); @@ -117,22 +110,18 @@ pub async fn sync( .load::(&mut connection) .unwrap(); - log::info!("Found {} feeds to sync.", feeds.len()); - for feed in feeds { - log::info!("Try to get url: {}", feed.url); let result = feeds::get_feed(&feed.url).await; match result { Ok(channel) => { for item in channel.into_items() { - log::info!("{:?}", item); create_feed_item(item, &feed, &mut connection); } } - Err(e) => log::error!("Could not get channel {}. Error: {}", feed.url, e), + Err(_e) => return HttpResponse::InternalServerError().await.unwrap(), } } - HttpResponse::Ok() + HttpResponse::Ok().await.unwrap() } diff --git a/src/startup.rs b/src/startup.rs index f3d7943..963fe0d 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -10,25 +10,23 @@ use futures::future::{ok, Either}; use crate::auth; use crate::views; +#[tracing::instrument(name = "Run application", skip(connection, listener))] pub fn run( listener: TcpListener, connection: Pool>, ) -> Result { let wrapper = web::Data::new(connection); let server = HttpServer::new(move || { - let app = App::new() + App::new() .wrap_fn(|req, srv| { let mut passed: bool; - let request_url: String = String::from(req.uri().path()); - log::info!("Request Url: {}", request_url); if req.path().contains("/article/") { match auth::process_token(&req) { Ok(_token) => passed = true, Err(_message) => passed = false, } } else { - log::warn!("No auth check done."); passed = true; } @@ -36,8 +34,6 @@ pub fn run( passed = true; } - log::info!("passed: {:?}", passed); - let end_result = match passed { true => Either::Left(srv.call(req)), false => Either::Right(ok(req.into_response( @@ -47,13 +43,11 @@ pub fn run( async move { let result = end_result.await?; - log::info!("{} -> {}", request_url, &result.status()); Ok(result) } }) .app_data(wrapper.clone()) - .configure(views::views_factory); - app + .configure(views::views_factory) }) .listen(listener)? .run(); diff --git a/src/telemetry.rs b/src/telemetry.rs new file mode 100644 index 0000000..c325922 --- /dev/null +++ b/src/telemetry.rs @@ -0,0 +1,27 @@ +use tracing::{dispatcher::set_global_default, Subscriber}; +use tracing_bunyan_formatter::{BunyanFormattingLayer, JsonStorageLayer}; +use tracing_log::LogTracer; +use tracing_subscriber::{fmt::MakeWriter, layer::SubscriberExt, EnvFilter, Registry}; + +pub fn get_subscriber( + name: String, + env_filter: String, + sink: Sink, +) -> impl Subscriber + Send + Sync +where + Sink: for<'a> MakeWriter<'a> + Send + Sync + 'static, +{ + let env_filter = + EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(env_filter)); + let formatting_layer = BunyanFormattingLayer::new(name, sink); + + Registry::default() + .with(env_filter) + .with(JsonStorageLayer) + .with(formatting_layer) +} + +pub fn init_subscriber(subscriber: impl Subscriber + Send + Sync) { + LogTracer::init().expect("Failed to set logger."); + set_global_default(subscriber.into()).expect("Failed to set subscriber."); +} diff --git a/src/views/auth/login.rs b/src/views/auth/login.rs index 70a9d8c..b891948 100755 --- a/src/views/auth/login.rs +++ b/src/views/auth/login.rs @@ -25,10 +25,6 @@ pub async fn login( if users.is_empty() { return HttpResponse::NotFound().await.unwrap(); } else if users.len() > 1 { - log::error!( - "multiple user have the usernam: {}", - credentials.username.clone() - ); return HttpResponse::Conflict().await.unwrap(); } @@ -36,7 +32,6 @@ pub async fn login( match user.clone().verify(password) { true => { - log::info!("verified password successfully for user {}", user.id); let token: String = JwtToken::encode(user.clone().id); HttpResponse::Ok() .insert_header(("token", token)) diff --git a/src/views/users/create.rs b/src/views/users/create.rs index e99fb23..ace2b53 100755 --- a/src/views/users/create.rs +++ b/src/views/users/create.rs @@ -7,7 +7,9 @@ use diesel::{ prelude::*, r2d2::{ConnectionManager, Pool}, }; +use secrecy::Secret; +#[tracing::instrument(name = "Create new User", skip(pool))] pub async fn create( new_user: web::Json, pool: web::Data>>, @@ -17,7 +19,7 @@ pub async fn create( let name: String = new_user.name.clone(); let email: String = new_user.email.clone(); - let new_password: String = new_user.password.clone(); + let new_password: Secret = Secret::new(new_user.password.clone()); let new_user = NewUser::new(name, email, new_password); diff --git a/vue/src/assets/main.css b/vue/src/assets/main.css index 863e9bc..2d412b6 100644 --- a/vue/src/assets/main.css +++ b/vue/src/assets/main.css @@ -45,18 +45,33 @@ a, font-family: Georgia, 'Times New Roman', Times, serif; font-size: 20px; padding: 1em; + display: flex; + flex-direction: column; + align-items: left; + text-align: left; } .feed-content p { padding: 1em; } -.feed-content h3 { +.feed-content h2, +h3, +h4, +h5, +h6 { padding: 1em; font-size: 21px; font-weight: bold; } + +.feed-content img { + max-width: 100%; + margin-bottom: 10px; + /* Adjust spacing between image and text */ +} + h3 { font-size: 14px; } diff --git a/vue/src/components/RssFeeds.vue b/vue/src/components/RssFeeds.vue index 43443c1..4a5efac 100644 --- a/vue/src/components/RssFeeds.vue +++ b/vue/src/components/RssFeeds.vue @@ -67,9 +67,10 @@ const fetchData = async () => { 'user-token': localStorage.getItem("user-token") } }); - response.data.feeds.forEach(feed => { - feeds.value.push(...feed.items); - }); + const sortedItems = response.data.feeds.flatMap(feed => feed.items) + .sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp)); + + feeds.value.push(...sortedItems); await nextTick(); setupIntersectionObserver(); } catch (error) {