initial echo project website (rust + axum + askama)
This commit is contained in:
commit
899040a319
11 changed files with 1480 additions and 0 deletions
122
src/main.rs
Normal file
122
src/main.rs
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
//! The echoIRCd project website: a small Axum server that renders a handful of
|
||||
//! compile-time Askama templates. Templates and static assets are baked into the
|
||||
//! binary, so the deployed artifact is fully self-contained.
|
||||
|
||||
use askama::Template;
|
||||
use axum::{
|
||||
http::{header, StatusCode},
|
||||
response::{Html, IntoResponse, Response},
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use std::net::SocketAddr;
|
||||
use tower_http::{compression::CompressionLayer, trace::TraceLayer};
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "index.html")]
|
||||
struct IndexTemplate {
|
||||
active: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "features.html")]
|
||||
struct FeaturesTemplate {
|
||||
active: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "connect.html")]
|
||||
struct ConnectTemplate {
|
||||
active: &'static str,
|
||||
}
|
||||
|
||||
/// Render a template to an HTML response, or a 500 if rendering fails.
|
||||
fn page<T: Template>(t: T) -> Response {
|
||||
match t.render() {
|
||||
Ok(body) => Html(body).into_response(),
|
||||
Err(e) => {
|
||||
tracing::error!("template render error: {e}");
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, "internal error").into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn index() -> Response {
|
||||
page(IndexTemplate { active: "home" })
|
||||
}
|
||||
async fn features() -> Response {
|
||||
page(FeaturesTemplate { active: "features" })
|
||||
}
|
||||
async fn connect() -> Response {
|
||||
page(ConnectTemplate { active: "connect" })
|
||||
}
|
||||
|
||||
async fn style() -> impl IntoResponse {
|
||||
(
|
||||
[(header::CONTENT_TYPE, "text/css; charset=utf-8")],
|
||||
include_str!("../static/style.css"),
|
||||
)
|
||||
}
|
||||
async fn favicon() -> impl IntoResponse {
|
||||
(
|
||||
[(header::CONTENT_TYPE, "image/svg+xml")],
|
||||
include_str!("../static/favicon.svg"),
|
||||
)
|
||||
}
|
||||
async fn health() -> &'static str {
|
||||
"ok"
|
||||
}
|
||||
|
||||
async fn not_found() -> Response {
|
||||
(
|
||||
StatusCode::NOT_FOUND,
|
||||
Html("<h1>404</h1><p>Not found. <a href=\"/\">Back home</a>.</p>"),
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(
|
||||
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||
.unwrap_or_else(|_| "info".into()),
|
||||
)
|
||||
.init();
|
||||
|
||||
let app = Router::new()
|
||||
.route("/", get(index))
|
||||
.route("/features", get(features))
|
||||
.route("/connect", get(connect))
|
||||
.route("/static/style.css", get(style))
|
||||
.route("/favicon.svg", get(favicon))
|
||||
.route("/health", get(health))
|
||||
.fallback(not_found)
|
||||
.layer(CompressionLayer::new())
|
||||
.layer(TraceLayer::new_for_http());
|
||||
|
||||
let addr: SocketAddr = std::env::var("ECHO_WEB_ADDR")
|
||||
.unwrap_or_else(|_| "127.0.0.1:8099".into())
|
||||
.parse()
|
||||
.expect("ECHO_WEB_ADDR must be a valid socket address");
|
||||
|
||||
let listener = tokio::net::TcpListener::bind(addr)
|
||||
.await
|
||||
.unwrap_or_else(|e| panic!("bind {addr}: {e}"));
|
||||
tracing::info!("echo website listening on http://{addr}");
|
||||
|
||||
axum::serve(listener, app)
|
||||
.with_graceful_shutdown(shutdown())
|
||||
.await
|
||||
.expect("server error");
|
||||
}
|
||||
|
||||
/// Resolve on SIGINT (Ctrl-C) or SIGTERM (systemd stop) for a clean shutdown.
|
||||
async fn shutdown() {
|
||||
use tokio::signal::unix::{signal, SignalKind};
|
||||
let mut term = signal(SignalKind::terminate()).expect("install SIGTERM handler");
|
||||
tokio::select! {
|
||||
_ = tokio::signal::ctrl_c() => {}
|
||||
_ = term.recv() => {}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue