feat(db): add SQLx migrations and AppState with connection pool
- Add sqlx 0.8 (AnyPool, runtime-tokio, sqlite, postgres, migrate) - Create 6 migration files for both SQLite and PostgreSQL backends - Add server/db.rs: create_pool and run_migrations helpers - Add server/state.rs: AppState with LeptosOptions + AnyPool - Run migrations at server startup before accepting requests - Fix Port model: remove host_id (ports are now a global catalog) - Add HostPort join struct for the host_ports many-to-many table Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
43
src/main.rs
43
src/main.rs
@@ -21,7 +21,12 @@ async fn main() {
|
||||
use leptos_axum::{generate_route_list, LeptosRoutes};
|
||||
use rust_ipam::{
|
||||
app::{App, Shell},
|
||||
server::{config::AppConfig, routes::not_found_handler},
|
||||
server::{
|
||||
config::AppConfig,
|
||||
db::{create_pool, run_migrations},
|
||||
routes::not_found_handler,
|
||||
state::AppState,
|
||||
},
|
||||
};
|
||||
use tower_http::services::ServeDir;
|
||||
|
||||
@@ -44,6 +49,18 @@ async fn main() {
|
||||
|
||||
tracing::info!("Database: {} ({})", app_config.backend, app_config.database_url);
|
||||
|
||||
// Connect to the database and apply any pending migrations.
|
||||
// The server cannot serve data without a working database, so we abort on error.
|
||||
let pool = create_pool(&app_config)
|
||||
.await
|
||||
.expect("Failed to connect to database");
|
||||
|
||||
run_migrations(&pool, &app_config.backend)
|
||||
.await
|
||||
.expect("Database migration failed");
|
||||
|
||||
tracing::info!("Database ready.");
|
||||
|
||||
// `Some("Cargo.toml")` tells Leptos to read the [package.metadata.leptos]
|
||||
// section from Cargo.toml (file paths, output names, server address...).
|
||||
let conf = get_configuration(Some("Cargo.toml"))
|
||||
@@ -51,27 +68,37 @@ async fn main() {
|
||||
let leptos_options = conf.leptos_options;
|
||||
let addr = leptos_options.site_addr;
|
||||
|
||||
// Combine Leptos options and the database pool into a single shared state.
|
||||
// `AppState` implements `FromRef<AppState> for LeptosOptions` so Leptos
|
||||
// can still extract just what it needs from the full state.
|
||||
let state = AppState { leptos_options: leptos_options.clone(), db: pool };
|
||||
|
||||
// Walk all `<Route>` components inside `App` to build the list of URLs
|
||||
// that Leptos SSR must handle.
|
||||
let routes = generate_route_list(App);
|
||||
|
||||
// Build the Axum router using the builder pattern (method chaining).
|
||||
let app = Router::new()
|
||||
//
|
||||
// `Router::<AppState>::new()` explicitly tells Rust the state type is `AppState`.
|
||||
// Without this annotation, type inference would default to `LeptosOptions`
|
||||
// (inferred from `leptos_routes`) and then reject `.with_state(state: AppState)`.
|
||||
let app = Router::<AppState>::new()
|
||||
// Serve static files compiled by trunk (WASM, JS...).
|
||||
// Trunk places them in target/site/pkg/ as configured in [package.metadata.leptos].
|
||||
.nest_service("/pkg", ServeDir::new("target/site/pkg"))
|
||||
// Mount all Leptos routes into Axum.
|
||||
// For each URL, Axum renders Shell() to HTML and sends it to the browser.
|
||||
// Shell() contains App(), which provides the page content.
|
||||
.leptos_routes(&leptos_options, routes, {
|
||||
// Clone options so the closure can capture them.
|
||||
// `leptos_routes` receives `&state` (the full AppState).
|
||||
// It extracts `LeptosOptions` via `FromRef<AppState>` implemented in state.rs.
|
||||
.leptos_routes(&state, routes, {
|
||||
// Clone options before moving into the closure.
|
||||
// `move` transfers ownership of `leptos_options` into the closure.
|
||||
let leptos_options = leptos_options.clone();
|
||||
let leptos_options = state.leptos_options.clone();
|
||||
move || view! { <Shell options=leptos_options.clone()/> }
|
||||
})
|
||||
.fallback(not_found_handler)
|
||||
// Share Leptos options with all handlers via Axum's state system.
|
||||
.with_state(leptos_options);
|
||||
// Share AppState (Leptos options + DB pool) with all handlers.
|
||||
.with_state(state);
|
||||
|
||||
let listener = tokio::net::TcpListener::bind(&addr)
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user