116 lines
3.8 KiB
Rust
116 lines
3.8 KiB
Rust
use std::{future::ready, net::SocketAddr};
|
|
|
|
use axum::{
|
|
extract::Extension,
|
|
http::StatusCode,
|
|
middleware,
|
|
response::Redirect,
|
|
routing::{any, get, get_service},
|
|
Router,
|
|
};
|
|
use tower_http::{auth::RequireAuthorizationLayer, services::ServeDir, trace::TraceLayer};
|
|
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
|
|
|
mod api;
|
|
mod matrix;
|
|
mod metrics;
|
|
|
|
/// Name of the directory where static sites are stored inside the data directory
|
|
const STATIC_DIR_NAME: &str = "static";
|
|
/// Name of the subdir of STATIC_DIR_NAME where the default (fallback) site is located
|
|
const DEFAULT_STATIC_SITE: &str = "default";
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
// Enable tracing
|
|
tracing_subscriber::registry()
|
|
.with(tracing_subscriber::EnvFilter::new(
|
|
std::env::var("RUST_LOG").unwrap_or_else(|_| "site=debug,tower_http=debug".into()),
|
|
))
|
|
.with(tracing_subscriber::fmt::layer())
|
|
.init();
|
|
|
|
// Get required variables from env vars
|
|
let api_key = std::env::var("API_KEY").expect("No API_KEY was provided.");
|
|
let data_dir = std::env::var("DATA_DIR").expect("No DATA_DIR was provided.");
|
|
let static_dir = format!("{}/{}", data_dir, STATIC_DIR_NAME);
|
|
|
|
std::fs::create_dir_all(&static_dir);
|
|
|
|
// Initialize metrics
|
|
let recorder_handle = metrics::setup_metrics_recorder();
|
|
|
|
let mut app = Router::new()
|
|
// Handle Matrix .well-known files
|
|
.nest("/", matrix::router())
|
|
// Routes under /api path
|
|
.nest(
|
|
"/api",
|
|
api::router().layer(RequireAuthorizationLayer::bearer(&api_key)),
|
|
)
|
|
.route("/metrics", get(move || ready(recorder_handle.render())));
|
|
|
|
// Each static site gets mounted explicitely so that the default site can be used as fallback
|
|
// Each entry is of the form (route, static dir name)
|
|
let sites = [
|
|
("/docs/vieter", "docs-vieter"),
|
|
("/api-docs/vieter", "api-docs-vieter"),
|
|
("/man/vieter", "man-vieter"),
|
|
];
|
|
|
|
for (path, dir) in sites {
|
|
let full_path = format!("{}/{}", static_dir, dir);
|
|
|
|
app = app.nest(
|
|
path,
|
|
get_service(ServeDir::new(full_path)).handle_error(
|
|
|error: std::io::Error| async move {
|
|
(
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
format!("Unhandled internal error: {}", error),
|
|
)
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
// Define some redirects
|
|
let redirects = [
|
|
("/github", "https://github.com/ChewingBever"),
|
|
("/gitea", "https://git.rustybever.be/Chewing_Bever"),
|
|
("/gitlab", "https://gitlab.com/Chewing_Bever"),
|
|
("/codeberg", "https://codeberg.org/Chewing_Bever"),
|
|
("/matrix", "https://matrix.to/#/@jef:rustybever.be"),
|
|
("/aur", "https://aur.archlinux.org/account/Chewing_Bever"),
|
|
];
|
|
|
|
for (path, url) in redirects {
|
|
app = app.route(path, any(|| async { Redirect::permanent(url) }))
|
|
}
|
|
|
|
app = app
|
|
// The fallback option is to serve the actual static files
|
|
.fallback(
|
|
get_service(ServeDir::new(format!(
|
|
"{}/{}",
|
|
static_dir, DEFAULT_STATIC_SITE
|
|
)))
|
|
.handle_error(|error: std::io::Error| async move {
|
|
(
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
format!("Unhandled internal error: {}", error),
|
|
)
|
|
}),
|
|
)
|
|
.layer(middleware::from_fn(metrics::track_metrics))
|
|
.layer(Extension(data_dir))
|
|
.layer(TraceLayer::new_for_http());
|
|
|
|
let addr = SocketAddr::from(([0, 0, 0, 0], 3000));
|
|
tracing::debug!("listening on {}", addr);
|
|
axum::Server::bind(&addr)
|
|
.serve(app.into_make_service())
|
|
.await
|
|
.unwrap();
|
|
}
|