diff --git a/Cargo.lock b/Cargo.lock index 0be4afc..2382d2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1407,6 +1407,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -2477,9 +2487,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.28.0" +version = "1.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" dependencies = [ "autocfg", "bytes", @@ -2570,7 +2580,13 @@ dependencies = [ "http", "http-body", "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", "pin-project-lite", + "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -2584,9 +2600,9 @@ checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" [[package]] name = "tower-livereload" -version = "0.7.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8b316f4712049eb6c939efd3a7e1f7fefd3cbebd00794e82e7774855c8858ea" +checksum = "7e90e6da0427c5e111e03c764d49c4e970f5a9f6569fe408e5a1cbe257f48388" dependencies = [ "bytes", "http", @@ -2688,6 +2704,15 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.13" diff --git a/Cargo.toml b/Cargo.toml index 88e0654..7eda352 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,8 +37,8 @@ thiserror = "1" tokio = { version = "1", features = ["full"] } tokio-stream = { version = "0.1", features = ["sync"] } tower = "0.4" -tower-livereload = "0.7" -tower-http = { version = "0.4", features = ["trace"] } +tower-livereload = "0.8" +tower-http = { version = "0.4", features = ["trace", "fs"] } tracing = "0.1" tracing-appender = "0.2" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/src/handlers/home.rs b/src/handlers/home.rs index d667be2..5495077 100644 --- a/src/handlers/home.rs +++ b/src/handlers/home.rs @@ -10,7 +10,7 @@ use crate::partials::layout::Layout; pub async fn get(State(pool): State, layout: Layout) -> Result { let entries = get_entries(&pool, GetEntriesOptions::default()).await?; Ok(layout.render(html! { - ul { + ul class="entries" { @for entry in entries { @let title = entry.title.unwrap_or_else(|| "Untitled".to_string()); @let url = format!("/entry/{}", entry.id); diff --git a/src/jobs/crawl.rs b/src/jobs/crawl.rs index 1896a8b..6a73b95 100644 --- a/src/jobs/crawl.rs +++ b/src/jobs/crawl.rs @@ -15,7 +15,8 @@ pub async fn crawl(pool: &PgPool) -> anyhow::Result<()> { let client = Client::new(); let feeds = get_feeds(pool).await?; for feed in feeds { - let _feed_span = info_span!("feed", id = feed.id, url = feed.url.as_str()); + let feed_span = info_span!("feed", id = feed.id, url = feed.url.as_str()); + let _feed_span_guard = feed_span.enter(); info!("Fetching feed"); // TODO: handle these results let bytes = client.get(feed.url).send().await?.bytes().await?; @@ -23,7 +24,8 @@ pub async fn crawl(pool: &PgPool) -> anyhow::Result<()> { let parsed_feed = parser::parse(&bytes[..])?; let mut payload = Vec::with_capacity(parsed_feed.entries.len()); for entry in parsed_feed.entries { - let _entry_span = info_span!("entry", id = entry.id, title = entry.title.clone().map(|t| t.content)); + let entry_span = info_span!("entry", id = entry.id, title = entry.title.clone().map(|t| t.content)); + let _entry_span_guard = entry_span.enter(); if let Some(link) = entry.links.get(0) { // if no scraped or feed date is available, fallback to the current time let published_at = entry.published.unwrap_or_else(Utc::now).naive_utc(); @@ -51,7 +53,7 @@ pub async fn crawl(pool: &PgPool) -> anyhow::Result<()> { } } let entries = upsert_entries(pool, payload).await?; - info!("Created {} entries for feed {}", entries.len(), feed.id); + info!("Created {} entries", entries.len()); } Ok(()) } diff --git a/src/main.rs b/src/main.rs index 5c47156..eaade8e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use std::path::Path; +use std::{path::Path, net::SocketAddr}; use anyhow::Result; use axum::{ @@ -11,7 +11,7 @@ use notify::Watcher; use sqlx::postgres::PgPoolOptions; use tokio::sync::watch::channel; use tower::ServiceBuilder; -use tower_http::trace::TraceLayer; +use tower_http::{trace::TraceLayer, services::ServeDir}; use tower_livereload::LiveReloadLayer; use tracing::debug; @@ -20,6 +20,14 @@ use lib::handlers; use lib::log::init_tracing; use lib::state::AppState; +async fn serve(app: Router, addr: SocketAddr) -> Result<()> { + debug!("listening on {}", addr); + axum::Server::bind(&addr) + .serve(app.into_make_service()) + .await?; + Ok(()) +} + #[tokio::main] async fn main() -> Result<()> { dotenv().ok(); @@ -49,6 +57,7 @@ async fn main() -> Result<()> { .route("/entry/:id", get(handlers::entry::get)) .route("/log", get(handlers::log::get)) .route("/log/stream", get(handlers::log::stream)) + .nest_service("/static", ServeDir::new("static")) .with_state(AppState { pool, config, @@ -56,22 +65,20 @@ async fn main() -> Result<()> { }) .layer(ServiceBuilder::new().layer(TraceLayer::new_for_http())); - #[cfg(debug_assertions)] - { + if cfg!(debug_assertions) { + debug!("starting livereload"); let livereload = LiveReloadLayer::new(); let reloader = livereload.reloader(); let mut watcher = notify::recommended_watcher(move |_| reloader.reload())?; watcher.watch( - Path::new("target/debug/crawlnicle"), + Path::new("static"), notify::RecursiveMode::Recursive, )?; app = app.layer(livereload); + serve(app, addr).await?; + } else { + serve(app, addr).await?; } - debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) - .await?; - Ok(()) } diff --git a/src/partials/header.rs b/src/partials/header.rs index 778dfae..5c0f4c4 100644 --- a/src/partials/header.rs +++ b/src/partials/header.rs @@ -2,7 +2,7 @@ use maud::{html, Markup}; pub fn header(title: &str) -> Markup { html! { - header { + header class="header" { nav { h1 { a href="/" data-turbo-frame="main" { (title) } } ul { diff --git a/src/partials/layout.rs b/src/partials/layout.rs index 2789d92..33e3228 100644 --- a/src/partials/layout.rs +++ b/src/partials/layout.rs @@ -42,6 +42,7 @@ impl Layout { script type="module" { r#"import * as Turbo from 'https://cdn.skypack.dev/@hotwired/turbo';"# } + link rel="stylesheet" href="/static/styles.css"; } body { (header(&self.title)) diff --git a/static/styles.css b/static/styles.css new file mode 100644 index 0000000..d7b3226 --- /dev/null +++ b/static/styles.css @@ -0,0 +1,39 @@ +/* Header */ + +header.header nav { + display: flex; + flex-direction: row; + align-items: baseline; +} + +header.header nav h1 { + margin: 0; +} + +header.header nav a { + text-decoration: none; +} + +header.header nav ul { + display: flex; + flex-direction: row; + list-style: none; + margin: 0; + padding: 0; +} + +header.header nav ul li { + margin-left: 16px; +} + +/* Home */ + +ul.entries { + list-style: none; + margin: 24px 8px; + padding: 0; +} + +ul.entries li { + margin-bottom: 8px; +}