use std::sync::Arc;
use std::time::Duration;
use axum::routing::{get, post};
use axum::Router;
use log::info;
use metrics_process::Collector;
use tower_http::classify::ServerErrorsFailureClass;
use tower_http::services::ServeDir;
use tower_http::trace::{DefaultMakeSpan, TraceLayer};
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use axum_prometheus::PrometheusMetricLayer;
use bismuth::middleware::{optional_auth_middleware, web_auth_middleware};
use bismuth::{build_environment, create_db_connection, handlers, AppState, Config};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::registry()
.with(tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| {
"bismuth=debug,webservice=debug,tower_http=info".into()
}))
.with(tracing_subscriber::fmt::layer())
.init();
let config = Arc::new(Config::from_env());
info!("configuration loaded");
let db = Arc::new(create_db_connection(&config.database_url).await?);
info!("database connection established");
if config.auto_migrate {
use migration::MigratorTrait;
info!("running pending migrations (set AUTO_MIGRATE=false to skip)");
migration::Migrator::up(db.as_ref(), None).await?;
}
// Prometheus request metrics + process (RSS/CPU/fd) metrics at /metrics.
let collector = Arc::new(Collector::default());
collector.describe();
let (prometheus_layer, metric_handle) = PrometheusMetricLayer::pair();
let state = AppState {
env: build_environment(),
db: db.clone(),
config: config.clone(),
};
// Pages that render for anyone but know who's signed in: the editor
// (landing page) and a snippet's share page, which offers its owner
// edit/delete. These get the non-redirecting optional-auth layer.
let public = Router::new()
.route("/", get(handlers::editor))
.route("/s/{slug}", get(handlers::show_snippet))
.layer(axum::middleware::from_fn_with_state(db.clone(), optional_auth_middleware));
// Stateless rendering and the embeddable/image routes: no session needed.
let open = Router::new()
.route("/preview", post(handlers::preview))
.route("/export", post(handlers::export))
.route("/s/{slug}/card.svg", get(handlers::snippet_svg))
.route("/s/{slug}/card.png", get(handlers::snippet_png))
.route("/e/{slug}", get(handlers::embed));
// Everything requiring a login. The web-auth layer 302s anonymous
// visitors to /login.
let protected = Router::new()
.route("/profile", get(handlers::show_profile))
.route("/invites", get(handlers::show_invites).post(handlers::handle_create_invite))
.route("/snippets", get(handlers::list_snippets).post(handlers::create_snippet))
.route("/snippets/{slug}", post(handlers::update_snippet))
.route("/snippets/{slug}/edit", get(handlers::edit_snippet))
.route("/snippets/{slug}/delete", post(handlers::delete_snippet))
.layer(axum::middleware::from_fn_with_state(db.clone(), web_auth_middleware));
let app = Router::new()
.route("/login", get(handlers::show_login))
.route("/register", get(handlers::show_register))
.route("/auth/login", post(handlers::handle_login))
.route("/auth/register", post(handlers::handle_register))
.route("/auth/logout", post(handlers::handle_logout))
.route(
"/metrics",
get({
let collector = collector.clone();
let handle = metric_handle.clone();
move || {
let collector = collector.clone();
let handle = handle.clone();
async move {
collector.collect();
handle.render()
}
}
}),
)
.nest_service("/assets", ServeDir::new("assets/f"))
.merge(public)
.merge(open)
.merge(protected)
.with_state(state)
.layer(
TraceLayer::new_for_http()
.make_span_with(DefaultMakeSpan::default().include_headers(false))
.on_failure(
|error: ServerErrorsFailureClass, _latency: Duration, _span: &tracing::Span| {
tracing::error!("request failed: {error}");
},
),
)
.layer(prometheus_layer);
let listener = tokio::net::TcpListener::bind(&config.bind_address).await?;
info!("server listening on {}", config.bind_address);
axum::serve(listener, app).await?;
Ok(())
}
neiam
/bismuth
MOTHER-FUCKING-BISMUTH
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
4.6 KiB · text
History
d76cd01