hammerwork_web/lib.rs
1//! # Hammerwork Web Dashboard
2//!
3//! A web-based admin dashboard for monitoring and managing Hammerwork job queues.
4//!
5//! This crate provides a comprehensive web interface for:
6//! - Real-time queue monitoring and statistics
7//! - Job management (retry, cancel, inspect)
8//! - Worker status and utilization
9//! - Dead job analysis and bulk operations
10//! - System health monitoring
11//!
12//! ## Usage
13//!
14//! ### As a Binary
15//!
16//! ```bash
17//! # Install the web dashboard
18//! cargo install hammerwork-web --features postgres
19//!
20//! # Start the dashboard
21//! hammerwork-web --database-url postgresql://localhost/hammerwork --bind 0.0.0.0:8080
22//! ```
23//!
24//! ### As a Library
25//!
26//! #### Basic Usage
27//!
28//! ```rust,no_run
29//! use hammerwork_web::{WebDashboard, DashboardConfig};
30//!
31//! #[tokio::main]
32//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
33//! let config = DashboardConfig {
34//! bind_address: "127.0.0.1".to_string(),
35//! port: 8080,
36//! database_url: "postgresql://localhost/hammerwork".to_string(),
37//! ..Default::default()
38//! };
39//!
40//! let dashboard = WebDashboard::new(config).await?;
41//! dashboard.start().await?;
42//!
43//! Ok(())
44//! }
45//! ```
46//!
47//! #### Builder Pattern Configuration
48//!
49//! ```rust
50//! use hammerwork_web::DashboardConfig;
51//! use std::time::Duration;
52//!
53//! let config = DashboardConfig::new()
54//! .with_bind_address("0.0.0.0", 9090)
55//! .with_database_url("postgresql://localhost/hammerwork")
56//! .with_auth("admin", "bcrypt_hash_here")
57//! .with_cors(true);
58//!
59//! assert_eq!(config.bind_addr(), "0.0.0.0:9090");
60//! assert_eq!(config.database_url, "postgresql://localhost/hammerwork");
61//! assert!(config.auth.enabled);
62//! assert!(config.enable_cors);
63//! ```
64//!
65//! #### Configuration from File
66//!
67//! ```rust,no_run
68//! use hammerwork_web::DashboardConfig;
69//!
70//! // Load from TOML file
71//! let config = DashboardConfig::from_file("dashboard.toml")?;
72//!
73//! // Save configuration
74//! config.save_to_file("dashboard.toml")?;
75//! # Ok::<(), Box<dyn std::error::Error>>(())
76//! ```
77//!
78//! #### Authentication Configuration
79//!
80//! ```rust
81//! use hammerwork_web::AuthConfig;
82//! use std::time::Duration;
83//!
84//! let auth_config = AuthConfig {
85//! enabled: true,
86//! username: "admin".to_string(),
87//! password_hash: "$2b$12$hash...".to_string(),
88//! session_timeout: Duration::from_secs(8 * 60 * 60), // 8 hours
89//! max_failed_attempts: 5,
90//! lockout_duration: Duration::from_secs(15 * 60), // 15 minutes
91//! };
92//!
93//! assert!(auth_config.enabled);
94//! assert_eq!(auth_config.max_failed_attempts, 5);
95//! ```
96
97pub mod api;
98pub mod auth;
99pub mod config;
100pub mod server;
101pub mod websocket;
102
103pub use config::{AuthConfig, DashboardConfig};
104pub use server::WebDashboard;
105
106/// Result type alias for consistent error handling
107pub type Result<T> = std::result::Result<T, anyhow::Error>;
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112 use tempfile::tempdir;
113
114 #[test]
115 fn test_dashboard_config_creation() {
116 let config = DashboardConfig::new()
117 .with_bind_address("0.0.0.0", 3000)
118 .with_database_url("postgresql://localhost/test")
119 .with_cors(true);
120
121 assert_eq!(config.bind_addr(), "0.0.0.0:3000");
122 assert_eq!(config.database_url, "postgresql://localhost/test");
123 assert!(config.enable_cors);
124 }
125
126 #[test]
127 fn test_auth_config_security_defaults() {
128 let auth_config = AuthConfig::default();
129
130 // Ensure secure defaults
131 assert!(
132 auth_config.enabled,
133 "Authentication should be enabled by default"
134 );
135 assert_eq!(auth_config.username, "admin");
136 assert_eq!(auth_config.max_failed_attempts, 5);
137 assert!(auth_config.lockout_duration.as_secs() > 0);
138 }
139
140 #[tokio::test]
141 async fn test_dashboard_creation_with_invalid_config() {
142 let temp_dir = tempdir().unwrap();
143
144 let config = DashboardConfig {
145 database_url: "invalid://url".to_string(),
146 static_dir: temp_dir.path().to_path_buf(),
147 ..Default::default()
148 };
149
150 // Dashboard creation should succeed, but starting would fail with invalid URL
151 let result = WebDashboard::new(config).await;
152 assert!(
153 result.is_ok(),
154 "Dashboard creation should succeed, connection validation happens later"
155 );
156 }
157
158 #[test]
159 fn test_config_builder_pattern() {
160 let temp_dir = tempdir().unwrap();
161
162 let config = DashboardConfig::new()
163 .with_bind_address("192.168.1.1", 8888)
164 .with_database_url("mysql://root:pass@localhost/db")
165 .with_static_dir(temp_dir.path().to_path_buf())
166 .with_auth("user", "hash")
167 .with_cors(false);
168
169 assert_eq!(config.bind_address, "192.168.1.1");
170 assert_eq!(config.port, 8888);
171 assert_eq!(config.database_url, "mysql://root:pass@localhost/db");
172 assert!(config.auth.enabled);
173 assert_eq!(config.auth.username, "user");
174 assert!(!config.enable_cors);
175 }
176}