use crate::shared::state::AppState; use axum::{ extract::{Query, State}, response::{Html, IntoResponse}, routing::get, Router, }; use serde::{Deserialize, Serialize}; use std::sync::Arc; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SearchQuery { pub q: Option, pub category: Option, } pub fn configure_sources_routes() -> Router> { Router::new() // Tab endpoints - match frontend hx-get endpoints .route("/api/sources/prompts", get(handle_prompts)) .route("/api/sources/templates", get(handle_templates)) .route("/api/sources/news", get(handle_news)) .route("/api/sources/mcp-servers", get(handle_mcp_servers)) .route("/api/sources/llm-tools", get(handle_llm_tools)) .route("/api/sources/models", get(handle_models)) // Search .route("/api/sources/search", get(handle_search)) } /// GET /api/sources/prompts - Prompts tab content pub async fn handle_prompts( State(_state): State>, Query(params): Query, ) -> impl IntoResponse { let category = params.category.unwrap_or_else(|| "all".to_string()); let prompts = get_prompts_data(&category); let mut html = String::new(); html.push_str("
"); // Categories sidebar html.push_str(""); // Prompts grid html.push_str("
"); html.push_str("
"); for prompt in &prompts { html.push_str("
"); html.push_str("
"); html.push_str(""); html.push_str(&prompt.icon); html.push_str(""); html.push_str("

"); html.push_str(&html_escape(&prompt.title)); html.push_str("

"); html.push_str("
"); html.push_str("

"); html.push_str(&html_escape(&prompt.description)); html.push_str("

"); html.push_str("
"); html.push_str(""); html.push_str(&html_escape(&prompt.category)); html.push_str(""); html.push_str(""); html.push_str("
"); html.push_str("
"); } if prompts.is_empty() { html.push_str("
"); html.push_str("

No prompts found in this category

"); html.push_str("
"); } html.push_str("
"); html.push_str("
"); html.push_str("
"); Html(html) } /// GET /api/sources/templates - Templates tab content pub async fn handle_templates(State(_state): State>) -> impl IntoResponse { let templates = get_templates_data(); let mut html = String::new(); html.push_str("
"); html.push_str("
"); html.push_str("

Bot Templates

"); html.push_str("

Pre-built bot configurations ready to deploy

"); html.push_str("
"); html.push_str("
"); for template in &templates { html.push_str("
"); html.push_str("
"); html.push_str(&template.icon); html.push_str("
"); html.push_str("
"); html.push_str("

"); html.push_str(&html_escape(&template.name)); html.push_str("

"); html.push_str("

"); html.push_str(&html_escape(&template.description)); html.push_str("

"); html.push_str("
"); html.push_str(""); html.push_str(&html_escape(&template.category)); html.push_str(""); html.push_str("
"); html.push_str("
"); html.push_str("
"); html.push_str(""); html.push_str(""); html.push_str("
"); html.push_str("
"); } html.push_str("
"); html.push_str("
"); Html(html) } /// GET /api/sources/news - News tab content pub async fn handle_news(State(_state): State>) -> impl IntoResponse { let news_items = vec![ ("🚀", "General Bots 6.0 Released", "Major update with improved performance and new features", "2 hours ago"), ("🔧", "New MCP Server Integration", "Connect to external tools more easily with our new MCP support", "1 day ago"), ("📊", "Analytics Dashboard Update", "Real-time metrics and improved visualizations", "3 days ago"), ("🔒", "Security Enhancement", "Enhanced encryption and authentication options", "1 week ago"), ("🌐", "Multi-language Support", "Now supporting 15+ languages for bot conversations", "2 weeks ago"), ]; let mut html = String::new(); html.push_str("
"); html.push_str("
"); html.push_str("

Latest News

"); html.push_str("

Updates and announcements from the General Bots team

"); html.push_str("
"); html.push_str("
"); for (icon, title, description, time) in &news_items { html.push_str("
"); html.push_str("
"); html.push_str(icon); html.push_str("
"); html.push_str("
"); html.push_str("

"); html.push_str(&html_escape(title)); html.push_str("

"); html.push_str("

"); html.push_str(&html_escape(description)); html.push_str("

"); html.push_str(""); html.push_str(time); html.push_str(""); html.push_str("
"); html.push_str("
"); } html.push_str("
"); html.push_str("
"); Html(html) } /// GET /api/sources/mcp-servers - MCP Servers tab content pub async fn handle_mcp_servers(State(_state): State>) -> impl IntoResponse { let servers = vec![ ("🗄️", "Database Server", "PostgreSQL, MySQL, SQLite connections", "Active", true), ("📁", "Filesystem Server", "Local and cloud file access", "Active", true), ("🌐", "Web Server", "HTTP/REST API integrations", "Active", true), ("📧", "Email Server", "SMTP/IMAP email handling", "Inactive", false), ("💬", "Slack Server", "Slack workspace integration", "Active", true), ("📊", "Analytics Server", "Data processing and reporting", "Active", true), ]; let mut html = String::new(); html.push_str("
"); html.push_str("
"); html.push_str("

MCP Servers

"); html.push_str("

Model Context Protocol servers for extended capabilities

"); html.push_str(""); html.push_str("
"); html.push_str("
"); for (icon, name, description, status, is_active) in &servers { let status_class = if *is_active { "status-active" } else { "status-inactive" }; html.push_str("
"); html.push_str("
"); html.push_str(icon); html.push_str("
"); html.push_str("
"); html.push_str("

"); html.push_str(&html_escape(name)); html.push_str("

"); html.push_str("

"); html.push_str(&html_escape(description)); html.push_str("

"); html.push_str("
"); html.push_str("
"); html.push_str(status); html.push_str("
"); html.push_str("
"); html.push_str(""); if *is_active { html.push_str(""); } else { html.push_str(""); } html.push_str("
"); html.push_str("
"); } html.push_str("
"); html.push_str("
"); Html(html) } /// GET /api/sources/llm-tools - LLM Tools tab content pub async fn handle_llm_tools(State(_state): State>) -> impl IntoResponse { let tools = vec![ ("🔍", "Web Search", "Search the web for real-time information", true), ("🧮", "Calculator", "Perform mathematical calculations", true), ("📅", "Calendar", "Manage calendar events and schedules", true), ("📝", "Note Taking", "Create and manage notes", true), ("🌤️", "Weather", "Get weather forecasts and conditions", false), ("📰", "News Reader", "Fetch and summarize news articles", false), ("🔗", "URL Fetcher", "Retrieve and parse web content", true), ("💾", "Code Executor", "Run code snippets safely", false), ]; let mut html = String::new(); html.push_str("
"); html.push_str("
"); html.push_str("

LLM Tools

"); html.push_str("

Extend your bot's capabilities with these tools

"); html.push_str("
"); html.push_str("
"); for (icon, name, description, enabled) in &tools { let enabled_class = if *enabled { "enabled" } else { "disabled" }; html.push_str("
"); html.push_str("
"); html.push_str(icon); html.push_str("
"); html.push_str("
"); html.push_str("

"); html.push_str(&html_escape(name)); html.push_str("

"); html.push_str("

"); html.push_str(&html_escape(description)); html.push_str("

"); html.push_str("
"); html.push_str(""); html.push_str("
"); } html.push_str("
"); html.push_str("
"); Html(html) } /// GET /api/sources/models - Models tab content pub async fn handle_models(State(_state): State>) -> impl IntoResponse { let models = vec![ ("🧠", "GPT-4o", "OpenAI", "Latest multimodal model with vision capabilities", "Active"), ("🧠", "GPT-4o-mini", "OpenAI", "Fast and efficient for most tasks", "Active"), ("🦙", "Llama 3.1 70B", "Meta", "Open source large language model", "Available"), ("🔷", "Claude 3.5 Sonnet", "Anthropic", "Advanced reasoning and analysis", "Available"), ("💎", "Gemini Pro", "Google", "Multimodal AI with long context", "Available"), ("🌐", "Mistral Large", "Mistral AI", "European AI model with strong performance", "Available"), ]; let mut html = String::new(); html.push_str("
"); html.push_str("
"); html.push_str("

AI Models

"); html.push_str("

Available language models for your bots

"); html.push_str("
"); html.push_str("
"); for (icon, name, provider, description, status) in &models { let status_class = if *status == "Active" { "model-active" } else { "model-available" }; html.push_str("
"); html.push_str("
"); html.push_str(icon); html.push_str("
"); html.push_str("
"); html.push_str("
"); html.push_str("

"); html.push_str(&html_escape(name)); html.push_str("

"); html.push_str(""); html.push_str(&html_escape(provider)); html.push_str(""); html.push_str("
"); html.push_str("

"); html.push_str(&html_escape(description)); html.push_str("

"); html.push_str("
"); html.push_str(""); html.push_str(status); html.push_str(""); if *status == "Active" { html.push_str(""); } else { html.push_str(""); } html.push_str("
"); html.push_str("
"); html.push_str("
"); } html.push_str("
"); html.push_str("
"); Html(html) } /// GET /api/sources/search - Search across all sources pub async fn handle_search( State(_state): State>, Query(params): Query, ) -> impl IntoResponse { let query = params.q.unwrap_or_default(); if query.is_empty() { return Html("

Enter a search term

".to_string()); } let query_lower = query.to_lowercase(); // Search across prompts let prompts = get_prompts_data("all"); let matching_prompts: Vec<_> = prompts .iter() .filter(|p| { p.title.to_lowercase().contains(&query_lower) || p.description.to_lowercase().contains(&query_lower) }) .collect(); // Search across templates let templates = get_templates_data(); let matching_templates: Vec<_> = templates .iter() .filter(|t| { t.name.to_lowercase().contains(&query_lower) || t.description.to_lowercase().contains(&query_lower) }) .collect(); let mut html = String::new(); html.push_str("
"); html.push_str("
"); html.push_str("

Search Results for \""); html.push_str(&html_escape(&query)); html.push_str("\"

"); html.push_str("
"); if matching_prompts.is_empty() && matching_templates.is_empty() { html.push_str("
"); html.push_str("

No results found

"); html.push_str("

Try different keywords

"); html.push_str("
"); } else { if !matching_prompts.is_empty() { html.push_str("
"); html.push_str("

Prompts ("); html.push_str(&matching_prompts.len().to_string()); html.push_str(")

"); html.push_str("
"); for prompt in matching_prompts { html.push_str("
"); html.push_str(""); html.push_str(&prompt.icon); html.push_str(""); html.push_str("
"); html.push_str(""); html.push_str(&html_escape(&prompt.title)); html.push_str(""); html.push_str("

"); html.push_str(&html_escape(&prompt.description)); html.push_str("

"); html.push_str("
"); html.push_str("
"); } html.push_str("
"); html.push_str("
"); } if !matching_templates.is_empty() { html.push_str("
"); html.push_str("

Templates ("); html.push_str(&matching_templates.len().to_string()); html.push_str(")

"); html.push_str("
"); for template in matching_templates { html.push_str("
"); html.push_str(""); html.push_str(&template.icon); html.push_str(""); html.push_str("
"); html.push_str(""); html.push_str(&html_escape(&template.name)); html.push_str(""); html.push_str("

"); html.push_str(&html_escape(&template.description)); html.push_str("

"); html.push_str("
"); html.push_str("
"); } html.push_str("
"); html.push_str("
"); } } html.push_str("
"); Html(html) } // Data structures struct PromptData { id: String, title: String, description: String, category: String, icon: String, } struct TemplateData { name: String, description: String, category: String, icon: String, } fn get_prompts_data(category: &str) -> Vec { let all_prompts = vec![ PromptData { id: "summarize".to_string(), title: "Summarize Text".to_string(), description: "Create concise summaries of long documents or articles".to_string(), category: "writing".to_string(), icon: "📝".to_string(), }, PromptData { id: "code-review".to_string(), title: "Code Review".to_string(), description: "Analyze code for bugs, improvements, and best practices".to_string(), category: "coding".to_string(), icon: "💻".to_string(), }, PromptData { id: "data-analysis".to_string(), title: "Data Analysis".to_string(), description: "Extract insights and patterns from data sets".to_string(), category: "analysis".to_string(), icon: "📊".to_string(), }, PromptData { id: "creative-writing".to_string(), title: "Creative Writing".to_string(), description: "Generate stories, poems, and creative content".to_string(), category: "creative".to_string(), icon: "🎨".to_string(), }, PromptData { id: "email-draft".to_string(), title: "Email Draft".to_string(), description: "Compose professional emails quickly".to_string(), category: "business".to_string(), icon: "📧".to_string(), }, PromptData { id: "explain-concept".to_string(), title: "Explain Concept".to_string(), description: "Break down complex topics into simple explanations".to_string(), category: "education".to_string(), icon: "📚".to_string(), }, PromptData { id: "debug-code".to_string(), title: "Debug Code".to_string(), description: "Find and fix issues in your code".to_string(), category: "coding".to_string(), icon: "🐛".to_string(), }, PromptData { id: "meeting-notes".to_string(), title: "Meeting Notes".to_string(), description: "Organize and format meeting discussions".to_string(), category: "business".to_string(), icon: "📋".to_string(), }, ]; if category == "all" { all_prompts } else { all_prompts .into_iter() .filter(|p| p.category == category) .collect() } } fn get_templates_data() -> Vec { vec![ TemplateData { name: "Customer Support Bot".to_string(), description: "Handle customer inquiries and support tickets automatically".to_string(), category: "Support".to_string(), icon: "🎧".to_string(), }, TemplateData { name: "FAQ Bot".to_string(), description: "Answer frequently asked questions from your knowledge base".to_string(), category: "Support".to_string(), icon: "❓".to_string(), }, TemplateData { name: "Lead Generation Bot".to_string(), description: "Qualify leads and collect prospect information".to_string(), category: "Sales".to_string(), icon: "🎯".to_string(), }, TemplateData { name: "Onboarding Bot".to_string(), description: "Guide new users through your product or service".to_string(), category: "HR".to_string(), icon: "👋".to_string(), }, TemplateData { name: "Survey Bot".to_string(), description: "Collect feedback through conversational surveys".to_string(), category: "Research".to_string(), icon: "📊".to_string(), }, TemplateData { name: "Appointment Scheduler".to_string(), description: "Book and manage appointments automatically".to_string(), category: "Productivity".to_string(), icon: "📅".to_string(), }, ] } fn html_escape(s: &str) -> String { s.replace('&', "&") .replace('<', "<") .replace('>', ">") .replace('"', """) .replace('\'', "'") }