swap backend to rust

This commit is contained in:
2025-07-16 16:43:24 +01:00
parent a674f5641d
commit 7dc46b6ad0
16 changed files with 2191 additions and 42 deletions

63
backend/src/lib.rs Normal file
View File

@ -0,0 +1,63 @@
mod routes;
use std::sync::mpsc;
use axum::{Router, serve::WithGracefulShutdown};
use tokio::signal;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
pub fn run(
listener: tokio::net::TcpListener,
) -> Result<
WithGracefulShutdown<
tokio::net::TcpListener,
Router,
Router,
impl std::future::Future<Output = ()>,
>,
std::io::Error,
> {
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| format!("{}=debug", env!("CARGO_CRATE_NAME")).into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
// build our application with some routes
let (app, rx) = routes::app();
// run it
tracing::debug!("listening on {}", listener.local_addr()?);
let server = axum::serve(listener, app);
Ok(server.with_graceful_shutdown(shutdown_signal(rx)))
}
async fn shutdown_signal(rx: mpsc::Receiver<bool>) {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
let endpoint = tokio::spawn(async move {
let _ = rx.recv();
});
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
_ = endpoint => {},
}
}

30
backend/src/main.rs Normal file
View File

@ -0,0 +1,30 @@
use std::net::SocketAddr;
use clap::{Parser, command};
use nuchat::run;
use tracing::{Level, event};
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
/// Host to bind to
#[arg(long, default_value = "127.0.0.1")]
host: String,
/// Port to use
#[arg(long, default_value_t = 7000)]
port: u16,
}
#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
let args = Args::parse();
let str_address = format!("{}:{}", args.host, args.port);
let address: SocketAddr = str_address
.parse()
.unwrap_or_else(|_| panic!("could not parse address: {str_address}"));
let listener = tokio::net::TcpListener::bind(address).await?;
let result = run(listener)?.await;
event!(Level::INFO, "Server stopped");
result
}

View File

@ -0,0 +1,6 @@
use axum::Json;
use serde_json::{Value, json};
pub async fn healthcheck() -> Json<Value> {
Json(json!({"healthy": true}))
}

19
backend/src/routes/mod.rs Normal file
View File

@ -0,0 +1,19 @@
use axum::Router;
use axum::routing::{get, post};
use std::sync::mpsc;
mod healthcheck;
mod shutdown;
use healthcheck::healthcheck;
use shutdown::shutdown;
pub fn app() -> (Router, mpsc::Receiver<bool>) {
let (tx, rx) = mpsc::channel();
(
Router::new()
.route("/healthcheck", get(healthcheck))
.route("/shutdown", post(move || shutdown(tx.clone()))),
rx,
)
}

View File

@ -0,0 +1,8 @@
use std::sync::mpsc;
use tracing::{Level, event};
#[allow(clippy::unused_async)]
pub async fn shutdown(tx: mpsc::Sender<bool>) {
event!(Level::INFO, "Shutdown request received, stopping server...");
tx.send(true).expect("failed to send shutdown signal");
}