1
Fork 0
mirror of https://github.com/RGBCube/Site synced 2025-07-31 13:07:46 +00:00

Add HTTP redirect

This commit is contained in:
RGBCube 2024-01-08 10:38:51 +03:00
parent 019e1c7091
commit dc306f1ed7
No known key found for this signature in database
2 changed files with 106 additions and 14 deletions

View file

@ -103,12 +103,39 @@
services.site = {
enable = mkEnableOption (mdDoc "site service");
port = mkOption {
certificate = mkOption {
type = types.nullOr types.path;
default = null;
example = "/path/to/cert.pem";
description = mdDoc ''
The path to the SSL certificate the site will use.
'';
};
key = mkOption {
type = types.nullOr types.path;
default = null;
example = "/path/to/key.pem";
description = mdDoc ''
The path to the SSL key the site will use.
'';
};
httpPort = mkOption {
type = types.port;
default = 8080;
example = 80;
description = mdDoc ''
Specifies on which port the site service listens for connections.
Specifies on which port the site service listens for HTTP connections.
'';
};
httpsPort = mkOption {
type = types.port;
default = 8443;
example = 80;
description = mdDoc ''
Specifies on which port the site service listens for HTTPS connections.
'';
};
@ -138,11 +165,20 @@
wantedBy = [ "multi-user.target" ];
serviceConfig = let
needsPrivilidges = cfg.port < 1024;
arguments = [
"--http-port" cfg.httpPort
"--https-port" cfg.httpsPort
"--log-level" cfg.logLevel
] ++ (optionals (cfg.certificate != null) [
"--certificate" cfg.certificate
]) ++ (optionals (cfg.key != null) [
"--key" cfg.key
]);
needsPrivilidges = cfg.httpPort < 1024 || cfg.httpsPort < 1024;
capabilities = [ "" ] ++ optionals needsPrivilidges [ "CAP_NET_BIND_SERVICE" ];
rootDirectory = "/run/site";
in {
ExecStart = "${self.packages.${pkgs.system}.site}/bin/site --port ${cfg.port} --log-level ${cfg.logLevel}";
ExecStart = "${self.packages.${pkgs.system}.site}/bin/site " + (concatStringsSep " " arguments);
Restart = "always";
DynamicUser = true;
RootDirectory = rootDirectory;

View file

@ -1,4 +1,4 @@
#![feature(lazy_cell, let_chains)]
#![feature(lazy_cell)]
mod asset;
mod errors;
@ -13,17 +13,31 @@ use std::{
};
use anyhow::Context;
use axum::Router;
use axum::{
extract::Host,
handler::HandlerWithoutStateExt,
http::{
uri::Scheme,
StatusCode,
Uri,
},
response::Redirect,
BoxError,
Router,
};
use axum_server::tls_rustls::RustlsConfig;
use clap::Parser;
use tower_http::trace::TraceLayer;
#[derive(Parser)]
#[derive(Parser, Clone)]
#[command(author, version, about)]
struct Cli {
/// The port to listen for connections on
/// The HTTP port to listen for connections on
#[arg(long, default_value = "8080")]
port: u16,
http_port: u16,
/// The HTTPS port to listen for connections on
#[arg(long, default_value = "8443")]
https_port: u16,
/// The log level to log stuff with
#[arg(long, default_value = "info")]
log_level: log::LevelFilter,
@ -36,6 +50,46 @@ struct Cli {
key: Option<PathBuf>,
}
async fn redirect_http(args: Cli) {
let http_port = args.http_port.to_string();
let https_port = args.https_port.to_string();
let make_https = move |host: String, uri: Uri| -> Result<Uri, BoxError> {
let mut parts = uri.into_parts();
parts.scheme = Some(Scheme::HTTPS);
if parts.path_and_query.is_none() {
parts.path_and_query = Some("/".parse().unwrap());
}
let https_host = host.replace(&http_port, &https_port);
parts.authority = Some(https_host.parse()?);
Ok(Uri::from_parts(parts)?)
};
let redirect = move |Host(host): Host, uri: Uri| {
async move {
match make_https(host, uri) {
Ok(uri) => Ok(Redirect::permanent(&uri.to_string())),
Err(error) => {
log::warn!("Failed to convert URI to HTTPS: {}", error);
Err(StatusCode::BAD_REQUEST)
},
}
}
};
let address = SocketAddr::from(([0, 0, 0, 0], args.http_port));
axum_server::bind(address)
.serve(redirect.into_make_service())
.await
.with_context(|| "Failed to run redirect server")
.unwrap();
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let args = Cli::parse();
@ -52,20 +106,22 @@ async fn main() -> anyhow::Result<()> {
.layer(TraceLayer::new_for_http())
.into_make_service();
let address = SocketAddr::from(([0, 0, 0, 0], args.port));
if let (Some(certificate_path), Some(key_path)) = (&args.certificate, &args.key) {
tokio::spawn(redirect_http(args.clone()));
if let Some(certificate_path) = args.certificate
&& let Some(key_path) = args.key
{
let config = RustlsConfig::from_pem_file(certificate_path, key_path)
.await
.with_context(|| "Failed to create TLS configuration from PEM files")?;
let address = SocketAddr::from(([0, 0, 0, 0], args.https_port));
axum_server::bind_rustls(address, config).serve(app).await
} else {
let address = SocketAddr::from(([0, 0, 0, 0], args.http_port));
axum_server::bind(address).serve(app).await
}
.with_context(|| "Failed to run server")?;
.with_context(|| "Failed to run main server")?;
Ok(())
}