Compare commits

...

2 Commits

Author SHA1 Message Date
~erin df116b1ef6
Show last updated time for search results 2023-07-26 09:11:07 -04:00
~erin dc66951808
Make search request 2023-07-26 09:06:41 -04:00
7 changed files with 68 additions and 21 deletions

11
Cargo.lock generated
View File

@ -385,6 +385,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "chrono-humanize"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "799627e6b4d27827a814e837b9d8a504832086081806d45b1afa34dc982b023b"
dependencies = [
"chrono",
]
[[package]]
name = "config"
version = "0.13.3"
@ -753,9 +762,11 @@ version = "0.1.0"
dependencies = [
"axum",
"chrono",
"chrono-humanize",
"config",
"dirs",
"ramhorns",
"reqwest",
"serde",
"tokio",
"tracing",

View File

@ -20,6 +20,7 @@ scraper = "0.17.1"
axum = { version = "0.6.19", features = [ "http2", "tracing" ] }
tracing-subscriber = "0.3.17"
tracing = "0.1.37"
reqwest = { version = "0.11", default-features = false, features = [ "rustls-tls", "gzip", "brotli", "deflate", "json" ] }
[profile.release]
strip = true

View File

@ -16,7 +16,7 @@ env_logger.workspace = true
chrono.workspace = true
sqlx.workspace = true
scraper.workspace = true
reqwest.workspace = true
serde = { version = "1.0.175", features = [ "derive" ] }
reqwest = { version = "0.11", default-features = false, features = [ "rustls-tls", "gzip", "brotli", "deflate" ] }
cacache = { version = "11.6.0", default-features = false, features = ["tokio-runtime", "mmap"] }
bincode = "1.3.3"

View File

@ -15,5 +15,7 @@ axum.workspace = true
url.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
reqwest.workspace = true
ramhorns = "0.14.0"
serde = "1.0.175"
chrono-humanize = "0.2.3"

View File

@ -8,8 +8,11 @@ use axum::{
Json, Router,
};
use chrono::{DateTime, NaiveDateTime, Utc};
use chrono_humanize::HumanTime;
use ramhorns::{Content, Template};
use serde::Deserialize;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
use std::net::SocketAddr;
@ -31,7 +34,7 @@ async fn main() {
.route("/search", post(search))
.route("/style.css", get(stylesheet));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let addr = SocketAddr::from(([127, 0, 0, 1], 3001));
tracing::debug!("listening on {}", addr);
axum::Server::bind(&addr)
@ -72,7 +75,7 @@ struct SearchForm {
}
#[derive(Content)]
struct SearchResult {
struct RenderSearchResult {
url: String,
size: i64,
title: String,
@ -80,12 +83,21 @@ struct SearchResult {
last_updated: String,
}
#[derive(Debug, Deserialize)]
struct SearchResult {
url: Url,
size: i64,
title: String,
summary: String,
last_updated: DateTime<Utc>,
}
#[derive(Content)]
struct SearchResults {
title: String,
description: String,
query: String,
results: Vec<SearchResult>,
results: Vec<RenderSearchResult>,
}
async fn search(Form(search): Form<SearchForm>) -> Html<String> {
@ -93,28 +105,41 @@ async fn search(Form(search): Form<SearchForm>) -> Html<String> {
let mut contents = String::new();
source.read_to_string(&mut contents).unwrap();
let mut map = HashMap::new();
map.insert("language", "eng");
map.insert("include", &search.query);
map.insert("option", "Fuzzy");
let client = reqwest::Client::new();
let res = client
.get("http://127.0.0.1:3000/api/search")
.json(&map)
.send()
.await
.unwrap()
.json::<BTreeMap<i64, SearchResult>>()
.await
.unwrap();
tracing::info!("{:#?}", res);
let mut results = Vec::new();
for (i, r) in res.iter().rev() {
results.push(RenderSearchResult {
url: r.url.as_str().to_string(),
size: r.size,
title: r.title.clone(),
summary: r.summary.clone(),
last_updated: HumanTime::from(r.last_updated).to_string(),
});
}
let tpl = Template::new(contents).unwrap();
let rendered = tpl.render(&SearchResults {
title: "Ferret".to_string(),
description: "A small independent search engine".to_string(),
query: search.query,
results: vec![
SearchResult {
url: "https://example.com/".to_string(),
size: 0,
title: "Example Domain".to_string(),
summary: "This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.".to_string(),
last_updated: "one week ago".to_string(),
},
SearchResult {
url: "https://example.com/".to_string(),
size: 0,
title: "Example Domain".to_string(),
summary: "This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.".to_string(),
last_updated: "one week ago".to_string(),
}
],
results,
});
Html(rendered)
}

View File

@ -27,6 +27,7 @@
{{#results}}
<article>
<h2>{{title}}</h2>
<p class="time">Updated {{last_updated}}</p>
<a class="result" href="{{url}}">{{url}}</a>
<p class="result">{{summary}}</a>
</article>

View File

@ -116,6 +116,13 @@ form.results {
border-radius: 2px;
}
.time {
font-size: .8em;
font-style: italic;
padding: 0;
margin: 0;
}
footer {
display: flex;
flex-direction: column;