adminx

Crates.ioadminx
lib.rsadminx
version0.2.1
created_at2025-07-31 06:12:53.890263+00
updated_at2025-08-26 13:43:09.06114+00
descriptionA powerful, modern admin panel framework for Rust built on Actix Web and MongoDB with automatic CRUD, role-based access control, and a beautiful responsive UI
homepagehttps://github.com/snmmaurya/adminx
repositoryhttps://github.com/snmmaurya/adminx
max_upload_size
id1774460
size647,743
Sandeep Maurya (snmmaurya)

documentation

https://docs.rs/adminx

README

AdminX

Crates.io Documentation License Build Status

AdminX is a powerful, modern admin panel framework for Rust built on top of Actix Web and MongoDB. It provides a complete solution for creating administrative interfaces with minimal boilerplate code, featuring automatic CRUD operations, role-based access control, and a beautiful responsive UI.

โœจ Features

๐Ÿš€ Core Functionality

  • Zero-Config CRUD Operations - Automatic Create, Read, Update, Delete with sensible defaults
  • Schema-Driven Forms - Auto-generate forms from JSON Schema using schemars
  • Resource-Centric Architecture - Define resources once, get full admin interface
  • Hybrid API/UI - Both REST API and web interface from same resource definitions
  • Dynamic Menu Generation - Automatic navigation based on registered resources

๐Ÿ” Security First

  • JWT + Session Authentication - Secure token-based auth with session management
  • Role-Based Access Control (RBAC) - Fine-grained permissions per resource
  • Rate Limiting - Built-in protection against brute force attacks
  • Timing Attack Prevention - Secure password verification
  • CSRF Protection - Form-based submission security

๐ŸŽจ Modern UI/UX

  • Responsive Design - Mobile-first TailwindCSS-based interface
  • Dark/Light Mode - Built-in theme switching
  • Toast Notifications - User feedback with auto-dismiss
  • Real-time Validation - Client-side form validation
  • Accessibility - WCAG compliant with proper ARIA labels

๐Ÿ› ๏ธ Developer Experience

  • Minimal Boilerplate - Resources work out-of-the-box
  • Type Safety - Full Rust type safety throughout
  • Embedded Templates - Zero external dependencies
  • Comprehensive Logging - Built-in tracing and debugging
  • Hot Reload Support - Fast development iteration

๐Ÿš€ Quick Start

Add AdminX to your Cargo.toml:

