mirror of
https://github.com/Such-Software/smirk-backend-core.git
synced 2026-10-19 08:18:39 +02:00
feat: AppState + server wiring (config, pool, migrations, session manager)
- AppState (lib.rs): Config + Database + SessionManager + in-memory web-challenge store; injected into handlers via State<Arc<AppState>> - main.rs: fail-closed Config::from_env, pg pool, run embedded migrations on start, build state, serve /health (auth/wallet routes added as handlers land) - Cargo.toml: sqlx 'migrate' feature for embedded migrations
This commit is contained in:
+1
-1
@@ -32,7 +32,7 @@ tower_governor = "0.4"
|
||||
governor = "0.6"
|
||||
|
||||
# Database
|
||||
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "chrono", "uuid", "ipnetwork"] }
|
||||
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "chrono", "uuid", "ipnetwork", "migrate"] }
|
||||
ipnetwork = "0.20"
|
||||
|
||||
# Serialization
|
||||
|
||||
+20
@@ -14,3 +14,23 @@ pub mod core;
|
||||
pub mod error;
|
||||
pub mod infra;
|
||||
pub mod models;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::core::session::{SessionManager, WebChallenge};
|
||||
use crate::infra::db::Database;
|
||||
|
||||
/// Shared application state injected into handlers via `State<Arc<AppState>>`.
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
pub config: Config,
|
||||
pub db: Database,
|
||||
pub sessions: SessionManager,
|
||||
/// In-memory website-auth challenges, keyed by nonce. Single-node store;
|
||||
/// a shared/stateless variant is the load-balanced-fleet path.
|
||||
pub web_challenges: Arc<RwLock<HashMap<String, WebChallenge>>>,
|
||||
}
|
||||
|
||||
+43
-12
@@ -1,23 +1,54 @@
|
||||
//! smirk-backend-core server entry point.
|
||||
//!
|
||||
//! Minimal server skeleton. Configuration, application state, the full router,
|
||||
//! and background tasks are added as modules land; for now it serves the
|
||||
//! health endpoint so the build, the OpenAPI contract, and deployment wiring
|
||||
//! are exercised end to end.
|
||||
//! Loads and validates configuration (fail-closed), connects the database and
|
||||
//! runs migrations, builds the shared [`AppState`], and serves the HTTP API.
|
||||
//! Routes are added as handler modules land; for now it serves health.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::{routing::get, Router};
|
||||
use smirk_backend_core::api::health;
|
||||
use sqlx::postgres::PgPoolOptions;
|
||||
|
||||
use smirk_backend_core::{
|
||||
api::health, config::Config, core::session::SessionManager, infra::db::Database, AppState,
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
tracing_subscriber::fmt::init();
|
||||
let _ = dotenvy::dotenv();
|
||||
|
||||
let app = Router::new().route("/health", get(health::health));
|
||||
// Fail-closed: aborts on a weak/missing/inconsistent secret.
|
||||
let config = Config::from_env()?;
|
||||
tracing::info!(environment = %config.environment, "configuration loaded");
|
||||
|
||||
let addr = "0.0.0.0:8080";
|
||||
let listener = tokio::net::TcpListener::bind(addr)
|
||||
.await
|
||||
.expect("bind listener");
|
||||
let pool = PgPoolOptions::new()
|
||||
.max_connections(20)
|
||||
.connect(&config.database_url)
|
||||
.await?;
|
||||
sqlx::migrate!("./migrations").run(&pool).await?;
|
||||
|
||||
let db = Database::new(
|
||||
pool,
|
||||
config.secrets.seed_fingerprint_pepper.clone(),
|
||||
config.secrets.ip_salt.clone(),
|
||||
);
|
||||
let sessions = SessionManager::new(&config.auth.jwt_secret, config.auth.jwt_expiry_hours);
|
||||
|
||||
let addr = format!("{}:{}", config.server_host, config.server_port);
|
||||
let state = Arc::new(AppState {
|
||||
config,
|
||||
db,
|
||||
sessions,
|
||||
web_challenges: Arc::default(),
|
||||
});
|
||||
|
||||
let app = Router::new()
|
||||
.route("/health", get(health::health))
|
||||
.with_state(state);
|
||||
|
||||
let listener = tokio::net::TcpListener::bind(&addr).await?;
|
||||
tracing::info!("smirk-backend-core listening on http://{addr}");
|
||||
axum::serve(listener, app).await.expect("server error");
|
||||
axum::serve(listener, app).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user