aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
blob: c9b80ab3d331e479952ead00766744a7d90a2c06 (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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#![feature(duration_constructors)]
pub mod embed;
pub mod error;
pub mod info;
pub mod state;
pub mod tool;

use anyhow::{anyhow, Context};
use embed::*;
use info::*;
use markup::Render;
use rocket::{
    catch, catchers,
    fairing::AdHoc,
    get,
    http::{ContentType, Header, Status},
    response::{self, Responder},
    routes,
    shield::{self, Shield},
    tokio::{self},
    Request, Response,
};
use state::{AdInfo, Config, Logic};
use std::{env::args, fs::read_to_string, io::Cursor, net::IpAddr, sync::Arc};
use tool::tool_main;

fn main() -> anyhow::Result<()> {
    env_logger::init_from_env("LOG");
    let mut args = args().skip(1);
    let config = args.next().expect("first arg needs to be the config");
    let config = read_to_string(config).context(anyhow!("could not read config"))?;
    let mut config: Config = toml::from_str(config.as_str()).context(anyhow!("config invalid"))?;

    if let Some(action) = args.next() {
        return tool_main(config, &action, args.collect::<Vec<_>>());
    }

    for entry in config
        .ad_dir
        .read_dir()
        .context(anyhow!("cannot read ad directory"))?
    {
        if let Ok(entry) = entry {
            if entry
                .path()
                .file_name()
                .unwrap()
                .to_str()
                .unwrap()
                .ends_with(".toml")
            {
                let path = entry.path();
                let imname = path.file_stem().unwrap().to_str().unwrap();
                let basename = imname.split_once(".").unwrap().0;
                let info: AdInfo = toml::from_str(&read_to_string(entry.path()).unwrap()).unwrap();
                config.ads.insert(
                    basename.to_string(),
                    AdInfo {
                        image: imname.into(),
                        ..info
                    },
                );
            }
        }
    }

    let state = Logic::new(config);

    tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .build()?
        .block_on(inner_main(state));

    Ok(())
}
async fn inner_main(state: Arc<Logic>) {
    let _ = rocket::build()
        .configure(rocket::Config {
            port: state.config.port,
            ..Default::default()
        })
        .attach(Shield::default().disable::<shield::Frame>())
        .attach(AdHoc::on_response("set server header", |_req, res| {
            res.set_header(Header::new("server", "meta adservices"));
            Box::pin(async {})
        }))
        .manage(state)
        .mount("/", routes![r_index, r_embed, r_style, r_image, r_iptest])
        .register("/", catchers![r_catch])
        .launch()
        .await;
}

#[get("/myip")]
fn r_iptest(addr: IpAddr) -> String {
    format!("{addr}")
}

pub struct Template<T>(pub T);
impl<'r, T: Render> Responder<'r, 'static> for Template<T> {
    fn respond_to(self, _req: &'r Request<'_>) -> response::Result<'static> {
        let mut out = String::new();
        self.0.render(&mut out).unwrap();
        Response::build()
            .header(ContentType::HTML)
            .streamed_body(Cursor::new(out))
            .ok()
    }
}

#[catch(default)]
pub fn r_catch<'a>(status: Status, _request: &Request) -> String {
    format!("{status}; This ad is likely misconfigured. Server could also be broken...")
}