feat: add router

This commit is contained in:
Kristofers Solo 2025-06-21 16:15:50 +03:00
parent 3e190e3cca
commit 85765bb3b0
Signed by: kristoferssolo
GPG Key ID: 8687F2D3EEE6F0ED
7 changed files with 64 additions and 47 deletions

View File

@ -1,4 +1,4 @@
mod configuration; pub mod configuration;
mod routes; pub mod routes;
mod startup; pub mod startup;
mod telemetry; pub mod telemetry;

View File

@ -1,30 +1,16 @@
use app::*; use server::{
use axum::Router; configuration::get_config,
use leptos::logging::log; startup::Application,
use leptos::prelude::*; telemetry::{get_subscriber, init_subscriber},
use leptos_axum::{LeptosRoutes, generate_route_list}; };
use std::io::Error;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() -> Result<(), Error> {
let conf = get_configuration(None).unwrap(); let subscriber = get_subscriber("kristofersxyz", "info", std::io::stdout);
let addr = conf.leptos_options.site_addr; init_subscriber(subscriber);
let leptos_options = conf.leptos_options; let config = get_config().expect("Failed to read configuation.");
// Generate the list of routes in your Leptos App let application = Application::build(&config).await?;
let routes = generate_route_list(App); application.start().await?;
Ok(())
let app = Router::new()
.leptos_routes(&leptos_options, routes, {
let leptos_options = leptos_options.clone();
move || shell(leptos_options.clone())
})
.fallback(leptos_axum::file_and_error_handler(shell))
.with_state(leptos_options);
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`
log!("listening on http://{}", &addr);
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
axum::serve(listener, app.into_make_service())
.await
.unwrap();
} }

View File

@ -1,6 +1,27 @@
use crate::startup::AppState; use crate::startup::AppState;
use axum::Router; use app::{App, shell};
use axum::{Router, extract::State, http::StatusCode, response::IntoResponse, routing::get};
use leptos_axum::{LeptosRoutes, generate_route_list};
pub fn route(state: AppState) -> Router { pub fn route(state: AppState) -> Router {
todo!() let leptos_options = state.leptos_options.clone();
let routes = generate_route_list(App);
let leptos_router = Router::new()
.leptos_routes(&leptos_options, routes, {
let leptos_options = leptos_options.clone();
move || shell(leptos_options.clone())
})
.fallback(leptos_axum::file_and_error_handler(shell))
.with_state(leptos_options);
let api_router = Router::new()
.route("/health_check", get(health_check))
.with_state(state);
leptos_router.merge(api_router)
}
async fn health_check(State(_state): State<AppState>) -> impl IntoResponse {
StatusCode::OK
} }

View File

@ -2,9 +2,12 @@ use crate::{
configuration::{DatabaseSettings, Settings}, configuration::{DatabaseSettings, Settings},
routes::route, routes::route,
}; };
use leptos::config::{LeptosOptions, get_configuration};
use sqlx::{PgPool, postgres::PgPoolOptions}; use sqlx::{PgPool, postgres::PgPoolOptions};
use std::{ use std::{
io::{Error, ErrorKind}, io::{Error, ErrorKind},
net::SocketAddr,
ops::Deref,
sync::Arc, sync::Arc,
}; };
use tokio::{net::TcpListener, task::JoinHandle}; use tokio::{net::TcpListener, task::JoinHandle};
@ -13,6 +16,8 @@ use tracing::{error, info};
/// Shared application state. /// Shared application state.
pub struct App { pub struct App {
pub pool: PgPool, pub pool: PgPool,
pub leptos_options: LeptosOptions,
pub addr: SocketAddr,
} }
/// Type alias for the shared application state wrapped in `Arc`. /// Type alias for the shared application state wrapped in `Arc`.
@ -20,7 +25,6 @@ pub type AppState = Arc<App>;
/// Represents the application, including its server and configuration. /// Represents the application, including its server and configuration.
pub struct Application { pub struct Application {
port: u16,
server: Option<JoinHandle<Result<(), Error>>>, server: Option<JoinHandle<Result<(), Error>>>,
} }
@ -30,20 +34,25 @@ impl Application {
/// This method initializes the database connection pool, binds the server /// This method initializes the database connection pool, binds the server
/// to the specified address, and prepares the application for startup. /// to the specified address, and prepares the application for startup.
pub async fn build(config: &Settings) -> Result<Self, Error> { pub async fn build(config: &Settings) -> Result<Self, Error> {
let conf = get_configuration(None).unwrap();
let addr = conf.leptos_options.site_addr;
let leptos_options = conf.leptos_options;
// Initialize the database connection pool // Initialize the database connection pool
let pool = get_connection_pool(&config.database); let pool = get_connection_pool(&config.database);
info!("Database connection pool initialized."); info!("Database connection pool initialized.");
// Bind the server to the specified address
let addr = format!("{}:{}", config.application.host, config.application.port);
let listener = TcpListener::bind(addr).await?;
let port = listener.local_addr().unwrap().port();
info!("Server listening on port {}", port);
// Create the shared application state // Create the shared application state
let app_state = App { pool }.into(); let app_state = App {
pool,
leptos_options: leptos_options.clone(),
addr,
}
.into();
// Spawn the server // Spawn the server
info!("listening on http://{}", &addr);
let listener = TcpListener::bind(addr).await?;
let server = tokio::spawn(async move { let server = tokio::spawn(async move {
axum::serve(listener, route(app_state)).await.map_err(|e| { axum::serve(listener, route(app_state)).await.map_err(|e| {
error!("Server error: {}", e); error!("Server error: {}", e);
@ -52,16 +61,10 @@ impl Application {
}); });
Ok(Self { Ok(Self {
port,
server: Some(server), server: Some(server),
}) })
} }
/// Get the port the server is listening on.
pub fn port(&self) -> u16 {
self.port
}
pub async fn start(self) -> Result<(), Error> { pub async fn start(self) -> Result<(), Error> {
self.server self.server
.ok_or_else(|| Error::new(ErrorKind::Other, "Server was not initialized."))? .ok_or_else(|| Error::new(ErrorKind::Other, "Server was not initialized."))?
@ -75,3 +78,10 @@ impl Application {
fn get_connection_pool(config: &DatabaseSettings) -> PgPool { fn get_connection_pool(config: &DatabaseSettings) -> PgPool {
PgPoolOptions::new().connect_lazy_with(config.with_db()) PgPoolOptions::new().connect_lazy_with(config.with_db())
} }
impl Deref for App {
type Target = LeptosOptions;
fn deref(&self) -> &Self::Target {
&self.leptos_options
}
}