feat: add MongoDB support with connection pooling and repository pattern
This commit is contained in:
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
26
src/main.rs
26
src/main.rs
@@ -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
6
src/mongodb/mod.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
// src/mongodb/mod.rs
|
||||
|
||||
pub mod models;
|
||||
pub mod mongodb;
|
||||
|
||||
pub use mongodb::MongoDb;
|
||||
5
src/mongodb/models/mod.rs
Normal file
5
src/mongodb/models/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
// src/mongodb/models/mod.rs
|
||||
|
||||
pub mod user;
|
||||
|
||||
// Re-exports can be added here when needed
|
||||
49
src/mongodb/models/user.rs
Normal file
49
src/mongodb/models/user.rs
Normal 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
38
src/mongodb/mongodb.rs
Normal 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 })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user