2.6 KB
raw
use axum::{
extract::State,
http::{header, HeaderMap, StatusCode},
response::{IntoResponse, Response},
routing::get,
Router,
};
use crate::AppState;
pub fn router() -> Router<AppState> {
Router::new()
.route("/favicon.ico", get(favicon))
.route("/robots.txt", get(robots))
.route("/sitemap.xml", get(sitemap))
}
/// The Paper Ledger mark: a rising figures line over the accountant's double
/// underline, ink-on-paper. Matches the topbar brand in `base.html`.
async fn favicon() -> Response {
let mut h = HeaderMap::new();
h.insert(header::CONTENT_TYPE, "image/svg+xml".parse().unwrap());
let svg = r##"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<rect width="64" height="64" rx="14" fill="#211f1a"/>
<polyline points="14,38 26,28 37,33 52,16" fill="none" stroke="#f0ece1" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="52" cy="16" r="4" fill="#f0ece1"/>
<line x1="14" y1="49" x2="50" y2="49" stroke="#f0ece1" stroke-width="4.2" stroke-linecap="round"/>
<line x1="14" y1="56" x2="50" y2="56" stroke="#f0ece1" stroke-width="4.2" stroke-linecap="round"/>
</svg>"##;
(StatusCode::OK, h, svg).into_response()
}
async fn robots() -> Response {
let mut h = HeaderMap::new();
h.insert(header::CONTENT_TYPE, "text/plain".parse().unwrap());
(StatusCode::OK, h, "User-agent: *\nAllow: /\n").into_response()
}
async fn sitemap(State(state): State<AppState>) -> Response {
let mut h = HeaderMap::new();
h.insert(header::CONTENT_TYPE, "application/xml".parse().unwrap());
let base = if state.config.base_url.is_empty() {
"/".to_string()
} else {
format!("{}/", state.config.base_url.trim_end_matches('/'))
};
let now = chrono::Utc::now().format("%Y-%m-%d");
// Static pages plus one entry per known symbol.
let tickers: Vec<String> = sqlx::query_scalar("SELECT ticker FROM symbols ORDER BY ticker")
.fetch_all(&state.pool)
.await
.unwrap_or_default();
let mut urls = String::new();
for page in ["", "search"] {
urls.push_str(&format!(
" <url><loc>{base}{page}</loc><lastmod>{now}</lastmod></url>\n"
));
}
for t in tickers {
let enc = urlencoding::encode(&t);
urls.push_str(&format!(
" <url><loc>{base}s/{enc}</loc><lastmod>{now}</lastmod></url>\n"
));
}
let body = format!(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n{urls}</urlset>\n"
);
(StatusCode::OK, h, body).into_response()
}