summaryrefslogtreecommitdiff
path: root/src/web/mod.rs
blob: 530a3f95e6a960a4bdf7506c398a87aa4fca9b5a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
use std::net::{IpAddr, SocketAddr, TcpListener};
use std::time::Duration;

use axum::routing::get;
use axum::{Extension, Router};
use moka::future::Cache;
use tokio::task::JoinHandle;
use tower_cookies::CookieManagerLayer;
use tracing::{error, info};

use crate::config::Config;
use crate::database::Database;
use crate::Result;

mod api;
mod extract;
mod file;
mod http;
mod pages;

pub struct WebServer {
    config: Config,
    database: Database,
    addr: SocketAddr,
}

impl WebServer {
    pub async fn new(config: Config, database: Database) -> Result<Self> {
        let addr = format!("[::]:{}", config.web_port).parse::<SocketAddr>()?;
        Ok(Self {
            config,
            database,
            addr,
        })
    }

    pub async fn run(&self) -> Result<JoinHandle<()>> {
        let config = self.config.clone();
        let database = self.database.clone();
        let listener = TcpListener::bind(self.addr)?;

        info!(
            "Listening for HTTP traffic on [::]:{}",
            self.config.web_port
        );

        let app = Self::router(config, database);
        let server = axum::Server::from_tcp(listener)?;

        let web_handle = tokio::spawn(async move {
            if let Err(err) = server
                .serve(app.into_make_service_with_connect_info::<SocketAddr>())
                .await
            {
                error!("{err}");
            }
        });

        Ok(web_handle)
    }

    fn router(config: Config, database: Database) -> Router {
        let cache: Cache<String, IpAddr> = Cache::builder()
            .time_to_live(Duration::from_secs(60 * 15))
            .max_capacity(config.dns_cache_size)
            .build();

        Router::new()
            .nest("/", pages::router())
            .nest("/api", api::router())
            .layer(Extension(config))
            .layer(Extension(cache))
            .layer(Extension(database))
            .layer(CookieManagerLayer::new())
            .route("/js/*path", get(file::js))
            .route("/css/*path", get(file::css))
            .route("/fonts/*path", get(file::fonts))
            .route("/image/*path", get(file::image))
            .route("/favicon.ico", get(file::favicon))
            .route("/robots.txt", get(file::robots))
    }
}