113 lines
3.8 KiB
Rust
113 lines
3.8 KiB
Rust
use axum::Router;
|
|
use axum::routing::{get, post};
|
|
use axum::serve::Listener as AxumListener;
|
|
use minijinja::Environment;
|
|
#[cfg(not(feature = "embed"))]
|
|
use minijinja::path_loader;
|
|
#[cfg(feature = "embed")]
|
|
use static_serve::embed_assets;
|
|
#[cfg(not(feature = "embed"))]
|
|
use tower_http::services::ServeDir;
|
|
|
|
use crate::db::Database;
|
|
use crate::listener::{Listener, ListenerKind};
|
|
use crate::stream::{AbstractSocketAddr, AbstractStreamKind};
|
|
|
|
mod domain;
|
|
mod home;
|
|
mod login;
|
|
mod logout;
|
|
mod redirect;
|
|
use redirect::InternalRedirect;
|
|
mod session;
|
|
use session::{HttpSession, HttpSessionManager, OptionalHttpSession};
|
|
mod user;
|
|
|
|
impl AxumListener for Listener {
|
|
type Io = AbstractStreamKind;
|
|
type Addr = AbstractSocketAddr;
|
|
|
|
async fn accept(&mut self) -> (Self::Io, Self::Addr) {
|
|
loop {
|
|
let res = match &self.kind {
|
|
ListenerKind::Tcp(l) => l
|
|
.accept()
|
|
.await
|
|
.map(|(stream, remote_addr)| (stream.into(), remote_addr.into())),
|
|
ListenerKind::Uds(l) => l
|
|
.accept()
|
|
.await
|
|
.map(|(stream, remote_addr)| (stream.into(), remote_addr.into())),
|
|
};
|
|
|
|
match res {
|
|
Ok((stream, remote_addr)) => return (stream, remote_addr),
|
|
Err(e) => {
|
|
// Here the error could be fatal, or could simply be that a client aborted the connected,
|
|
// in which case we don't want to crash the server, simply skip this client connection.
|
|
// https://doc.rust-lang.org/stable/std/net/struct.TcpListener.html#errors
|
|
match e.kind() {
|
|
std::io::ErrorKind::ConnectionAborted => {}
|
|
_ => panic!("Unrecoverable HTTP client connection error: {e}"),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn local_addr(&self) -> std::io::Result<Self::Addr> {
|
|
match &self.kind {
|
|
ListenerKind::Tcp(l) => l.local_addr().map(Into::into),
|
|
ListenerKind::Uds(l) => l.local_addr().map(Into::into),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct HttpState {
|
|
pub db: Database,
|
|
pub sessions: HttpSessionManager,
|
|
pub templates: Environment<'static>,
|
|
}
|
|
|
|
impl HttpState {
|
|
pub fn new(db: Database) -> Self {
|
|
let mut templates = Environment::new();
|
|
#[cfg(feature = "embed")]
|
|
minijinja_embed::load_templates!(&mut templates);
|
|
#[cfg(not(feature = "embed"))]
|
|
templates.set_loader(path_loader("templates"));
|
|
Self {
|
|
db,
|
|
sessions: HttpSessionManager::new(),
|
|
templates,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn http_listen(listener: Listener, db: Database) {
|
|
#[cfg(all(feature = "embed", feature = "noembed"))]
|
|
compile_error!("You cannot have `embed` and `noembed` features enabled at the same time.");
|
|
#[cfg(not(any(feature = "embed", feature = "noembed")))]
|
|
compile_error!("You must have `embed` or `noembed` feature enabled.");
|
|
|
|
#[cfg(feature = "embed")]
|
|
let app = {
|
|
embed_assets!("assets");
|
|
Router::new().nest("/assets", static_router())
|
|
};
|
|
#[cfg(not(feature = "embed"))]
|
|
let app = { Router::new().nest_service("/assets", ServeDir::new("assets")) };
|
|
let app = app
|
|
.route("/", get(home::home))
|
|
.route("/login", get(login::get_login))
|
|
.route("/login", post(login::post_login))
|
|
.route("/logout", get(logout::logout))
|
|
.route("/domain/{domain}", get(domain::get_domain))
|
|
.route("/domain", get(domain::list_domains))
|
|
.route("/domain", post(domain::create_domain))
|
|
.route("/user", post(user::create_user))
|
|
.with_state(HttpState::new(db));
|
|
|
|
axum::serve(listener, app).await.unwrap();
|
|
}
|