feat: add MongoDB support with connection pooling and repository pattern

This commit is contained in:
2025-08-16 06:38:30 -04:00
parent 8a05f4edac
commit ed612bd717
12 changed files with 1226 additions and 49 deletions

View File

@@ -4,10 +4,14 @@ use std::env;
use std::net::SocketAddr;
use std::str::FromStr;
#[derive(Debug)]
#[cfg(feature = "no-auth")]
use tracing::error;
#[derive(Debug, Clone)]
pub struct Config {
pub bind_address: SocketAddr,
// pub database_url: Option<String>,
pub mongodb_uri: String,
pub database_name: String,
}
impl Config {
@@ -24,11 +28,15 @@ impl Config {
return Err("In no-auth mode, BIND_ADDRESS must be 127.0.0.1".to_string());
}
// let database_url = env::var("DATABASE_URL").ok();
let mongodb_uri =
env::var("MONGODB_URI").unwrap_or_else(|_| "mongodb://localhost:27017".to_string());
let database_name = env::var("DATABASE_NAME").unwrap_or_else(|_| "purenotify".to_string());
Ok(Self {
bind_address,
// database_url,
mongodb_uri,
database_name,
})
}
}

View File

@@ -1,7 +1,9 @@
// src/main.rs
use std::process::exit;
use std::sync::Arc;
use ::mongodb::Database;
use axum::Router;
use dotenvy::dotenv;
use tokio::signal;
@@ -11,9 +13,17 @@ use tracing_subscriber::{EnvFilter, fmt, prelude::*};
mod config;
mod handlers;
mod mongodb;
mod routes;
use config::Config;
use mongodb::MongoDb;
// Shared application state
pub struct AppState {
pub db: Database,
pub config: Config,
}
#[tokio::main]
async fn main() {
@@ -35,6 +45,21 @@ async fn main() {
}
};
// Connect to MongoDB using the config
let mongodb = match MongoDb::connect(&config).await {
Ok(db) => db,
Err(e) => {
error!("Failed to connect to MongoDB: {}", e);
exit(1);
}
};
// Create shared state
let _shared_state = Arc::new(AppState {
db: mongodb.database,
config: config.clone(),
});
#[cfg(feature = "no-auth")]
info!("NO-AUTH MODE ENABLED");
@@ -42,7 +67,6 @@ async fn main() {
// Build the Axum router
let app = Router::new()
// .nest("/health", routes::health::health::health_routes())
.nest("/health", routes::health::health::health_routes())
.nest("/user", routes::user::user::user_routes())
.layer(TraceLayer::new_for_http());

6
src/mongodb/mod.rs Normal file
View File

@@ -0,0 +1,6 @@
// src/mongodb/mod.rs
pub mod models;
pub mod mongodb;
pub use mongodb::MongoDb;

View File

@@ -0,0 +1,5 @@
// src/mongodb/models/mod.rs
pub mod user;
// Re-exports can be added here when needed

View File

@@ -0,0 +1,49 @@
use bson::oid::ObjectId;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct User {
#[serde(rename = "_id")]
id: ObjectId,
username: String,
email: String,
first_name: String,
last_name: String,
age: u32,
is_active: bool,
phone_number: String,
password: String,
salt: String,
created_at: DateTime<Utc>,
updated_at: DateTime<Utc>,
last_login: DateTime<Utc>,
role: String,
profile: Option<Profile>,
preferences: Option<Preferences>,
stats: Option<Stats>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Profile {
avatar_url: String,
bio: String,
location: String,
website: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Preferences {
theme: String,
language: String,
notifications_enabled: bool,
email_verified: bool,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Stats {
total_posts: u32,
total_comments: u32,
total_likes: u32,
account_age_days: u32,
}

38
src/mongodb/mongodb.rs Normal file
View File

@@ -0,0 +1,38 @@
use crate::config::Config;
use mongodb::options::{ClientOptions, ServerApi, ServerApiVersion};
use mongodb::{Client, Database};
pub struct MongoDb {
// pub client: Client,
pub database: Database,
}
impl MongoDb {
pub async fn connect(config: &Config) -> Result<Self, mongodb::error::Error> {
// Parse connection string from config
let mut client_options = ClientOptions::parse(&config.mongodb_uri).await?;
// Set the server API version (optional but recommended for MongoDB Atlas)
let server_api = ServerApi::builder().version(ServerApiVersion::V1).build();
client_options.server_api = Some(server_api);
// Optional: Set additional options
client_options.app_name = Some("PureNotify".to_string());
// Create client
let client = Client::with_options(client_options)?;
// Ping the server to verify connection
client
.database("admin")
.run_command(mongodb::bson::doc! {"ping": 1}, None)
.await?;
println!("✅ Successfully connected to MongoDB!");
// Get database handle using the database_name from config
let database = client.database(&config.database_name);
Ok(MongoDb { database })
}
}