diff --git a/flake.nix b/flake.nix index ee872eb..d03a941 100644 --- a/flake.nix +++ b/flake.nix @@ -126,6 +126,10 @@ path = ./templates/webapp; description = "A template for a web application (frontend + backend)"; }; + webserver = { + path = ./templates/webserver; + description = "A template for a web server (using templates for the frontend)"; + }; }; packages.x86_64-linux = pkgList "x86_64-linux" nixpkgs.legacyPackages.x86_64-linux.callPackage; packages.aarch64-linux = pkgList "aarch64-linux" nixpkgs.legacyPackages.aarch64-linux.callPackage; diff --git a/templates/webserver/.envrc b/templates/webserver/.envrc new file mode 100644 index 0000000..5e9005b --- /dev/null +++ b/templates/webserver/.envrc @@ -0,0 +1,4 @@ +if ! has nix_direnv_version || ! nix_direnv_version 2.1.1; then + source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.1.1/direnvrc" "sha256-b6qJ4r34rbE23yWjMqbmu3ia2z4b2wIlZUksBke/ol0=" +fi +use flake diff --git a/templates/webserver/.gitignore b/templates/webserver/.gitignore new file mode 100644 index 0000000..6abfe1b --- /dev/null +++ b/templates/webserver/.gitignore @@ -0,0 +1,2 @@ +/target +/.direnv diff --git a/templates/webserver/Cargo.toml b/templates/webserver/Cargo.toml new file mode 100644 index 0000000..76a5273 --- /dev/null +++ b/templates/webserver/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "webserver-template" +version = "0.1.0" +authors = ["traxys "] +edition = "2021" + +[dependencies] +axum = "0.6.20" +envious = "0.2.2" +serde = { version = "1.0.190", features = ["derive"] } +tera = "1.19.1" +thiserror = "1.0.50" +tokio = { version = "1.33.0", features = ["rt", "macros"] } +tower-http = { version = "0.4.4", features = ["trace"] } +tracing = "0.1.40" +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } diff --git a/templates/webserver/flake.nix b/templates/webserver/flake.nix new file mode 100644 index 0000000..4c067b9 --- /dev/null +++ b/templates/webserver/flake.nix @@ -0,0 +1,34 @@ +{ + description = "A basic flake with a shell"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + inputs.flake-utils.url = "github:numtide/flake-utils"; + inputs.naersk.url = "github:nix-community/naersk"; + inputs.rust-overlay.url = "github:oxalica/rust-overlay"; + + outputs = { + self, + nixpkgs, + flake-utils, + naersk, + rust-overlay, + }: + flake-utils.lib.eachDefaultSystem (system: let + pkgs = import nixpkgs { + inherit system; + overlays = [(import rust-overlay)]; + }; + rust = pkgs.rust-bin.stable.latest.default; + naersk' = pkgs.callPackage naersk { + cargo = rust; + rustc = rust; + }; + in { + devShell = pkgs.mkShell { + nativeBuildInputs = [rust]; + RUST_PATH = "${rust}"; + RUST_DOC_PATH = "${rust}/share/doc/rust/html/std/index.html"; + }; + + packages.default = naersk'.buildPackage ./.; + }); +} diff --git a/templates/webserver/src/main.rs b/templates/webserver/src/main.rs new file mode 100644 index 0000000..44e4a5f --- /dev/null +++ b/templates/webserver/src/main.rs @@ -0,0 +1,88 @@ +use std::{net::SocketAddr, sync::Arc}; + +use axum::{ + http::StatusCode, + response::{Html, IntoResponse}, + routing::get, + Router, +}; +use serde::{Deserialize, Serialize}; +use tera::{Context, Tera}; +use tower_http::trace::TraceLayer; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + +#[derive(thiserror::Error, Debug)] +enum Error { + #[error("Could not render tera template")] + Template(#[from] tera::Error), +} + +impl IntoResponse for Error { + fn into_response(self) -> axum::response::Response { + tracing::error!("Failure in route: {self:?}"); + + match self { + Error::Template(_) => ( + StatusCode::INTERNAL_SERVER_ERROR, + "an internal error occured", + ) + .into_response(), + } + } +} + +const TEMPLATE_DIR: &str = match option_env!("TEMPLATE_DIR") { + Some(s) => s, + None => "templates", +}; + +struct AppState { + templates: Tera, +} + +type State = axum::extract::State>; + +async fn index(state: State) -> Result, Error> { + let rendered = state.templates.render("index.html", &Context::new())?; + + Ok(rendered.into()) +} + +fn mk_port() -> u16 { + 8080 +} + +#[derive(Serialize, Deserialize)] +struct Config { + #[serde(default = "mk_port")] + port: u16, +} + +#[tokio::main(flavor = "current_thread")] +async fn main() { + tracing_subscriber::registry() + .with(tracing_subscriber::EnvFilter::from_default_env()) + .with(tracing_subscriber::fmt::layer()) + .init(); + + let config: Config = envious::Config::default() + .build_from_env() + .expect("could not get env vars"); + + let templates = Tera::new(&format!("{TEMPLATE_DIR}/**/*.html")).unwrap(); + let state = Arc::new(AppState { templates }); + + let router = Router::new() + .route("/", get(index)) + .with_state(state) + .layer(TraceLayer::new_for_http()); + + tracing::info!("Listening on port {}", config.port); + + let listen = SocketAddr::new("0.0.0.0".parse().unwrap(), config.port); + axum::Server::bind(&listen) + .serve(router.into_make_service()) + .await + .expect("web server failed"); +} + diff --git a/templates/webserver/templates/index.html b/templates/webserver/templates/index.html new file mode 100644 index 0000000..beb3229 --- /dev/null +++ b/templates/webserver/templates/index.html @@ -0,0 +1,9 @@ + + + + + + TODO: set title + + +