Implement real code, remove dead code

- AppState now uses BotServerClient directly
- BOTSERVER_URL env var support for configuration
- index() handler properly integrated into router
- Removed unused web module (DTOs were never used)
- Removed all #[allow(dead_code)] attributes
- Zero warnings, cargo audit clean
This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2025-12-04 09:33:31 -03:00
parent 38fc25b2a2
commit 0143ad49b1
7 changed files with 109 additions and 226 deletions

View file

@ -8,25 +8,9 @@
//! For desktop/mobile native features, see the `botapp` crate which
//! wraps this pure web UI with Tauri.
// Re-export common types from botlib
pub use botlib::{
branding, error, init_branding, is_white_label, platform_name, platform_short, ApiResponse,
BotError, BotResponse, BotResult, MessageType, Session, Suggestion, UserMessage,
};
// HTTP client is always available via botlib
pub use botlib::BotServerClient;
pub mod shared;
#[cfg(feature = "ui-server")]
pub mod ui_server;
#[cfg(feature = "ui-server")]
pub mod web;
// Re-exports
pub use shared::*;
#[cfg(feature = "ui-server")]
pub use ui_server::*;
// Re-export commonly used types
pub use shared::AppState;
pub use ui_server::configure_router;

View file

@ -7,7 +7,6 @@ use log::info;
mod shared;
mod ui_server;
mod web;
#[tokio::main]
async fn main() -> std::io::Result<()> {

View file

@ -1,10 +1,8 @@
//! Shared types and state management for BotUI
//!
//! This module re-exports common types from botlib and provides
//! UI-specific shared functionality.
//! This module provides shared application state and utilities
//! used across the UI server.
pub mod state;
// Re-export from botlib for convenience
// Local re-exports
pub use state::AppState;

View file

@ -1,48 +1,33 @@
//! Application state management
//!
//! This module contains the shared application state that is passed to all
//! route handlers and provides access to database connections, configuration,
//! and other shared resources.
#![allow(dead_code)] // Prepared for future use
//! route handlers and provides access to the BotServer client.
use botlib::http_client::BotServerClient;
use std::sync::Arc;
use tokio::sync::RwLock;
/// Database connection pool type
/// This would typically be a real connection pool in production
pub type DbPool = Arc<RwLock<()>>;
/// Application state shared across all handlers
#[derive(Clone)]
pub struct AppState {
/// Database connection pool
pub conn: Arc<std::sync::Mutex<()>>,
/// Configuration cache
pub config: Arc<RwLock<std::collections::HashMap<String, String>>>,
/// Session store
pub sessions: Arc<RwLock<std::collections::HashMap<String, Session>>>,
}
/// User session information
#[derive(Clone, Debug)]
pub struct Session {
pub user_id: String,
pub username: String,
pub email: String,
pub created_at: chrono::DateTime<chrono::Utc>,
pub expires_at: chrono::DateTime<chrono::Utc>,
/// HTTP client for communicating with BotServer
pub client: Arc<BotServerClient>,
}
impl AppState {
/// Create a new application state
///
/// Uses BOTSERVER_URL environment variable if set, otherwise defaults to localhost:8080
pub fn new() -> Self {
let url = std::env::var("BOTSERVER_URL").ok();
Self {
conn: Arc::new(std::sync::Mutex::new(())),
config: Arc::new(RwLock::new(std::collections::HashMap::new())),
sessions: Arc::new(RwLock::new(std::collections::HashMap::new())),
client: Arc::new(BotServerClient::new(url)),
}
}
/// Check if the BotServer is healthy
pub async fn health_check(&self) -> bool {
self.client.health_check().await
}
}
impl Default for AppState {

View file

@ -2,8 +2,6 @@
//!
//! Serves the web UI (suite, minimal) and handles API proxying.
#![allow(dead_code)] // Some functions prepared for future use
use axum::{
extract::State,
http::StatusCode,
@ -12,17 +10,16 @@ use axum::{
Router,
};
use log::error;
use std::{fs, path::PathBuf, sync::Arc};
use tower_http::services::ServeDir;
use std::{fs, path::PathBuf};
use botlib::http_client::BotServerClient;
use crate::shared::AppState;
// Serve minimal UI (default at /)
/// Serve the index page (minimal UI)
pub async fn index() -> impl IntoResponse {
serve_minimal().await
}
// Handler for minimal UI
/// Handler for minimal UI
pub async fn serve_minimal() -> impl IntoResponse {
match fs::read_to_string("ui/minimal/index.html") {
Ok(html) => (StatusCode::OK, [("content-type", "text/html")], Html(html)),
@ -37,7 +34,7 @@ pub async fn serve_minimal() -> impl IntoResponse {
}
}
// Handler for suite UI
/// Handler for suite UI
pub async fn serve_suite() -> impl IntoResponse {
match fs::read_to_string("ui/suite/index.html") {
Ok(html) => (StatusCode::OK, [("content-type", "text/html")], Html(html)),
@ -52,49 +49,9 @@ pub async fn serve_suite() -> impl IntoResponse {
}
}
pub fn configure_router() -> Router {
let suite_path = PathBuf::from("./ui/suite");
let minimal_path = PathBuf::from("./ui/minimal");
let client = Arc::new(BotServerClient::new(None));
Router::new()
// API health check
.route("/health", get(health))
.route("/api/health", get(api_health))
// Default route serves minimal UI
.route("/", get(root))
.route("/minimal", get(serve_minimal))
// Suite UI route
.route("/suite", get(serve_suite))
// Suite static assets (when accessing /suite/*)
.nest_service("/suite/js", ServeDir::new(suite_path.join("js")))
.nest_service("/suite/css", ServeDir::new(suite_path.join("css")))
.nest_service("/suite/public", ServeDir::new(suite_path.join("public")))
.nest_service("/suite/drive", ServeDir::new(suite_path.join("drive")))
.nest_service("/suite/chat", ServeDir::new(suite_path.join("chat")))
.nest_service("/suite/mail", ServeDir::new(suite_path.join("mail")))
.nest_service("/suite/tasks", ServeDir::new(suite_path.join("tasks")))
// Legacy paths for backward compatibility (serve suite assets)
.nest_service("/js", ServeDir::new(suite_path.join("js")))
.nest_service("/css", ServeDir::new(suite_path.join("css")))
.nest_service("/public", ServeDir::new(suite_path.join("public")))
.nest_service("/drive", ServeDir::new(suite_path.join("drive")))
.nest_service("/chat", ServeDir::new(suite_path.join("chat")))
.nest_service("/mail", ServeDir::new(suite_path.join("mail")))
.nest_service("/tasks", ServeDir::new(suite_path.join("tasks")))
// Fallback for other static files
.fallback_service(
ServeDir::new(minimal_path.clone()).fallback(
ServeDir::new(minimal_path.clone()).append_index_html_on_directories(true),
),
)
.with_state(client)
}
async fn health(
State(client): State<Arc<BotServerClient>>,
) -> (StatusCode, axum::Json<serde_json::Value>) {
match client.health_check().await {
/// Health check endpoint - checks BotServer connectivity
async fn health(State(state): State<AppState>) -> (StatusCode, axum::Json<serde_json::Value>) {
match state.health_check().await {
true => (
StatusCode::OK,
axum::Json(serde_json::json!({
@ -114,25 +71,95 @@ async fn health(
}
}
/// API health check endpoint
async fn api_health() -> (StatusCode, axum::Json<serde_json::Value>) {
(
StatusCode::OK,
axum::Json(serde_json::json!({
"status": "ok",
"version": "1.0.0"
"version": env!("CARGO_PKG_VERSION")
})),
)
}
async fn root() -> axum::Json<serde_json::Value> {
axum::Json(serde_json::json!({
"service": "BotUI",
"version": "1.0.0",
"description": "General Bots User Interface",
"endpoints": {
"health": "/health",
"api": "/api/health",
"ui": "/"
}
}))
/// Configure and return the main router
pub fn configure_router() -> Router {
let suite_path = PathBuf::from("./ui/suite");
let minimal_path = PathBuf::from("./ui/minimal");
let state = AppState::new();
Router::new()
// Health check endpoints
.route("/health", get(health))
.route("/api/health", get(api_health))
// UI routes
.route("/", get(index))
.route("/minimal", get(serve_minimal))
.route("/suite", get(serve_suite))
// Suite static assets (when accessing /suite/*)
.nest_service(
"/suite/js",
tower_http::services::ServeDir::new(suite_path.join("js")),
)
.nest_service(
"/suite/css",
tower_http::services::ServeDir::new(suite_path.join("css")),
)
.nest_service(
"/suite/public",
tower_http::services::ServeDir::new(suite_path.join("public")),
)
.nest_service(
"/suite/drive",
tower_http::services::ServeDir::new(suite_path.join("drive")),
)
.nest_service(
"/suite/chat",
tower_http::services::ServeDir::new(suite_path.join("chat")),
)
.nest_service(
"/suite/mail",
tower_http::services::ServeDir::new(suite_path.join("mail")),
)
.nest_service(
"/suite/tasks",
tower_http::services::ServeDir::new(suite_path.join("tasks")),
)
// Legacy paths for backward compatibility (serve suite assets)
.nest_service(
"/js",
tower_http::services::ServeDir::new(suite_path.join("js")),
)
.nest_service(
"/css",
tower_http::services::ServeDir::new(suite_path.join("css")),
)
.nest_service(
"/public",
tower_http::services::ServeDir::new(suite_path.join("public")),
)
.nest_service(
"/drive",
tower_http::services::ServeDir::new(suite_path.join("drive")),
)
.nest_service(
"/chat",
tower_http::services::ServeDir::new(suite_path.join("chat")),
)
.nest_service(
"/mail",
tower_http::services::ServeDir::new(suite_path.join("mail")),
)
.nest_service(
"/tasks",
tower_http::services::ServeDir::new(suite_path.join("tasks")),
)
// Fallback for other static files
.fallback_service(
tower_http::services::ServeDir::new(minimal_path.clone()).fallback(
tower_http::services::ServeDir::new(minimal_path)
.append_index_html_on_directories(true),
),
)
.with_state(state)
}

View file

@ -1,56 +0,0 @@
#![cfg(not(feature = "desktop"))]
use axum::{extract::State, http::StatusCode, Json};
use serde_json::json;
use std::sync::Arc;
use crate::http_client::BotServerClient;
/// Health check endpoint
pub async fn health(
State(client): State<Arc<BotServerClient>>,
) -> (StatusCode, Json<serde_json::Value>) {
match client.health_check().await {
true => (
StatusCode::OK,
Json(json!({
"status": "healthy",
"service": "botui",
"mode": "web"
})),
),
false => (
StatusCode::SERVICE_UNAVAILABLE,
Json(json!({
"status": "unhealthy",
"service": "botui",
"error": "botserver unreachable"
})),
),
}
}
/// API health check endpoint
pub async fn api_health() -> (StatusCode, Json<serde_json::Value>) {
(
StatusCode::OK,
Json(json!({
"status": "ok",
"version": "1.0.0"
})),
)
}
/// Root endpoint
pub async fn root() -> Json<serde_json::Value> {
Json(json!({
"service": "BotUI",
"version": "1.0.0",
"description": "General Bots User Interface",
"endpoints": {
"health": "/health",
"api": "/api/health",
"ui": "/"
}
}))
}

View file

@ -1,54 +0,0 @@
//! Web module with basic data structures
//!
//! Contains DTOs and types for the web API layer.
#![allow(dead_code)] // DTOs prepared for future use
use serde::{Deserialize, Serialize};
/// Request/Response DTOs for web API
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Message {
pub id: String,
pub session_id: String,
pub sender: String,
pub content: String,
pub timestamp: String,
pub is_user: bool,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct ScanRequest {
pub bot_id: Option<String>,
pub include_info: bool,
}
#[derive(Debug, Serialize)]
pub struct IssueResponse {
pub id: String,
pub severity: String,
pub issue_type: String,
pub title: String,
pub description: String,
pub file_path: String,
pub line_number: Option<usize>,
pub code_snippet: Option<String>,
pub remediation: String,
pub category: String,
}
#[derive(Debug, Serialize)]
pub struct ScanSummary {
pub total_issues: usize,
pub critical_count: usize,
pub high_count: usize,
pub total_files_scanned: usize,
pub compliance_score: f64,
}
#[derive(Debug, Serialize)]
pub struct ScanResponse {
pub scan_id: String,
pub issues: Vec<IssueResponse>,
pub summary: ScanSummary,
}