Rename employee-tracking-backend to purenotify_backend and add OFFLINE
mode - Rename crate and update dependencies to newer versions - Add OFFLINE runtime mode for loopback-only server without DB - Refactor state handling with typed Axum routers and state injection - Rename mongodb module to mongo and fix imports accordingly - Update Cargo.lock with updated and removed dependencies - Remove no-auth feature and related code - Simplify health and user routes to generic state parameter Rename backend to purenotify_backend and add OFFLINE mode Use OFFLINE env var to run server without DB, binding to loopback only. Rename mongodb module to mongo and update dependencies. Update dependencies and fix router state handling.
This commit is contained in:
2
.env
2
.env
@@ -2,4 +2,4 @@
|
||||
|
||||
RUST_LOG=info
|
||||
BIND_ADDRESS=127.0.0.1:3000
|
||||
# DATABASE_URL=postgres://gerard@localhost/db (not used yet)
|
||||
MONGO_URI=mongodb://localhost:27017
|
||||
|
||||
1248
Cargo.lock
generated
1248
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
20
Cargo.toml
20
Cargo.toml
@@ -1,24 +1,20 @@
|
||||
[package]
|
||||
name = "employee-tracking-backend"
|
||||
name = "purenotify_backend"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
axum = "0.8.4"
|
||||
bson = { version = "2.9.0", features = ["chrono-0_4"] }
|
||||
chrono = { version = "0.4.31", features = ["serde"] }
|
||||
sha2 = "0.10.8"
|
||||
rand = "0.8.5"
|
||||
regex = "1.10.4"
|
||||
bson = { version = "2.15.0", features = ["chrono-0_4"] }
|
||||
chrono = { version = "0.4.41", features = ["serde"] }
|
||||
sha2 = "0.10.9"
|
||||
rand = "0.9.2"
|
||||
regex = "1.11.1"
|
||||
dotenvy = "0.15.7"
|
||||
mongodb = "2.8.2"
|
||||
mongodb = "3.2.4"
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
serde_json = "1.0.142"
|
||||
sqlx = { version = "0.8.6", features = ["runtime-tokio", "tls-native-tls"] }
|
||||
serde_json = "1.0.143"
|
||||
tokio = { version = "1.47.1", features = ["full", "rt-multi-thread", "signal"] }
|
||||
tower-http = { version = "0.6.6", features = ["trace"] }
|
||||
tracing = "0.1.41"
|
||||
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||
|
||||
[features]
|
||||
no-auth = []
|
||||
|
||||
@@ -4,7 +4,6 @@ use std::env;
|
||||
use std::net::SocketAddr;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[cfg(feature = "no-auth")]
|
||||
use tracing::error;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -22,7 +21,6 @@ impl Config {
|
||||
let bind_address = SocketAddr::from_str(&bind_address_str)
|
||||
.map_err(|e| format!("Invalid BIND_ADDRESS: {}", e))?;
|
||||
|
||||
#[cfg(feature = "no-auth")]
|
||||
if bind_address.ip() != std::net::IpAddr::from([127, 0, 0, 1]) {
|
||||
error!("In no-auth mode, BIND_ADDRESS must be 127.0.0.1");
|
||||
return Err("In no-auth mode, BIND_ADDRESS must be 127.0.0.1".to_string());
|
||||
|
||||
82
src/main.rs
82
src/main.rs
@@ -1,9 +1,7 @@
|
||||
// src/main.rs
|
||||
|
||||
use std::process::exit;
|
||||
use std::sync::Arc;
|
||||
use std::{process::exit, sync::Arc};
|
||||
|
||||
use ::mongodb::Database;
|
||||
use axum::Router;
|
||||
use dotenvy::dotenv;
|
||||
use tokio::signal;
|
||||
@@ -13,13 +11,14 @@ use tracing_subscriber::{EnvFilter, fmt, prelude::*};
|
||||
|
||||
mod config;
|
||||
mod handlers;
|
||||
mod mongodb;
|
||||
mod mongo; // local module wrapping the Mongo client
|
||||
mod routes;
|
||||
|
||||
use ::mongodb::Database; // external crate (absolute path avoids name clash)
|
||||
use config::Config;
|
||||
use mongodb::MongoDb;
|
||||
use mongo::MongoDb; // your wrapper
|
||||
|
||||
// Shared application state
|
||||
// Shared application state for online mode
|
||||
pub struct AppState {
|
||||
pub db: Database,
|
||||
pub config: Config,
|
||||
@@ -27,51 +26,86 @@ pub struct AppState {
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// Load environment variables from .env file
|
||||
// Load .env early
|
||||
dotenv().ok();
|
||||
|
||||
// Initialize tracing
|
||||
// Tracing with a safe fallback if RUST_LOG is unset
|
||||
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
|
||||
tracing_subscriber::registry()
|
||||
.with(fmt::layer())
|
||||
.with(EnvFilter::from_env("RUST_LOG"))
|
||||
.with(env_filter)
|
||||
.init();
|
||||
|
||||
// Load config
|
||||
let config = match Config::from_env() {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
error!("Failed to load config: {}", e);
|
||||
error!("Failed to load config: {e}");
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
// Connect to MongoDB using the config
|
||||
let mongodb = match MongoDb::connect(&config).await {
|
||||
// Runtime OFFLINE switch: true if OFFLINE is 1/true/yes/on (case-insensitive)
|
||||
let offline = std::env::var("OFFLINE")
|
||||
.ok()
|
||||
.map(|v| matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes" | "on"))
|
||||
.unwrap_or(false);
|
||||
|
||||
if offline {
|
||||
// Enforce loopback binding while offline
|
||||
if !config.bind_address.ip().is_loopback() {
|
||||
error!(
|
||||
"OFFLINE=true requires binding to a loopback address (e.g., 127.0.0.1:<port> or [::1]:<port>), got {}",
|
||||
config.bind_address
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
info!("OFFLINE mode enabled — not connecting to MongoDB");
|
||||
info!("Server starting on {}", config.bind_address);
|
||||
|
||||
// Health-only, no state. Subrouter is typed to `()`.
|
||||
let app = Router::new()
|
||||
.nest("/health", routes::health::health::health_routes::<()>())
|
||||
.layer(TraceLayer::new_for_http());
|
||||
|
||||
let listener = tokio::net::TcpListener::bind(config.bind_address)
|
||||
.await
|
||||
.unwrap();
|
||||
axum::serve(listener, app)
|
||||
.with_graceful_shutdown(shutdown_signal())
|
||||
.await
|
||||
.unwrap();
|
||||
return;
|
||||
}
|
||||
|
||||
// --- Online (DB-enabled) path ---
|
||||
let mongo = match MongoDb::connect(&config).await {
|
||||
Ok(db) => db,
|
||||
Err(e) => {
|
||||
error!("Failed to connect to MongoDB: {}", e);
|
||||
error!("Failed to connect to MongoDB: {e}");
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
// Create shared state
|
||||
let _shared_state = Arc::new(AppState {
|
||||
db: mongodb.database,
|
||||
let shared_state = Arc::new(AppState {
|
||||
db: mongo.database,
|
||||
config: config.clone(),
|
||||
});
|
||||
|
||||
#[cfg(feature = "no-auth")]
|
||||
info!("NO-AUTH MODE ENABLED");
|
||||
|
||||
info!("Server starting on {}", config.bind_address);
|
||||
|
||||
// Build the Axum router
|
||||
let app = Router::new()
|
||||
.nest("/health", routes::health::health::health_routes())
|
||||
.nest("/user", routes::user::user::user_routes())
|
||||
// Build subrouters typed with the same state as the root
|
||||
let health_router = routes::health::health::health_routes::<Arc<AppState>>();
|
||||
let user_router = routes::user::user::user_routes::<Arc<AppState>>();
|
||||
|
||||
// Root router typed with state; set state once on the root
|
||||
let app = Router::<Arc<AppState>>::new()
|
||||
.nest("/health", health_router)
|
||||
.nest("/user", user_router)
|
||||
.with_state(shared_state)
|
||||
.layer(TraceLayer::new_for_http());
|
||||
|
||||
// Run the server
|
||||
let listener = tokio::net::TcpListener::bind(config.bind_address)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -25,7 +25,7 @@ impl MongoDb {
|
||||
// Ping the server to verify connection
|
||||
client
|
||||
.database("admin")
|
||||
.run_command(mongodb::bson::doc! {"ping": 1}, None)
|
||||
.run_command(mongodb::bson::doc! {"ping": 1})
|
||||
.await?;
|
||||
|
||||
println!("✅ Successfully connected to MongoDB!");
|
||||
@@ -1,9 +1,11 @@
|
||||
// src/routes/health/health.rs
|
||||
// src/routes/health/healh.rs
|
||||
|
||||
use axum::{Router, routing::get};
|
||||
|
||||
use crate::handlers::health::health::health;
|
||||
|
||||
pub fn health_routes() -> Router {
|
||||
Router::new().route("/", get(health))
|
||||
pub fn health_routes<S>() -> Router<S>
|
||||
where
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
// keep your existing routes/handlers here
|
||||
Router::new().route("/", get(crate::handlers::health::health::health))
|
||||
}
|
||||
|
||||
@@ -5,11 +5,12 @@ use axum::{
|
||||
routing::{get, post},
|
||||
};
|
||||
|
||||
use crate::handlers::user::register::register;
|
||||
use crate::handlers::user::user::user;
|
||||
|
||||
pub fn user_routes() -> Router {
|
||||
pub fn user_routes<S>() -> Router<S>
|
||||
where
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
// keep your existing routes/handlers here
|
||||
Router::new()
|
||||
.route("/", get(user))
|
||||
.route("/register", post(register))
|
||||
.route("/", get(crate::handlers::user::user::user))
|
||||
.route("/register", post(crate::handlers::user::register::register))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user