feat: self-host fonts, styled block page, wildcard TLS (#16)
* perf: optimize hot path — RwLock, inline filtering, pre-allocated strings - Mutex → RwLock for cache, blocklist, and overrides (concurrent read access) - Make cache.lookup() and overrides.lookup() take &self (read-only) - Eliminate 3 Vec allocations per DnsPacket::write() via inline filtering - Pre-allocate domain strings with capacity 64 in parse path - Add criterion micro-benchmarks (hot_path + throughput) - Add bench README documenting both benchmark suites Measured improvement: ~14% faster parsing, ~9% pipeline throughput, round-trip cached 733ns → 698ns (~2.3M queries/sec). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: simplify benchmark code after review - Remove redundant DnsHeader::new() (already set by DnsPacket::new()) - Remove unused DnsHeader import - Change simulate_cached_pipeline to take &DnsCache (lookup is &self now) - Remove unnecessary mut on cache in cache_lookup_miss bench Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * site: landing page overhaul, blog, benchmarks, numa.rs domain Landing page: - Split features into 3-layer card layout (Block & Protect, Developer Tools, Self-Sovereign DNS) - Add DoH and conditional forwarding to comparison table - Fix performance claim (2.3M → 2.0M qps to match benchmarks) - Add all 3 install methods (brew, cargo, curl) - Add OG tags + canonical URL for numa.rs - Fix code block whitespace rendering - Update roadmap with .onion bridge phase Blog: - Add "Building a DNS Resolver from Scratch in Rust" post - Blog index + template for future posts Other: - CNAME for GitHub Pages (numa.rs) - Benchmark results (bench/results.json) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: self-host fonts, styled block page, wildcard TLS Fonts: - Replace Google Fonts CDN with self-hosted woff2 (73KB, 5 files) - Serve fonts from API server via include_bytes! (dashboard works offline) - Proxy error pages use system fonts (zero external deps when DNS is broken) - Fix Instrument Serif font-weight: use 400 (only available weight) instead of synthetic bold 600/700 Proxy: - Styled "Blocked by Numa" page when blocked domain hits the proxy (was confusing "not a .numa domain" error) - Extract shared error_page() template for 403 + 404 pages (deduplicate ~160 lines of CSS) TLS: - Add wildcard SAN *.numa to cert — unregistered .numa domains get valid HTTPS (styled 404 without cert warning) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit was merged in pull request #16.
This commit is contained in:
48
src/api.rs
48
src/api.rs
@@ -15,6 +15,13 @@ use crate::question::QueryType;
|
||||
use crate::stats::QueryPath;
|
||||
|
||||
const DASHBOARD_HTML: &str = include_str!("../site/dashboard.html");
|
||||
const FONTS_CSS: &str = include_str!("../site/fonts/fonts.css");
|
||||
const FONT_DM_SANS: &[u8] = include_bytes!("../site/fonts/dm-sans-latin.woff2");
|
||||
const FONT_DM_SANS_ITALIC: &[u8] = include_bytes!("../site/fonts/dm-sans-italic-latin.woff2");
|
||||
const FONT_INSTRUMENT: &[u8] = include_bytes!("../site/fonts/instrument-serif-latin.woff2");
|
||||
const FONT_INSTRUMENT_ITALIC: &[u8] =
|
||||
include_bytes!("../site/fonts/instrument-serif-italic-latin.woff2");
|
||||
const FONT_JETBRAINS: &[u8] = include_bytes!("../site/fonts/jetbrains-mono-latin.woff2");
|
||||
|
||||
pub fn router(ctx: Arc<ServerCtx>) -> Router {
|
||||
Router::new()
|
||||
@@ -50,6 +57,27 @@ pub fn router(ctx: Arc<ServerCtx>) -> Router {
|
||||
.route("/services/{name}/routes", post(add_route))
|
||||
.route("/services/{name}/routes", delete(remove_route))
|
||||
.route("/ca.pem", get(serve_ca))
|
||||
.route("/fonts/fonts.css", get(serve_fonts_css))
|
||||
.route(
|
||||
"/fonts/dm-sans-latin.woff2",
|
||||
get(|| async { serve_font(FONT_DM_SANS) }),
|
||||
)
|
||||
.route(
|
||||
"/fonts/dm-sans-italic-latin.woff2",
|
||||
get(|| async { serve_font(FONT_DM_SANS_ITALIC) }),
|
||||
)
|
||||
.route(
|
||||
"/fonts/instrument-serif-latin.woff2",
|
||||
get(|| async { serve_font(FONT_INSTRUMENT) }),
|
||||
)
|
||||
.route(
|
||||
"/fonts/instrument-serif-italic-latin.woff2",
|
||||
get(|| async { serve_font(FONT_INSTRUMENT_ITALIC) }),
|
||||
)
|
||||
.route(
|
||||
"/fonts/jetbrains-mono-latin.woff2",
|
||||
get(|| async { serve_font(FONT_JETBRAINS) }),
|
||||
)
|
||||
.with_state(ctx)
|
||||
}
|
||||
|
||||
@@ -844,6 +872,26 @@ async fn serve_ca(State(ctx): State<Arc<ServerCtx>>) -> Result<impl IntoResponse
|
||||
))
|
||||
}
|
||||
|
||||
async fn serve_fonts_css() -> impl IntoResponse {
|
||||
(
|
||||
[
|
||||
(header::CONTENT_TYPE, "text/css"),
|
||||
(header::CACHE_CONTROL, "public, max-age=31536000"),
|
||||
],
|
||||
FONTS_CSS,
|
||||
)
|
||||
}
|
||||
|
||||
fn serve_font(data: &'static [u8]) -> impl IntoResponse {
|
||||
(
|
||||
[
|
||||
(header::CONTENT_TYPE, "font/woff2"),
|
||||
(header::CACHE_CONTROL, "public, max-age=31536000"),
|
||||
],
|
||||
data,
|
||||
)
|
||||
}
|
||||
|
||||
async fn check_tcp(addr: std::net::SocketAddr) -> bool {
|
||||
tokio::time::timeout(
|
||||
std::time::Duration::from_millis(100),
|
||||
|
||||
Reference in New Issue
Block a user