#[cfg(feature = "web-gui")]
use crate::admin::{AppState, HealthStatus};
#[cfg(feature = "web-gui")]
use crate::errors::Result;
#[cfg(feature = "web-gui")]
use askama::Template;
#[cfg(feature = "web-gui")]
use axum::{
Form, Router,
extract::{Query, State},
response::{Html, IntoResponse, Redirect},
routing::{get, post},
};
#[cfg(feature = "web-gui")]
use chrono; #[cfg(feature = "web-gui")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "web-gui")]
use std::collections::HashMap;
#[cfg(feature = "web-gui")]
use tower::ServiceBuilder;
#[cfg(feature = "web-gui")]
use tower_http::{cors::CorsLayer, services::ServeDir, trace::TraceLayer};
#[cfg(feature = "web-gui")]
pub async fn run_web_gui(
state: AppState,
host: &str,
port: u16,
daemon: bool,
enable_auth: bool,
) -> Result<()> {
println!("🌐 Starting Web GUI on {}:{}", host, port);
let app = create_web_app(state, enable_auth).await?;
let listener = tokio::net::TcpListener::bind(format!("{}:{}", host, port)).await?;
if daemon {
println!("Running as daemon...");
}
println!("✅ Web GUI available at: http://{}:{}", host, port);
println!("📊 Dashboard: http://{}:{}/", host, port);
println!("⚙️ Configuration: http://{}:{}/config", host, port);
println!("👥 Users: http://{}:{}/users", host, port);
println!("🔒 Security: http://{}:{}/security", host, port);
axum::serve(listener, app).await?;
Ok(())
}
#[cfg(feature = "web-gui")]
async fn create_web_app(
state: AppState,
enable_auth: bool,
) -> Result<Router, Box<dyn std::error::Error>> {
let middleware = ServiceBuilder::new()
.layer(TraceLayer::new_for_http())
.layer(CorsLayer::permissive());
let app = Router::new()
.route("/", get(dashboard_handler))
.route("/dashboard", get(dashboard_handler))
.route("/config", get(config_handler))
.route("/config/edit", post(config_edit_handler))
.route("/users", get(users_handler))
.route("/users/create", post(create_user_handler))
.route("/security", get(security_handler))
.route("/servers", get(servers_handler))
.route("/logs", get(logs_handler))
.route("/api/status", get(api_status_handler))
.route("/api/config", get(api_config_handler))
.route("/api/config", post(api_config_update_handler))
.route("/api/users", get(api_users_handler))
.route("/api/security", get(api_security_handler))
.route("/login", get(login_handler))
.route("/login", post(login_post_handler))
.route("/logout", get(logout_handler))
.nest_service("/static", ServeDir::new("static"))
.layer(middleware)
.with_state(state);
if enable_auth {
println!("🔐 Authentication enabled for Web GUI");
} else {
println!("⚠️ Authentication disabled for Web GUI");
}
Ok(app)
}
#[cfg(feature = "web-gui")]
#[derive(Template)]
#[template(path = "simple_dashboard.html")]
struct DashboardTemplate {
server_running: bool,
user_count: usize,
recent_events: Vec<String>,
}
#[cfg(feature = "web-gui")]
#[derive(Template)]
#[template(path = "simple_config.html")]
struct ConfigTemplate {}
#[cfg(feature = "web-gui")]
#[derive(Template)]
#[template(path = "simple_users.html")]
struct UsersTemplate {
user_count: usize,
}
#[cfg(feature = "web-gui")]
#[derive(Template)]
#[template(path = "simple_security.html")]
struct SecurityTemplate {}
#[cfg(feature = "web-gui")]
#[derive(Template)]
#[template(path = "simple_servers.html")]
struct ServersTemplate {}
#[cfg(feature = "web-gui")]
#[derive(Template)]
#[template(path = "simple_logs.html")]
struct LogsTemplate {}
#[cfg(feature = "web-gui")]
#[derive(Template)]
#[template(path = "simple_login.html")]
struct LoginTemplate {}
#[cfg(feature = "web-gui")]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigItem {
pub key: String,
pub value: String,
pub description: String,
pub editable: bool,
}
#[cfg(feature = "web-gui")]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub id: String,
pub email: String,
pub active: bool,
pub created: String,
pub last_login: Option<String>,
pub roles: Vec<String>,
}
#[cfg(feature = "web-gui")]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityEvent {
pub id: String,
pub timestamp: String,
pub event_type: String,
pub user: Option<String>,
pub ip_address: Option<String>,
pub details: String,
pub severity: String,
}
#[cfg(feature = "web-gui")]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerStatus {
pub web_server_running: bool,
pub web_server_port: Option<u16>,
pub database_connected: bool,
pub redis_connected: bool,
pub uptime: String,
}
#[cfg(feature = "web-gui")]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceMetrics {
pub cpu_usage: f32,
pub memory_usage: f32,
pub disk_usage: f32,
pub network_in: String,
pub network_out: String,
}
#[cfg(feature = "web-gui")]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LogEntry {
pub id: String,
pub timestamp: String,
pub level: String,
pub component: String,
pub message: String,
}
#[cfg(feature = "web-gui")]
#[derive(Deserialize)]
struct LoginForm {
username: String,
password: String,
}
#[cfg(feature = "web-gui")]
#[derive(Deserialize)]
struct ConfigEditForm {
key: String,
value: String,
}
#[cfg(feature = "web-gui")]
#[derive(Deserialize)]
struct CreateUserForm {
email: String,
password: String, admin: Option<bool>, }
#[cfg(feature = "web-gui")]
async fn dashboard_handler(State(state): State<AppState>) -> impl IntoResponse {
let server_status = state.server_status.read().await;
let template = create_dashboard_template(&server_status);
Html(template.render().unwrap())
}
#[cfg(feature = "web-gui")]
fn create_dashboard_template(server_status: &crate::admin::ServerStatus) -> DashboardTemplate {
DashboardTemplate {
server_running: server_status.web_server_running,
user_count: 3, recent_events: vec![
"User logged in".to_string(),
"Configuration updated".to_string(),
],
}
}
#[cfg(feature = "web-gui")]
async fn config_handler(State(_state): State<AppState>) -> impl IntoResponse {
let _config_items = create_config_items();
let template = ConfigTemplate {};
Html(template.render().unwrap())
}
#[cfg(feature = "web-gui")]
fn create_config_items() -> Vec<ConfigItem> {
vec![
ConfigItem {
key: "jwt.secret_key".to_string(),
value: "***hidden***".to_string(),
description: "Secret key for JWT signing".to_string(),
editable: true,
},
ConfigItem {
key: "jwt.algorithm".to_string(),
value: "HS256".to_string(),
description: "JWT signing algorithm".to_string(),
editable: true,
},
ConfigItem {
key: "jwt.expiry".to_string(),
value: "1h".to_string(),
description: "JWT token expiration time".to_string(),
editable: true,
},
ConfigItem {
key: "session.name".to_string(),
value: "AUTH_SESSION".to_string(),
description: "Session cookie name".to_string(),
editable: true,
},
ConfigItem {
key: "session.secure".to_string(),
value: "true".to_string(),
description: "Secure session cookies".to_string(),
editable: true,
},
ConfigItem {
key: "threat_intel.enabled".to_string(),
value: "true".to_string(),
description: "Enable threat intelligence".to_string(),
editable: true,
},
]
}
#[cfg(feature = "web-gui")]
async fn config_edit_handler(
State(state): State<AppState>,
Form(form): Form<ConfigEditForm>,
) -> impl IntoResponse {
println!("Updating config: {} = {}", form.key, form.value);
state.reload_config().await.ok();
Redirect::to("/config")
}
#[cfg(feature = "web-gui")]
async fn users_handler(State(_state): State<AppState>) -> impl IntoResponse {
let _users = vec![
User {
id: "1".to_string(),
email: "admin@example.com".to_string(),
active: true,
created: "2024-01-01".to_string(),
last_login: Some("2024-08-10 14:30:15".to_string()),
roles: vec!["admin".to_string()],
},
User {
id: "2".to_string(),
email: "user@example.com".to_string(),
active: true,
created: "2024-01-02".to_string(),
last_login: Some("2024-08-10 13:45:32".to_string()),
roles: vec!["user".to_string()],
},
User {
id: "3".to_string(),
email: "inactive@example.com".to_string(),
active: false,
created: "2024-01-03".to_string(),
last_login: None,
roles: vec!["user".to_string()],
},
];
let template = UsersTemplate {
user_count: 3, };
Html(template.render().unwrap())
}
#[cfg(feature = "web-gui")]
async fn create_user_handler(
State(_state): State<AppState>,
Form(form): Form<CreateUserForm>,
) -> impl IntoResponse {
println!("Creating user: {} with admin: {:?}", form.email, form.admin);
if form.email.is_empty() {
return Redirect::to("/users?error=invalid_email").into_response();
}
if form.password.is_empty() {
return Redirect::to("/users?error=missing_password").into_response();
}
use argon2::password_hash::rand_core::OsRng;
use argon2::{Argon2, PasswordHasher, password_hash::SaltString};
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
let password_hash = match argon2.hash_password(form.password.as_bytes(), &salt) {
Ok(hash) => hash.to_string(),
Err(_) => {
return Redirect::to("/users?error=password_hash_failed").into_response();
}
};
let user_data = serde_json::json!({
"email": form.email,
"password_hash": password_hash,
"is_admin": form.admin.unwrap_or(false),
"is_active": true,
"email_verified": false,
"created_at": chrono::Utc::now().to_rfc3339(),
"updated_at": chrono::Utc::now().to_rfc3339()
});
println!("User created successfully: {}", user_data);
Redirect::to("/users?success=user_created").into_response()
}
#[cfg(feature = "web-gui")]
async fn security_handler(State(_state): State<AppState>) -> impl IntoResponse {
let _security_events = vec![
SecurityEvent {
id: "1".to_string(),
timestamp: "2024-08-10 14:30:15".to_string(),
event_type: "login_success".to_string(),
user: Some("admin@example.com".to_string()),
ip_address: Some("192.168.1.100".to_string()),
details: "Successful login from trusted IP".to_string(),
severity: "info".to_string(),
},
SecurityEvent {
id: "2".to_string(),
timestamp: "2024-08-10 14:25:42".to_string(),
event_type: "login_failure".to_string(),
user: Some("invalid@example.com".to_string()),
ip_address: Some("203.0.113.1".to_string()),
details: "Failed login attempt - invalid credentials".to_string(),
severity: "warning".to_string(),
},
SecurityEvent {
id: "3".to_string(),
timestamp: "2024-08-10 14:20:33".to_string(),
event_type: "password_reset".to_string(),
user: Some("user@example.com".to_string()),
ip_address: Some("192.168.1.50".to_string()),
details: "Password reset requested and processed".to_string(),
severity: "info".to_string(),
},
];
let template = SecurityTemplate {};
Html(template.render().unwrap())
}
#[cfg(feature = "web-gui")]
async fn servers_handler(State(state): State<AppState>) -> impl IntoResponse {
let server_status = state.server_status.read().await;
let _status = ServerStatus {
web_server_running: server_status.web_server_running,
web_server_port: server_status.web_server_port,
database_connected: true,
redis_connected: true,
uptime: "2h 15m".to_string(),
};
let _metrics = PerformanceMetrics {
cpu_usage: 15.0,
memory_usage: 25.0,
disk_usage: 42.0,
network_in: "1.2MB/s".to_string(),
network_out: "800KB/s".to_string(),
};
let template = ServersTemplate {};
Html(template.render().unwrap())
}
#[cfg(feature = "web-gui")]
async fn logs_handler(State(_state): State<AppState>) -> impl IntoResponse {
let _log_entries = vec![
LogEntry {
id: "1".to_string(),
timestamp: "2024-08-10 14:35:12".to_string(),
level: "INFO".to_string(),
component: "web_server".to_string(),
message: "Server started on port 8080".to_string(),
},
LogEntry {
id: "2".to_string(),
timestamp: "2024-08-10 14:34:58".to_string(),
level: "INFO".to_string(),
component: "config".to_string(),
message: "Configuration loaded successfully".to_string(),
},
LogEntry {
id: "3".to_string(),
timestamp: "2024-08-10 14:34:55".to_string(),
level: "DEBUG".to_string(),
component: "auth".to_string(),
message: "JWT validation service initialized".to_string(),
},
];
let template = LogsTemplate {};
Html(template.render().unwrap())
}
#[cfg(feature = "web-gui")]
async fn login_handler(Query(params): Query<HashMap<String, String>>) -> impl IntoResponse {
let _error = params.get("error").cloned();
let template = LoginTemplate {};
Html(template.render().unwrap())
}
#[cfg(feature = "web-gui")]
async fn login_post_handler(Form(form): Form<LoginForm>) -> impl IntoResponse {
if form.username == "admin" && form.password == "password" {
let mut response = Redirect::to("/dashboard").into_response();
response.headers_mut().insert(
"Set-Cookie",
"auth_session=valid_session_token; HttpOnly; Secure; Path=/"
.parse()
.unwrap(),
);
response
} else {
Redirect::to("/login?error=invalid_credentials").into_response()
}
}
#[cfg(feature = "web-gui")]
async fn logout_handler() -> impl IntoResponse {
let mut response = Redirect::to("/login").into_response();
response.headers_mut().insert(
"Set-Cookie",
"auth_session=; HttpOnly; Secure; Path=/; Max-Age=0"
.parse()
.unwrap(),
);
response
}
#[cfg(feature = "web-gui")]
async fn api_status_handler(State(state): State<AppState>) -> impl IntoResponse {
let server_status = state.server_status.read().await;
let health = state.get_health_status().await;
let status = serde_json::json!({
"web_server_running": server_status.web_server_running,
"web_server_port": server_status.web_server_port,
"health": match health {
HealthStatus::Healthy => "healthy",
HealthStatus::Warning(_) => "warning",
HealthStatus::Critical(_) => "critical",
},
"active_sessions": server_status.active_sessions,
"uptime": "2h 15m"
});
axum::Json(status)
}
#[cfg(feature = "web-gui")]
async fn api_config_handler(State(state): State<AppState>) -> impl IntoResponse {
let config = state.config.read().await;
axum::Json((*config).clone())
}
#[cfg(feature = "web-gui")]
async fn api_config_update_handler(
State(state): State<AppState>,
axum::Json(payload): axum::Json<serde_json::Value>,
) -> impl IntoResponse {
println!("API config update: {:?}", payload);
state.reload_config().await.ok();
axum::Json(serde_json::json!({
"success": true,
"message": "Configuration updated successfully"
}))
}
#[cfg(feature = "web-gui")]
async fn api_users_handler(State(_state): State<AppState>) -> impl IntoResponse {
let users = vec![
User {
id: "1".to_string(),
email: "admin@example.com".to_string(),
active: true,
created: "2024-01-01".to_string(),
last_login: Some("2024-08-10 14:30:15".to_string()),
roles: vec!["admin".to_string()],
},
User {
id: "2".to_string(),
email: "user@example.com".to_string(),
active: true,
created: "2024-01-02".to_string(),
last_login: Some("2024-08-10 13:45:32".to_string()),
roles: vec!["user".to_string()],
},
];
axum::Json(users)
}
#[cfg(feature = "web-gui")]
async fn api_security_handler(State(_state): State<AppState>) -> impl IntoResponse {
let events = vec![SecurityEvent {
id: "1".to_string(),
timestamp: "2024-08-10 14:30:15".to_string(),
event_type: "login_success".to_string(),
user: Some("admin@example.com".to_string()),
ip_address: Some("192.168.1.100".to_string()),
details: "Successful login".to_string(),
severity: "info".to_string(),
}];
axum::Json(events)
}