tetratto/crates/app/src/main.rs

129 lines
3.6 KiB
Rust
Raw Normal View History

mod assets;
mod image;
mod macros;
mod routes;
2025-04-10 18:16:52 -04:00
mod sanitize;
use assets::{init_dirs, write_assets};
2025-05-05 23:12:46 -04:00
use tetratto_core::model::{permissions::FinePermission, uploads::CustomEmoji};
pub use tetratto_core::*;
use axum::{Extension, Router};
use reqwest::Client;
use tera::{Tera, Value};
2025-05-02 22:59:44 -04:00
use tower_http::{
trace::{self, TraceLayer},
catch_panic::CatchPanicLayer,
};
use tower_governor::{GovernorLayer, governor::GovernorConfigBuilder};
use tracing::{Level, info};
2025-05-02 22:59:44 -04:00
use std::{collections::HashMap, env::var, net::SocketAddr, process::exit, sync::Arc, time::Duration};
use tokio::sync::RwLock;
pub(crate) type State = Arc<RwLock<(DataManager, Tera, Client)>>;
fn render_markdown(value: &Value, _: &HashMap<String, Value>) -> tera::Result<Value> {
2025-05-05 23:12:46 -04:00
Ok(
CustomEmoji::replace(&tetratto_shared::markdown::render_markdown(
value.as_str().unwrap(),
))
.into(),
)
}
fn color_escape(value: &Value, _: &HashMap<String, Value>) -> tera::Result<Value> {
Ok(sanitize::color_escape(value.as_str().unwrap()).into())
}
fn check_supporter(value: &Value, _: &HashMap<String, Value>) -> tera::Result<Value> {
Ok(FinePermission::from_bits(value.as_u64().unwrap() as u32)
.unwrap()
.check(FinePermission::SUPPORTER)
.into())
}
#[tokio::main(flavor = "multi_thread")]
async fn main() {
tracing_subscriber::fmt()
.with_target(false)
.compact()
.init();
let config = config::Config::get_config();
// init
init_dirs(&config).await;
let html_path = write_assets(&config).await;
2025-05-02 22:59:44 -04:00
// governor
let governor_config = Arc::new(
GovernorConfigBuilder::default()
.per_millisecond(75)
2025-05-02 22:59:44 -04:00
.burst_size(32)
.finish()
.unwrap(),
);
let governor_limiter = governor_config.limiter().clone();
let governor_interval = Duration::from_secs(60);
std::thread::spawn(move || {
loop {
std::thread::sleep(governor_interval);
tracing::info!("rate limiting storage size: {}", governor_limiter.len());
governor_limiter.retain_recent();
}
});
// ...
let database = DataManager::new(config.clone()).await.unwrap();
database.init().await.unwrap();
2025-04-13 12:15:14 -04:00
let mut tera = match Tera::new(&format!("{html_path}/**/*")) {
Ok(t) => t,
Err(e) => {
println!("{e}");
exit(1);
}
};
tera.register_filter("markdown", render_markdown);
tera.register_filter("color", color_escape);
tera.register_filter("has_supporter", check_supporter);
let client = Client::new();
let app = Router::new()
.merge(routes::routes(&config))
.layer(Extension(Arc::new(RwLock::new((database, tera, client)))))
.layer(axum::extract::DefaultBodyLimit::max(
2025-04-08 22:13:55 -04:00
var("BODY_LIMIT")
.unwrap_or("8388608".to_string())
.parse::<usize>()
.unwrap(),
))
.layer(
TraceLayer::new_for_http()
.make_span_with(trace::DefaultMakeSpan::new().level(Level::INFO))
.on_response(trace::DefaultOnResponse::new().level(Level::INFO)),
2025-05-02 22:59:44 -04:00
)
.layer(CatchPanicLayer::new())
.layer(GovernorLayer {
config: governor_config,
});
let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", config.port))
.await
.unwrap();
info!("🐇 tetratto.");
info!("listening on http://0.0.0.0:{}", config.port);
2025-05-02 22:59:44 -04:00
axum::serve(
listener,
app.into_make_service_with_connect_info::<SocketAddr>(),
)
.await
.unwrap();
}