feat: implement audit logs system, request extractor, admin log panel, and dedicated documentation

- Added an enterprise-grade, request-scoped AuditLogger extractor in Axum.
- Configured MongoDB persistence for structured, replayable audit logs (capturing timestamp, user, action, type, payload snapshot, client IP with proxy header support, and User-Agent).
- Created a live Administrator console at /auth/audit to filter and inspect log events.
- Re-architected documentation by moving Design Wiki pages out of /components into a dedicated /docs route.
- Published logging architecture documentation at /docs/logging.
This commit is contained in:
2026-05-30 18:23:49 +05:00
parent f6ea8a99d9
commit 4c98dd93ad
34 changed files with 1389 additions and 134 deletions
+15 -3
View File
@@ -1,9 +1,10 @@
mod common;
mod auth;
mod tasks;
mod audit;
mod developers;
mod main_view;
mod components;
mod docs;
use axum::{extract::FromRef, Router};
use std::net::SocketAddr;
@@ -15,6 +16,7 @@ use crate::common::database::connect_db;
use crate::auth::repository::MongoUserRepository;
use crate::tasks::repository::MongoTaskRepository;
use crate::developers::repository::MongoDeveloperRepository;
use crate::audit::repository::MongoAuditRepository;
#[derive(Clone)]
struct AppState {
@@ -23,6 +25,7 @@ struct AppState {
user_repo: MongoUserRepository,
task_repo: MongoTaskRepository,
dev_repo: MongoDeveloperRepository,
audit_repo: MongoAuditRepository,
}
impl FromRef<AppState> for Config {
@@ -55,6 +58,12 @@ impl FromRef<AppState> for MongoDeveloperRepository {
}
}
impl FromRef<AppState> for MongoAuditRepository {
fn from_ref(state: &AppState) -> Self {
state.audit_repo.clone()
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. Initialize logging
@@ -74,6 +83,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let user_repo = MongoUserRepository::new(db.clone());
let task_repo = MongoTaskRepository::new(db.clone());
let dev_repo = MongoDeveloperRepository::new(db.clone());
let audit_repo = MongoAuditRepository::new(db.clone());
// Auto-provision initial administrator if users collection is empty
let users_count = db.collection::<crate::auth::models::User>("users")
@@ -118,15 +128,17 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
user_repo,
task_repo,
dev_repo,
audit_repo,
};
// 6. Build and merge routers by use-case
let app = Router::new()
.merge(main_view::router())
.merge(components::router())
.merge(docs::router())
.merge(auth::router())
.merge(tasks::router())
.merge(developers::router())
.merge(audit::router())
.with_state(state);
// 7. Bind address and run server
@@ -137,7 +149,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
info!("Listening on http://{}", host_addr);
let listener = tokio::net::TcpListener::bind(host_addr).await?;
axum::serve(listener, app).await?;
axum::serve(listener, app.into_make_service_with_connect_info::<SocketAddr>()).await?;
Ok(())
}