ferret/frontend/src/main.rs

146 lines
3.6 KiB
Rust

use axum::Form;
use axum::{
body::Bytes,
extract::State,
http::{header, HeaderMap, StatusCode},
response::{Html, IntoResponse},
routing::{get, post},
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;
use url::Url;
#[derive(Content)]
struct HomePage {
title: String,
description: String,
source: String,
version: String,
}
#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();
let app = Router::new()
.route("/", get(root))
.route("/search", post(search))
.route("/style.css", get(stylesheet));
let addr = SocketAddr::from(([127, 0, 0, 1], 3001));
tracing::debug!("listening on {}", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
async fn stylesheet() -> impl IntoResponse {
let mut headers = HeaderMap::new();
headers.insert(header::CONTENT_TYPE, "text/css".parse().unwrap());
let mut file = File::open("static/style.css").unwrap();
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
(headers, contents)
}
async fn root() -> Html<String> {
let mut source = File::open("static/home.html").unwrap();
let mut contents = String::new();
source.read_to_string(&mut contents).unwrap();
let tpl = Template::new(contents).unwrap();
let rendered = tpl.render(&HomePage {
title: "Ferret".to_string(),
description: "A small independent search engine".to_string(),
source: "https://git.lavender.software/erin/ferret".to_string(),
version: "v0.2.0".to_string(),
});
Html(rendered)
}
#[derive(Deserialize)]
struct SearchForm {
query: String,
}
#[derive(Content)]
struct RenderSearchResult {
url: String,
size: i64,
title: String,
summary: String,
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<RenderSearchResult>,
}
async fn search(Form(search): Form<SearchForm>) -> Html<String> {
let mut source = File::open("static/results.html").unwrap();
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,
});
Html(rendered)
}