[dependencies]
adminx = "0.1.0"
actix-web = "4"
mongodb = { version = "2.4", features = ["tokio-runtime"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
schemars = { version = "0.8", features = ["derive"] }

1. Define Your Resource

use adminx::prelude::*;
use serde::{Deserialize, Serialize};
use schemars::JsonSchema;
use mongodb::{Collection, bson::Document};

#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
pub struct User {
    #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
    pub id: Option<mongodb::bson::oid::ObjectId>,
    
    #[schemars(regex = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$")]
    pub email: String,
    
    pub name: String,
    
    #[schemars(range(min = 18, max = 120))]
    pub age: Option<u32>,
    
    pub created_at: Option<mongodb::bson::DateTime>,
}

pub struct UserResource;

impl AdmixResource for UserResource {
    fn new() -> Self { Self }
    
    fn resource_name(&self) -> &'static str { "User" }
    fn base_path(&self) -> &'static str { "users" }
    fn collection_name(&self) -> &'static str { "users" }
    
    fn get_collection(&self) -> Collection<Document> {
        use adminx::utils::database::get_adminx_database;
        get_adminx_database().collection(self.collection_name())
    }
    
    fn clone_box(&self) -> Box<dyn AdmixResource> {
        Box::new(self.clone())
    }
    
    // Specify which fields can be created/updated
    fn permit_keys(&self) -> Vec<&'static str> {
        vec!["email", "name", "age"]
    }
    
    // Define allowed roles
    fn allowed_roles(&self) -> Vec<String> {
        vec!["admin".to_string(), "moderator".to_string()]
    }
    
    // Optional: Custom form structure
    fn form_structure(&self) -> Option<serde_json::Value> {
        Some(serde_json::json!({
            "groups": [
                {
                    "title": "User Details",
                    "fields": [
                        {"name": "name", "label": "Full Name", "type": "text", "required": true},
                        {"name": "email", "label": "Email Address", "type": "email", "required": true},
                        {"name": "age", "label": "Age", "type": "number", "required": false}
                    ]
                }
            ]
        }))
    }
}

2. Set up Your Application

use actix_web::{web, App, HttpServer, middleware::Logger};
use adminx::prelude::*;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    // Load configuration
    let config = get_adminx_config();
    setup_adminx_logging(&config);
    
    // Connect to MongoDB
    let client = mongodb::Client::with_uri_str("mongodb://localhost:27017").await.unwrap();
    let database = client.database("adminx_demo");
    
    // Initialize AdminX
    adminx_initialize(database.clone()).await.unwrap();
    
    // Register your resources
    adminx::register_resource(Box::new(UserResource::new()));
    
    // Create admin user (optional)
    use adminx::utils::auth::{initiate_auth, NewAdminxUser, AdminxStatus};
    let admin_user = NewAdminxUser {
        username: "admin".to_string(),
        email: "admin@example.com".to_string(),
        password: "secure_password".to_string(),
        status: AdminxStatus::Active,
        delete: false,
    };
    initiate_auth(admin_user).await.ok();
    
    println!("๐Ÿš€ Starting AdminX server at http://localhost:8080");
    println!("๐Ÿ“ฑ Admin panel: http://localhost:8080/adminx");
    
    HttpServer::new(move || {
        App::new()
            .app_data(web::Data::new(config.clone()))
            .wrap(get_adminx_session_middleware(&config))
            .wrap(Logger::default())
            .service(register_all_admix_routes())
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

3. Environment Variables

Create a .env file:

JWT_SECRET=your-super-secret-jwt-key-minimum-32-characters
SESSION_SECRET=your-session-secret-key-must-be-at-least-64-characters-long
ENVIRONMENT=development
RUST_LOG=debug

4. Run Your Application

cargo run

Visit http://localhost:8080/adminx and log in with:

  • Email: admin@example.com
  • Password: secure_password

๐Ÿ“– Documentation

Resource Customization

impl AdmixResource for UserResource {
    // ... basic implementation ...
    
    // Custom validation before create
    fn create(&self, req: &HttpRequest, mut payload: Value) -> BoxFuture<'static, HttpResponse> {
        let collection = self.get_collection();
        
        Box::pin(async move {
            // Custom validation
            if let Some(email) = payload.get("email").and_then(|e| e.as_str()) {
                if email.is_empty() {
                    return HttpResponse::BadRequest().json(json!({
                        "error": "Email is required"
                    }));
                }
            }
            
            // Add timestamp
            payload["created_at"] = json!(mongodb::bson::DateTime::now());
            
            // Call default create logic or implement custom logic
            // ... your custom create logic here
        })
    }
    
    // Custom search filters
    fn filters(&self) -> Option<Value> {
        Some(json!({
            "filters": [
                {"field": "name", "label": "Name", "type": "text"},
                {"field": "email", "label": "Email", "type": "text"},
                {"field": "age", "label": "Age", "type": "range"}
            ]
        }))
    }
    
    // Custom actions
    fn custom_actions(&self) -> Vec<CustomAction> {
        vec![
            CustomAction {
                name: "activate",
                method: "POST",
                handler: |_req, path, _body| {
                    Box::pin(async move {
                        let id = path.into_inner();
                        // Custom activation logic
                        HttpResponse::Ok().json(json!({
                            "message": format!("User {} activated", id)
                        }))
                    })
                }
            }
        ]
    }
}

Advanced Configuration

// Custom middleware and advanced setup
use adminx::middleware::RoleGuardMiddleware;

HttpServer::new(move || {
    App::new()
        .app_data(web::Data::new(config.clone()))
        .wrap(get_adminx_session_middleware(&config))
        .wrap(Logger::default())
        // Custom routes before AdminX
        .route("/api/health", web::get().to(health_check))
        // AdminX routes with custom middleware
        .service(
            web::scope("/admin")
                .wrap(RoleGuard::admin_only())
                .service(register_all_admix_routes())
        )
        // Your app routes
        .service(web::scope("/api").service(your_api_routes()))
})

Cli Configuration

Use environment variables

export MONGODB_URL="mongodb://localhost:27017"
export ADMINX_DB_NAME="adminx"
adminx create -u admin -e admin@example.com -y

Use command line arguments

adminx --mongodb-url "mongodb://localhost:27017" --database-name "adminx" list

Interactive mode (will prompt for connection details)

adminx create -u newuser -e user@example.com

Quick setup with defaults (localhost:27017, database: adminx)

adminx --mongodb-url "mongodb+srv://username:password@mongo-atlas-cluster.mongodb.net/?retryWrites=true&w=majority&appName=cluster-name" --database-name "dbname" create -u admin -e admin@srotas.space -p password -y

๐ŸŽฏ Examples

Check out the examples/ directory for complete working examples:

๐Ÿ”ง Available Features

Resource Trait Methods

Method Purpose Required
resource_name() Display name โœ…
base_path() URL path segment โœ…
collection_name() MongoDB collection โœ…
get_collection() Database connection โœ…
clone_box() Resource cloning โœ…
permit_params() Allowed fields โšช
allowed_roles() RBAC permissions โšช
form_structure() Custom forms โšช
list_structure() Table customization โšช
custom_actions() Additional endpoints โšช

Built-in Routes

Each registered resource automatically gets:

Route Method Purpose
/adminx/{resource}/list GET List view (HTML)
/adminx/{resource}/new GET Create form (HTML)
/adminx/{resource}/view/{id} GET Detail view (HTML)
/adminx/{resource}/edit/{id} GET Edit form (HTML)
/adminx/{resource}/create POST Create handler
/adminx/{resource}/update/{id} POST Update handler
/adminx/{resource} GET List API (JSON)
/adminx/{resource} POST Create API (JSON)
/adminx/{resource}/{id} GET Get API (JSON)
/adminx/{resource}/{id} PUT Update API (JSON)
/adminx/{resource}/{id} DELETE Delete API (JSON)

๐Ÿ”’ Security

AdminX includes comprehensive security features:

Authentication & Authorization

// Role-based access control
fn allowed_roles(&self) -> Vec<String> {
    vec!["admin".to_string(), "moderator".to_string()]
}

// Fine-grained permissions
fn allowed_roles_with_permissions(&self) -> Value {
    json!({
        "admin": ["create", "read", "update", "delete"],
        "moderator": ["create", "read", "update"],
        "user": ["read"]
    })
}

Rate Limiting

Built-in rate limiting protects against brute force attacks:

// Automatic rate limiting in auth controller
// 5 attempts per 15 minutes per email
if is_rate_limited(email, 5, Duration::from_secs(900)) {
    return HttpResponse::TooManyRequests()
        .body("Too many login attempts. Please try again later.");
}

๐ŸŽจ UI Customization

Themes and Styling

AdminX uses TailwindCSS with built-in dark mode support:

<!-- Automatic dark mode toggle in header -->
<div class="flex gap-2">
  <label><input type="radio" name="theme" value="light" onchange="setTheme(this.value)" /> Light</label>
  <label><input type="radio" name="theme" value="dark" onchange="setTheme(this.value)" /> Dark</label>
</div>

Custom Templates

Override default templates by providing your own:

// Custom template helper
pub async fn render_custom_template(template_name: &str, ctx: Context) -> HttpResponse {
    // Your custom template logic
}

๐Ÿงช Testing

AdminX includes comprehensive test utilities:

#[cfg(test)]
mod tests {
    use super::*;
    use adminx::test_utils::*;
    
    #[tokio::test]
    async fn test_user_resource_crud() {
        let resource = UserResource::new();
        let test_db = setup_test_database().await;
        
        // Test create
        let user_data = json!({
            "name": "Test User",
            "email": "test@example.com",
            "age": 25
        });
        
        let response = resource.create(&test_request(), user_data).await;
        assert!(response.status().is_success());
        
        // Test list
        let response = resource.list(&test_request(), "".to_string()).await;
        assert!(response.status().is_success());
    }
}

๐Ÿ“Š Performance

Database Optimization

// Automatic indexing for common queries
impl AdmixResource for UserResource {
    async fn setup_indexes(&self) -> Result<(), mongodb::error::Error> {
        let collection = self.get_collection();
        
        collection.create_index(
            mongodb::IndexModel::builder()
                .keys(doc! { "email": 1 })
                .options(mongodb::options::IndexOptions::builder()
                    .unique(true)
                    .build())
                .build(),
            None
        ).await?;
        
        Ok(())
    }
}

Caching

// Built-in response caching (optional)
fn cache_duration(&self) -> Option<Duration> {
    Some(Duration::from_secs(300)) // 5 minutes
}

๐Ÿค Contributing

We welcome contributions! Please see CONTRIBUTING.md for guidelines.

Development Setup

git clone https://github.com/snmmaurya/adminx.git
cd adminx
cargo build
cargo test

Running Examples

cd examples/basic-crud
cargo run

๐Ÿ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.


๐Ÿ™ Acknowledgments

  • Built with Actix Web - Fast, powerful web framework
  • UI powered by TailwindCSS - Utility-first CSS framework
  • Templates with Tera - Jinja2-inspired template engine
  • Database with MongoDB - Document database
  • Schemas with Schemars - JSON Schema generation

๐Ÿ“ž Support


Made with โค๏ธ by the Rustacean360 Team

๐Ÿ‘ฅ Contributors

GitHub stars

Commit count: 18

cargo fmt