Single-binary self-hosted market watcher for stocks, ETFs, indexes, and futures: live charts, key stats, fundamentals, SEC filings, and SSE streaming.
axumdockerfinancerustself-hostedsqlitestocksvite
1use axum::{
2 extract::{Request, State},
3 middleware::Next,
4 response::Response,
5};
6use chrono::Local;
7use std::time::Instant;
8
9use crate::AppState;
10
11/// Per-request log line: `time METHOD STATUS latency path`, status ANSI-colored.
12pub async fn log_requests(req: Request, next: Next) -> Response {
13 let method = req.method().clone();
14 let path = req
15 .uri()
16 .path_and_query()
17 .map(|p| p.as_str().to_string())
18 .unwrap_or_else(|| req.uri().path().to_string());
19 let start = Instant::now();
20 let response = next.run(req).await;
21 let elapsed_ms = start.elapsed().as_secs_f64() * 1000.0;
22 let status = response.status().as_u16();
23 let now = Local::now().format("%H:%M:%S");
24 let color = match status {
25 200..=299 => "\x1b[32m",
26 300..=399 => "\x1b[36m",
27 400..=499 => "\x1b[33m",
28 _ => "\x1b[31m",
29 };
30 eprintln!("{now} {method:<5} {color}{status}\x1b[0m {elapsed_ms:>7.2}ms {path}");
31 response
32}
33
34/// Router fallback: render the themed 404 shell for any unmatched path.
35pub async fn not_found(State(state): State<AppState>) -> Response {
36 crate::render::not_found(&state)
37}