chore: Remove emoji icons from log messages and UI

Replace emoji icons with plain text/ASCII equivalents to prevent
encoding issues and improve console compatibility:
- Replace checkmarks with *
- Replace x marks with x
- Replace status indicators with text [OK], [ERR], etc.
- Remove decorative emojis from info/debug log messages
- Keep functional emojis in user-facing chat/sentiment features
This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2025-12-09 07:55:11 -03:00
parent b204aebd50
commit 583e764bb9
24 changed files with 203 additions and 203 deletions

View file

@ -631,10 +631,10 @@ pub async fn handle_recent_activity(State(state): State<Arc<AppState>>) -> impl
for activity in &activities { for activity in &activities {
let icon = match activity.activity_type.as_str() { let icon = match activity.activity_type.as_str() {
"session" => "💬", "session" => "",
"error" => "⚠️", "error" => "",
"bot" => "🤖", "bot" => "",
_ => "📌", _ => "",
}; };
html.push_str("<div class=\"activity-item\">"); html.push_str("<div class=\"activity-item\">");
@ -773,7 +773,7 @@ pub async fn handle_analytics_chat(
let mut html = String::new(); let mut html = String::new();
html.push_str("<div class=\"chat-message assistant\">"); html.push_str("<div class=\"chat-message assistant\">");
html.push_str("<div class=\"message-avatar\">🤖</div>"); html.push_str("<div class=\"message-avatar\"></div>");
html.push_str("<div class=\"message-content\">"); html.push_str("<div class=\"message-content\">");
html.push_str(&html_escape(response)); html.push_str(&html_escape(response));
html.push_str("</div>"); html.push_str("</div>");

View file

@ -1059,10 +1059,10 @@ async fn handle_queue_command(state: &Arc<AppState>) -> Result<String, String> {
.map_err(|e| e.to_string())??; .map_err(|e| e.to_string())??;
if result.is_empty() { if result.is_empty() {
return Ok("📋 *Queue is empty*\nNo conversations waiting for attention.".to_string()); return Ok(" *Queue is empty*\nNo conversations waiting for attention.".to_string());
} }
let mut response = format!("📋 *Queue* ({} waiting)\n\n", result.len()); let mut response = format!(" *Queue* ({} waiting)\n\n", result.len());
for (i, session) in result.iter().enumerate() { for (i, session) in result.iter().enumerate() {
let name = session let name = session
@ -1144,12 +1144,12 @@ async fn handle_take_command(
.unwrap_or("Unknown"); .unwrap_or("Unknown");
Ok(format!( Ok(format!(
" *Conversation assigned*\n\nCustomer: *{}*\nSession: {}\n\nYou can now respond to this customer. Their messages will be forwarded to you.", " *Conversation assigned*\n\nCustomer: *{}*\nSession: {}\n\nYou can now respond to this customer. Their messages will be forwarded to you.",
name, name,
&session.id.to_string()[..8] &session.id.to_string()[..8]
)) ))
} else { } else {
Ok("📭 No conversations waiting in queue.".to_string()) Ok(" No conversations waiting in queue.".to_string())
} }
}) })
.await .await
@ -1165,17 +1165,17 @@ async fn handle_status_command(
) -> Result<String, String> { ) -> Result<String, String> {
if args.is_empty() { if args.is_empty() {
return Ok( return Ok(
"📊 *Status Options*\n\n`/status online` - Available\n`/status busy` - In conversation\n`/status away` - Temporarily away\n`/status offline` - Not available" " *Status Options*\n\n`/status online` - Available\n`/status busy` - In conversation\n`/status away` - Temporarily away\n`/status offline` - Not available"
.to_string(), .to_string(),
); );
} }
let status = args[0].to_lowercase(); let status = args[0].to_lowercase();
let (emoji, text, status_value) = match status.as_str() { let (emoji, text, status_value) = match status.as_str() {
"online" => ("🟢", "Online - Available for conversations", "online"), "online" => ("", "Online - Available for conversations", "online"),
"busy" => ("🟡", "Busy - Handling conversations", "busy"), "busy" => ("", "Busy - Handling conversations", "busy"),
"away" => ("🟠", "Away - Temporarily unavailable", "away"), "away" => ("", "Away - Temporarily unavailable", "away"),
"offline" => ("", "Offline - Not available", "offline"), "offline" => ("", "Offline - Not available", "offline"),
_ => { _ => {
return Err(format!( return Err(format!(
"Invalid status: {}. Use online, busy, away, or offline.", "Invalid status: {}. Use online, busy, away, or offline.",
@ -1306,7 +1306,7 @@ async fn handle_transfer_command(
); );
Ok(format!( Ok(format!(
"🔄 *Transfer initiated*\n\nSession {} is being transferred to *{}*.\n\nThe conversation is now in the queue for the target attendant. They will be notified when they check their queue.", " *Transfer initiated*\n\nSession {} is being transferred to *{}*.\n\nThe conversation is now in the queue for the target attendant. They will be notified when they check their queue.",
&session_id.to_string()[..8], &session_id.to_string()[..8],
target_clean target_clean
)) ))
@ -1345,7 +1345,7 @@ async fn handle_resolve_command(
.map_err(|e| e.to_string())??; .map_err(|e| e.to_string())??;
Ok(format!( Ok(format!(
" *Conversation resolved*\n\nSession {} has been marked as resolved. The customer will be returned to bot mode.", " *Conversation resolved*\n\nSession {} has been marked as resolved. The customer will be returned to bot mode.",
&session_id.to_string()[..8] &session_id.to_string()[..8]
)) ))
} }
@ -1361,7 +1361,7 @@ async fn handle_tips_command(
if history.is_empty() { if history.is_empty() {
return Ok( return Ok(
"💡 No messages yet. Tips will appear when the customer sends a message.".to_string(), " No messages yet. Tips will appear when the customer sends a message.".to_string(),
); );
} }
@ -1383,19 +1383,19 @@ async fn handle_tips_command(
let (_, Json(tip_response)) = response.into_response().into_parts(); let (_, Json(tip_response)) = response.into_response().into_parts();
if tip_response.tips.is_empty() { if tip_response.tips.is_empty() {
return Ok("💡 No specific tips for this conversation yet.".to_string()); return Ok(" No specific tips for this conversation yet.".to_string());
} }
let mut result = "💡 *Tips for this conversation*\n\n".to_string(); let mut result = " *Tips for this conversation*\n\n".to_string();
for tip in tip_response.tips { for tip in tip_response.tips {
let emoji = match tip.tip_type { let emoji = match tip.tip_type {
TipType::Intent => "🎯", TipType::Intent => "",
TipType::Action => "", TipType::Action => "",
TipType::Warning => "⚠️", TipType::Warning => "",
TipType::Knowledge => "📚", TipType::Knowledge => "",
TipType::History => "📜", TipType::History => "",
TipType::General => "💡", TipType::General => "",
}; };
result.push_str(&format!("{} {}\n\n", emoji, tip.content)); result.push_str(&format!("{} {}\n\n", emoji, tip.content));
} }
@ -1429,7 +1429,7 @@ async fn handle_polish_command(
.unwrap_or("Failed to polish message".to_string())); .unwrap_or("Failed to polish message".to_string()));
} }
let mut result = " *Polished message*\n\n".to_string(); let mut result = " *Polished message*\n\n".to_string();
result.push_str(&format!("_{}_\n\n", polish_response.polished)); result.push_str(&format!("_{}_\n\n", polish_response.polished));
if !polish_response.changes.is_empty() { if !polish_response.changes.is_empty() {
@ -1461,10 +1461,10 @@ async fn handle_replies_command(
let (_, Json(replies_response)) = response.into_response().into_parts(); let (_, Json(replies_response)) = response.into_response().into_parts();
if replies_response.replies.is_empty() { if replies_response.replies.is_empty() {
return Ok("💬 No reply suggestions available.".to_string()); return Ok(" No reply suggestions available.".to_string());
} }
let mut result = "💬 *Suggested replies*\n\n".to_string(); let mut result = " *Suggested replies*\n\n".to_string();
for (i, reply) in replies_response.replies.iter().enumerate() { for (i, reply) in replies_response.replies.iter().enumerate() {
result.push_str(&format!( result.push_str(&format!(
@ -1497,7 +1497,7 @@ async fn handle_summary_command(
let summary = summary_response.summary; let summary = summary_response.summary;
let mut result = "📝 *Conversation Summary*\n\n".to_string(); let mut result = " *Conversation Summary*\n\n".to_string();
result.push_str(&format!("{}\n\n", summary.brief)); result.push_str(&format!("{}\n\n", summary.brief));
if !summary.key_points.is_empty() { if !summary.key_points.is_empty() {
@ -1525,13 +1525,13 @@ async fn handle_summary_command(
} }
result.push_str(&format!( result.push_str(&format!(
"📊 {} messages | {} minutes | Sentiment: {}", " {} messages | {} minutes | Sentiment: {}",
summary.message_count, summary.duration_minutes, summary.sentiment_trend summary.message_count, summary.duration_minutes, summary.sentiment_trend
)); ));
if !summary.recommended_action.is_empty() { if !summary.recommended_action.is_empty() {
result.push_str(&format!( result.push_str(&format!(
"\n\n💡 *Recommended:* {}", "\n\n *Recommended:* {}",
summary.recommended_action summary.recommended_action
)); ));
} }
@ -1540,7 +1540,7 @@ async fn handle_summary_command(
} }
fn get_help_text() -> String { fn get_help_text() -> String {
r#"🤖 *Attendant Commands* r#" *Attendant Commands*
*Queue Management:* *Queue Management:*
`/queue` - View waiting conversations `/queue` - View waiting conversations

View file

@ -44,7 +44,7 @@ pub fn register_clear_kb_keyword(
match result { match result {
Ok(Ok(_)) => { Ok(Ok(_)) => {
info!( info!(
" KB '{}' removed from session {}", " KB '{}' removed from session {}",
kb_name, session_clone.id kb_name, session_clone.id
); );
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
@ -131,7 +131,7 @@ fn clear_specific_kb(
); );
} else { } else {
info!( info!(
" Cleared KB '{}' from session {}, {} KB(s) remaining active", " Cleared KB '{}' from session {}, {} KB(s) remaining active",
kb_name, session_id, remaining_count kb_name, session_id, remaining_count
); );
} }
@ -160,7 +160,7 @@ fn clear_all_kbs(
if rows_affected > 0 { if rows_affected > 0 {
info!( info!(
" Cleared {} active KBs from session {}", " Cleared {} active KBs from session {}",
rows_affected, session_id rows_affected, session_id
); );
} else { } else {

View file

@ -104,23 +104,23 @@ pub fn register_core_functions(state: Arc<AppState>, user: UserSession, engine:
// Register math functions (ABS, ROUND, INT, MAX, MIN, MOD, RANDOM, etc.) // Register math functions (ABS, ROUND, INT, MAX, MIN, MOD, RANDOM, etc.)
register_math_functions(state.clone(), user.clone(), engine); register_math_functions(state.clone(), user.clone(), engine);
debug!(" Math functions registered"); debug!(" * Math functions registered");
// Register date/time functions (NOW, TODAY, YEAR, MONTH, DAY, etc.) // Register date/time functions (NOW, TODAY, YEAR, MONTH, DAY, etc.)
register_datetime_functions(state.clone(), user.clone(), engine); register_datetime_functions(state.clone(), user.clone(), engine);
debug!(" Date/Time functions registered"); debug!(" * Date/Time functions registered");
// Register validation functions (VAL, STR, ISNULL, ISEMPTY, TYPEOF, etc.) // Register validation functions (VAL, STR, ISNULL, ISEMPTY, TYPEOF, etc.)
register_validation_functions(state.clone(), user.clone(), engine); register_validation_functions(state.clone(), user.clone(), engine);
debug!(" Validation functions registered"); debug!(" * Validation functions registered");
// Register array functions (SORT, UNIQUE, CONTAINS, PUSH, POP, etc.) // Register array functions (SORT, UNIQUE, CONTAINS, PUSH, POP, etc.)
register_array_functions(state.clone(), user.clone(), engine); register_array_functions(state.clone(), user.clone(), engine);
debug!(" Array functions registered"); debug!(" * Array functions registered");
// Register error handling functions (THROW, ERROR, IS_ERROR, ASSERT, etc.) // Register error handling functions (THROW, ERROR, IS_ERROR, ASSERT, etc.)
register_error_functions(state, user, engine); register_error_functions(state, user, engine);
debug!(" Error handling functions registered"); debug!(" * Error handling functions registered");
debug!("All core BASIC functions registered successfully"); debug!("All core BASIC functions registered successfully");
} }

View file

@ -63,7 +63,7 @@ pub fn register_use_kb_keyword(
match result { match result {
Ok(Ok(_)) => { Ok(Ok(_)) => {
info!(" KB '{}' added to session {}", kb_name, session_clone.id); info!(" KB '{}' added to session {}", kb_name, session_clone.id);
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
} }
Ok(Err(e)) => { Ok(Err(e)) => {
@ -163,7 +163,7 @@ fn add_kb_to_session(
.map_err(|e| format!("Failed to add KB association: {}", e))?; .map_err(|e| format!("Failed to add KB association: {}", e))?;
info!( info!(
" Added KB '{}' to session {} (collection: {}, path: {})", " Added KB '{}' to session {} (collection: {}, path: {})",
kb_name, session_id, qdrant_collection, kb_folder_path kb_name, session_id, qdrant_collection, kb_folder_path
); );

View file

@ -263,7 +263,7 @@ fn add_website_to_session(
.map_err(|e| format!("Failed to add website to session: {}", e))?; .map_err(|e| format!("Failed to add website to session: {}", e))?;
info!( info!(
" Added website '{}' to session {} (collection: {})", " Added website '{}' to session {} (collection: {})",
url, session_id, collection_name url, session_id, collection_name
); );

View file

@ -350,12 +350,12 @@ async fn fetch_fallback_weather(location: &str) -> Result<String, String> {
fn format_weather_response(weather: &WeatherData) -> String { fn format_weather_response(weather: &WeatherData) -> String {
format!( format!(
"Current weather in {}:\n\ "Current weather in {}:\n\
🌡 Temperature: {:.1}{} (feels like {:.1}{})\n\ Temperature: {:.1}{} (feels like {:.1}{})\n\
Conditions: {}\n\ Conditions: {}\n\
💧 Humidity: {}%\n\ Humidity: {}%\n\
💨 Wind: {:.1} m/s {}\n\ Wind: {:.1} m/s {}\n\
🔍 Visibility: {:.1} km\n\ Visibility: {:.1} km\n\
📊 Pressure: {} hPa", Pressure: {} hPa",
weather.location, weather.location,
weather.temperature, weather.temperature,
weather.temperature_unit, weather.temperature_unit,
@ -375,10 +375,10 @@ fn format_forecast_response(weather: &WeatherData) -> String {
for day in &weather.forecast { for day in &weather.forecast {
response.push_str(&format!( response.push_str(&format!(
"📅 {}\n\ " {}\n\
🌡 High: {:.1}°C, Low: {:.1}°C\n\ High: {:.1}°C, Low: {:.1}°C\n\
{}\n\ {}\n\
Rain chance: {}%\n\n", Rain chance: {}%\n\n",
day.date, day.temp_high, day.temp_low, day.description, day.rain_chance day.date, day.temp_high, day.temp_low, day.description, day.rain_chance
)); ));
} }

View file

@ -37,7 +37,7 @@ impl FileTree {
for bucket in buckets { for bucket in buckets {
if let Some(name) = bucket.name() { if let Some(name) = bucket.name() {
let icon = if name.ends_with(".gbai") { let icon = if name.ends_with(".gbai") {
"🤖" ""
} else { } else {
"📦" "📦"
}; };
@ -53,7 +53,7 @@ impl FileTree {
} }
Err(e) => { Err(e) => {
self.items.push(( self.items.push((
format!(" Error: {}", e), format!("x Error: {}", e),
TreeNode::Bucket { TreeNode::Bucket {
name: String::new(), name: String::new(),
}, },
@ -62,7 +62,7 @@ impl FileTree {
} }
} else { } else {
self.items.push(( self.items.push((
" Drive not connected".to_string(), "x Drive not connected".to_string(),
TreeNode::Bucket { TreeNode::Bucket {
name: String::new(), name: String::new(),
}, },
@ -116,7 +116,7 @@ impl FileTree {
async fn load_bucket_contents(&mut self, bucket: &str, prefix: &str) -> Result<()> { async fn load_bucket_contents(&mut self, bucket: &str, prefix: &str) -> Result<()> {
self.items.clear(); self.items.clear();
self.items.push(( self.items.push((
"⬆️ .. (go back)".to_string(), " .. (go back)".to_string(),
TreeNode::Folder { TreeNode::Folder {
bucket: bucket.to_string(), bucket: bucket.to_string(),
path: "..".to_string(), path: "..".to_string(),
@ -195,13 +195,13 @@ impl FileTree {
files.sort_by(|(a, _), (b, _)| a.cmp(b)); files.sort_by(|(a, _), (b, _)| a.cmp(b));
for (name, full_path) in files { for (name, full_path) in files {
let icon = if name.ends_with(".bas") { let icon = if name.ends_with(".bas") {
"⚙️" ""
} else if name.ends_with(".ast") { } else if name.ends_with(".ast") {
"🔧" ""
} else if name.ends_with(".csv") { } else if name.ends_with(".csv") {
"📊" ""
} else if name.ends_with(".gbkb") { } else if name.ends_with(".gbkb") {
"📚" ""
} else if name.ends_with(".json") { } else if name.ends_with(".json") {
"🔖" "🔖"
} else { } else {

View file

@ -350,9 +350,9 @@ impl XtreeUI {
.iter() .iter()
.map(|(comp_name, process, _port)| { .map(|(comp_name, process, _port)| {
let status = if status_panel::StatusPanel::check_component_running(process) { let status = if status_panel::StatusPanel::check_component_running(process) {
format!("🟢 {}", comp_name) format!(" {}", comp_name)
} else { } else {
format!("🔴 {}", comp_name) format!(" {}", comp_name)
}; };
status status
}) })
@ -467,7 +467,7 @@ impl XtreeUI {
.style(Style::default().bg(bg)); .style(Style::default().bg(bg));
let status_text = format!( let status_text = format!(
"\n {}\n\n Components:\n ○ Vault\n ○ Database\n ○ Drive\n ○ Cache\n ○ LLM", "\n {}\n\n Components:\n ○ Vault\n ○ Database\n ○ Drive\n ○ Cache\n ○ LLM",
self.bootstrap_status self.bootstrap_status
); );
let status_para = Paragraph::new(status_text) let status_para = Paragraph::new(status_text)

View file

@ -104,9 +104,9 @@ impl StatusPanel {
for (comp_name, process, port) in components { for (comp_name, process, port) in components {
let status = if Self::check_component_running(process) { let status = if Self::check_component_running(process) {
format!("🟢 ONLINE [Port: {}]", port) format!(" ONLINE [Port: {}]", port)
} else { } else {
"🔴 OFFLINE".to_string() " OFFLINE".to_string()
}; };
lines.push(format!(" {:<10} {}", comp_name, status)); lines.push(format!(" {:<10} {}", comp_name, status));
} }
@ -138,7 +138,7 @@ impl StatusPanel {
} else { } else {
" " " "
}; };
lines.push(format!(" {} 🤖 {}", marker, bot_name)); lines.push(format!(" {} {}", marker, bot_name));
if let Some(ref selected) = selected_bot { if let Some(ref selected) = selected_bot {
if selected == &bot_name { if selected == &bot_name {

View file

@ -666,7 +666,7 @@ impl StartupWizard {
stdout, stdout,
cursor::MoveTo(4, 19 + i as u16), cursor::MoveTo(4, 19 + i as u16),
SetForegroundColor(Color::Green), SetForegroundColor(Color::Green),
Print(" "), Print("* "),
ResetColor, ResetColor,
Print(format!("{}", component)) Print(format!("{}", component))
)?; )?;
@ -718,7 +718,7 @@ impl StartupWizard {
execute!( execute!(
stdout, stdout,
SetForegroundColor(Color::Green), SetForegroundColor(Color::Green),
Print(" "), Print("> "),
Print(format!("{:<25}", name)), Print(format!("{:<25}", name)),
SetForegroundColor(Color::DarkGrey), SetForegroundColor(Color::DarkGrey),
Print(format!(" {}", desc)), Print(format!(" {}", desc)),
@ -775,8 +775,8 @@ impl StartupWizard {
for (i, (component, _, can_toggle)) in options.iter().enumerate() { for (i, (component, _, can_toggle)) in options.iter().enumerate() {
execute!(stdout, cursor::MoveTo(4, start_row + i as u16))?; execute!(stdout, cursor::MoveTo(4, start_row + i as u16))?;
let checkbox = if selected[i] { "[]" } else { "[ ]" }; let checkbox = if selected[i] { "[*]" } else { "[ ]" };
let prefix = if i == cursor { "" } else { " " }; let prefix = if i == cursor { ">" } else { " " };
if !can_toggle { if !can_toggle {
execute!( execute!(
@ -930,7 +930,7 @@ pub fn apply_wizard_config(config: &WizardConfig) -> io::Result<()> {
fs::write(config.data_dir.join(".env"), env_content)?; fs::write(config.data_dir.join(".env"), env_content)?;
println!("\n Configuration applied successfully!"); println!("\n Configuration applied successfully!");
println!(" Data directory: {}", config.data_dir.display()); println!(" Data directory: {}", config.data_dir.display());
println!("\n Next steps:"); println!("\n Next steps:");
println!(" 1. Run: botserver start"); println!(" 1. Run: botserver start");

View file

@ -491,13 +491,13 @@ impl BootstrapManager {
pub async fn bootstrap(&mut self) -> Result<()> { pub async fn bootstrap(&mut self) -> Result<()> {
// Generate certificates first (including for Vault) // Generate certificates first (including for Vault)
info!("🔒 Generating TLS certificates..."); info!("Generating TLS certificates...");
if let Err(e) = self.generate_certificates().await { if let Err(e) = self.generate_certificates().await {
error!("Failed to generate certificates: {}", e); error!("Failed to generate certificates: {}", e);
} }
// Create Vault configuration with mTLS // Create Vault configuration with mTLS
info!("📝 Creating Vault configuration..."); info!("Creating Vault configuration...");
if let Err(e) = self.create_vault_config().await { if let Err(e) = self.create_vault_config().await {
error!("Failed to create Vault config: {}", e); error!("Failed to create Vault config: {}", e);
} }
@ -562,7 +562,7 @@ impl BootstrapManager {
// After tables is installed, START PostgreSQL and create Zitadel config files before installing directory // After tables is installed, START PostgreSQL and create Zitadel config files before installing directory
if component == "tables" { if component == "tables" {
info!("🚀 Starting PostgreSQL database..."); info!("Starting PostgreSQL database...");
match pm.start("tables") { match pm.start("tables") {
Ok(_) => { Ok(_) => {
info!("PostgreSQL started successfully"); info!("PostgreSQL started successfully");
@ -575,7 +575,7 @@ impl BootstrapManager {
} }
// Run migrations using direct connection (Vault not set up yet) // Run migrations using direct connection (Vault not set up yet)
info!("🔄 Running database migrations..."); info!("Running database migrations...");
let database_url = let database_url =
format!("postgres://gbuser:{}@localhost:5432/botserver", db_password); format!("postgres://gbuser:{}@localhost:5432/botserver", db_password);
match diesel::PgConnection::establish(&database_url) { match diesel::PgConnection::establish(&database_url) {
@ -583,7 +583,7 @@ impl BootstrapManager {
if let Err(e) = self.apply_migrations(&mut conn) { if let Err(e) = self.apply_migrations(&mut conn) {
error!("Failed to apply migrations: {}", e); error!("Failed to apply migrations: {}", e);
} else { } else {
info!("Database migrations applied"); info!("Database migrations applied");
} }
} }
Err(e) => { Err(e) => {
@ -591,7 +591,7 @@ impl BootstrapManager {
} }
} }
info!("🔧 Creating Directory configuration files..."); info!("Creating Directory configuration files...");
if let Err(e) = self.configure_services_in_directory(&db_password).await { if let Err(e) = self.configure_services_in_directory(&db_password).await {
error!("Failed to create Directory config files: {}", e); error!("Failed to create Directory config files: {}", e);
} }
@ -599,7 +599,7 @@ impl BootstrapManager {
// Directory configuration - setup happens after install starts Zitadel // Directory configuration - setup happens after install starts Zitadel
if component == "directory" { if component == "directory" {
info!("🔧 Waiting for Directory to be ready..."); info!("Waiting for Directory to be ready...");
if let Err(e) = self.setup_directory().await { if let Err(e) = self.setup_directory().await {
// Don't fail completely - Zitadel may still be usable with first instance setup // Don't fail completely - Zitadel may still be usable with first instance setup
warn!("Directory additional setup had issues: {}", e); warn!("Directory additional setup had issues: {}", e);
@ -608,7 +608,7 @@ impl BootstrapManager {
// After Vault is installed, START the server then initialize it // After Vault is installed, START the server then initialize it
if component == "vault" { if component == "vault" {
info!("🚀 Starting Vault server..."); info!("Starting Vault server...");
match pm.start("vault") { match pm.start("vault") {
Ok(_) => { Ok(_) => {
info!("Vault server started"); info!("Vault server started");
@ -620,7 +620,7 @@ impl BootstrapManager {
} }
} }
info!("🔐 Initializing Vault with secrets..."); info!("Initializing Vault with secrets...");
if let Err(e) = self if let Err(e) = self
.setup_vault( .setup_vault(
&db_password, &db_password,
@ -634,14 +634,14 @@ impl BootstrapManager {
} }
// Initialize the global SecretsManager so other components can use Vault // Initialize the global SecretsManager so other components can use Vault
info!("🔑 Initializing SecretsManager..."); info!("Initializing SecretsManager...");
debug!( debug!(
"VAULT_ADDR={:?}, VAULT_TOKEN set={}", "VAULT_ADDR={:?}, VAULT_TOKEN set={}",
std::env::var("VAULT_ADDR").ok(), std::env::var("VAULT_ADDR").ok(),
std::env::var("VAULT_TOKEN").is_ok() std::env::var("VAULT_TOKEN").is_ok()
); );
match init_secrets_manager().await { match init_secrets_manager().await {
Ok(_) => info!("SecretsManager initialized successfully"), Ok(_) => info!("SecretsManager initialized successfully"),
Err(e) => { Err(e) => {
error!("Failed to initialize SecretsManager: {}", e); error!("Failed to initialize SecretsManager: {}", e);
// Don't continue if SecretsManager fails - it's required for DB connection // Don't continue if SecretsManager fails - it's required for DB connection
@ -654,21 +654,21 @@ impl BootstrapManager {
} }
if component == "email" { if component == "email" {
info!("🔧 Auto-configuring Email (Stalwart)..."); info!("Auto-configuring Email (Stalwart)...");
if let Err(e) = self.setup_email().await { if let Err(e) = self.setup_email().await {
error!("Failed to setup Email: {}", e); error!("Failed to setup Email: {}", e);
} }
} }
if component == "proxy" { if component == "proxy" {
info!("🔧 Configuring Caddy reverse proxy..."); info!("Configuring Caddy reverse proxy...");
if let Err(e) = self.setup_caddy_proxy().await { if let Err(e) = self.setup_caddy_proxy().await {
error!("Failed to setup Caddy: {}", e); error!("Failed to setup Caddy: {}", e);
} }
} }
if component == "dns" { if component == "dns" {
info!("🔧 Configuring CoreDNS for dynamic DNS..."); info!("Configuring CoreDNS for dynamic DNS...");
if let Err(e) = self.setup_coredns().await { if let Err(e) = self.setup_coredns().await {
error!("Failed to setup CoreDNS: {}", e); error!("Failed to setup CoreDNS: {}", e);
} }
@ -1217,7 +1217,7 @@ meet IN A 127.0.0.1
vault_addr, root_token, db_password vault_addr, root_token, db_password
)) ))
.output()?; .output()?;
info!(" Stored database credentials"); info!(" Stored database credentials");
// Drive credentials // Drive credentials
let _ = std::process::Command::new("sh") let _ = std::process::Command::new("sh")
@ -1227,7 +1227,7 @@ meet IN A 127.0.0.1
vault_addr, root_token, drive_accesskey, drive_secret vault_addr, root_token, drive_accesskey, drive_secret
)) ))
.output()?; .output()?;
info!(" Stored drive credentials"); info!(" Stored drive credentials");
// Cache credentials // Cache credentials
let _ = std::process::Command::new("sh") let _ = std::process::Command::new("sh")
@ -1237,7 +1237,7 @@ meet IN A 127.0.0.1
vault_addr, root_token, cache_password vault_addr, root_token, cache_password
)) ))
.output()?; .output()?;
info!(" Stored cache credentials"); info!(" Stored cache credentials");
// Directory placeholder (will be updated after Zitadel setup) // Directory placeholder (will be updated after Zitadel setup)
let _ = std::process::Command::new("sh") let _ = std::process::Command::new("sh")
@ -1247,7 +1247,7 @@ meet IN A 127.0.0.1
vault_addr, root_token vault_addr, root_token
)) ))
.output()?; .output()?;
info!(" Created directory placeholder"); info!(" Created directory placeholder");
// LLM placeholder // LLM placeholder
let _ = std::process::Command::new("sh") let _ = std::process::Command::new("sh")
@ -1257,7 +1257,7 @@ meet IN A 127.0.0.1
vault_addr, root_token vault_addr, root_token
)) ))
.output()?; .output()?;
info!(" Created LLM placeholder"); info!(" Created LLM placeholder");
// Email placeholder // Email placeholder
let _ = std::process::Command::new("sh") let _ = std::process::Command::new("sh")
@ -1267,7 +1267,7 @@ meet IN A 127.0.0.1
vault_addr, root_token vault_addr, root_token
)) ))
.output()?; .output()?;
info!(" Created email placeholder"); info!(" Created email placeholder");
// Encryption key // Encryption key
let encryption_key = self.generate_secure_password(32); let encryption_key = self.generate_secure_password(32);
@ -1278,7 +1278,7 @@ meet IN A 127.0.0.1
vault_addr, root_token, encryption_key vault_addr, root_token, encryption_key
)) ))
.output()?; .output()?;
info!(" Generated and stored encryption key"); info!(" Generated and stored encryption key");
// Write .env file with ONLY Vault variables - NO LEGACY FALLBACK // Write .env file with ONLY Vault variables - NO LEGACY FALLBACK
info!("Writing .env file with Vault configuration..."); info!("Writing .env file with Vault configuration...");
@ -1308,9 +1308,9 @@ VAULT_CACHE_TTL=300
); );
fs::write(&env_file_path, env_content)?; fs::write(&env_file_path, env_content)?;
info!(" Created .env file with Vault configuration"); info!(" Created .env file with Vault configuration");
info!("🔐 Vault setup complete!"); info!("Vault setup complete!");
info!(" Vault UI: {}/ui", vault_addr); info!(" Vault UI: {}/ui", vault_addr);
info!(" Root token saved to: {}", vault_init_path.display()); info!(" Root token saved to: {}", vault_init_path.display());
@ -1341,7 +1341,7 @@ VAULT_CACHE_TTL=300
info!(" IMAP: {}:{}", config.imap_host, config.imap_port); info!(" IMAP: {}:{}", config.imap_host, config.imap_port);
info!(" Admin: {} / {}", config.admin_user, config.admin_pass); info!(" Admin: {} / {}", config.admin_user, config.admin_pass);
if config.directory_integration { if config.directory_integration {
info!(" 🔗 Integrated with Directory for authentication"); info!(" Integrated with Directory for authentication");
} }
Ok(()) Ok(())
@ -1562,7 +1562,7 @@ VAULT_CACHE_TTL=300
if synced > 0 { if synced > 0 {
info!( info!(
"Synced {} config values for bot {} (skipped {} empty lines)", "Synced {} config values for bot {} (skipped {} empty lines)",
synced, bot_id, skipped synced, bot_id, skipped
); );
} else { } else {

View file

@ -79,12 +79,12 @@ impl OAuthProvider {
/// Get icon/emoji for UI /// Get icon/emoji for UI
pub fn icon(&self) -> &'static str { pub fn icon(&self) -> &'static str {
match self { match self {
OAuthProvider::Google => "🔵", OAuthProvider::Google => "",
OAuthProvider::Discord => "🎮", OAuthProvider::Discord => "",
OAuthProvider::Reddit => "🟠", OAuthProvider::Reddit => "",
OAuthProvider::Twitter => "🐦", OAuthProvider::Twitter => "",
OAuthProvider::Microsoft => "🪟", OAuthProvider::Microsoft => "",
OAuthProvider::Facebook => "📘", OAuthProvider::Facebook => "",
} }
} }
} }

View file

@ -32,12 +32,12 @@ pub async fn run() -> Result<()> {
for component in components { for component in components {
if pm.is_installed(component.name) { if pm.is_installed(component.name) {
match pm.start(component.name) { match pm.start(component.name) {
Ok(_) => println!(" Started {}", component.name), Ok(_) => println!("* Started {}", component.name),
Err(e) => eprintln!(" Failed to start {}: {}", component.name, e), Err(e) => eprintln!("x Failed to start {}: {}", component.name, e),
} }
} }
} }
println!(" BotServer components started"); println!("* BotServer components started");
} }
"stop" => { "stop" => {
println!("Stopping all components..."); println!("Stopping all components...");
@ -48,7 +48,7 @@ pub async fn run() -> Result<()> {
.arg(component.termination_command) .arg(component.termination_command)
.output(); .output();
} }
println!(" BotServer components stopped"); println!("* BotServer components stopped");
} }
"restart" => { "restart" => {
println!("Restarting BotServer..."); println!("Restarting BotServer...");
@ -77,7 +77,7 @@ pub async fn run() -> Result<()> {
let _ = pm.start(component.name); let _ = pm.start(component.name);
} }
} }
println!(" BotServer restarted"); println!("* BotServer restarted");
} }
"install" => { "install" => {
if args.len() < 3 { if args.len() < 3 {
@ -97,7 +97,7 @@ pub async fn run() -> Result<()> {
}; };
let pm = PackageManager::new(mode, tenant)?; let pm = PackageManager::new(mode, tenant)?;
pm.install(component).await?; pm.install(component).await?;
println!(" Component '{}' installed successfully", component); println!("* Component '{}' installed successfully", component);
} }
"remove" => { "remove" => {
if args.len() < 3 { if args.len() < 3 {
@ -117,7 +117,7 @@ pub async fn run() -> Result<()> {
}; };
let pm = PackageManager::new(mode, tenant)?; let pm = PackageManager::new(mode, tenant)?;
pm.remove(component)?; pm.remove(component)?;
println!(" Component '{}' removed successfully", component); println!("* Component '{}' removed successfully", component);
} }
"list" => { "list" => {
let mode = if args.contains(&"--container".to_string()) { let mode = if args.contains(&"--container".to_string()) {
@ -134,7 +134,7 @@ pub async fn run() -> Result<()> {
println!("Available components:"); println!("Available components:");
for component in pm.list() { for component in pm.list() {
let status = if pm.is_installed(&component) { let status = if pm.is_installed(&component) {
" installed" "* installed"
} else { } else {
" available" " available"
}; };
@ -159,9 +159,9 @@ pub async fn run() -> Result<()> {
}; };
let pm = PackageManager::new(mode, tenant)?; let pm = PackageManager::new(mode, tenant)?;
if pm.is_installed(component) { if pm.is_installed(component) {
println!(" Component '{}' is installed", component); println!("* Component '{}' is installed", component);
} else { } else {
println!(" Component '{}' is not installed", component); println!("x Component '{}' is not installed", component);
} }
} }
"--help" | "-h" => { "--help" | "-h" => {

View file

@ -117,7 +117,7 @@ impl DirectorySetup {
/// Initialize directory with default configuration /// Initialize directory with default configuration
pub async fn initialize(&mut self) -> Result<DirectoryConfig> { pub async fn initialize(&mut self) -> Result<DirectoryConfig> {
log::info!("🔧 Initializing Directory (Zitadel) with defaults..."); log::info!(" Initializing Directory (Zitadel) with defaults...");
// Check if already initialized // Check if already initialized
if let Ok(existing_config) = self.load_existing_config().await { if let Ok(existing_config) = self.load_existing_config().await {
@ -133,19 +133,19 @@ impl DirectorySetup {
// Create default organization // Create default organization
let org = self.create_default_organization().await?; let org = self.create_default_organization().await?;
log::info!(" Created default organization: {}", org.name); log::info!(" Created default organization: {}", org.name);
// Create default user // Create default user
let user = self.create_default_user(&org.id).await?; let user = self.create_default_user(&org.id).await?;
log::info!(" Created default user: {}", user.username); log::info!(" Created default user: {}", user.username);
// Create OAuth2 application for BotServer // Create OAuth2 application for BotServer
let (project_id, client_id, client_secret) = self.create_oauth_application(&org.id).await?; let (project_id, client_id, client_secret) = self.create_oauth_application(&org.id).await?;
log::info!(" Created OAuth2 application"); log::info!(" Created OAuth2 application");
// Grant user admin permissions // Grant user admin permissions
self.grant_user_permissions(&org.id, &user.id).await?; self.grant_user_permissions(&org.id, &user.id).await?;
log::info!(" Granted admin permissions to default user"); log::info!(" Granted admin permissions to default user");
let config = DirectoryConfig { let config = DirectoryConfig {
base_url: self.base_url.clone(), base_url: self.base_url.clone(),
@ -159,15 +159,15 @@ impl DirectorySetup {
// Save configuration // Save configuration
self.save_config_internal(&config).await?; self.save_config_internal(&config).await?;
log::info!(" Saved Directory configuration"); log::info!(" Saved Directory configuration");
log::info!("🎉 Directory initialization complete!"); log::info!(" Directory initialization complete!");
log::info!( log::info!(
"📧 Default user: {} / {}", " Default user: {} / {}",
config.default_user.email, config.default_user.email,
config.default_user.password config.default_user.password
); );
log::info!("🌐 Login at: {}", self.base_url); log::info!(" Login at: {}", self.base_url);
Ok(config) Ok(config)
} }

View file

@ -93,7 +93,7 @@ impl EmailSetup {
&mut self, &mut self,
directory_config_path: Option<PathBuf>, directory_config_path: Option<PathBuf>,
) -> Result<EmailConfig> { ) -> Result<EmailConfig> {
log::info!("🔧 Initializing Email (Stalwart) server..."); log::info!(" Initializing Email (Stalwart) server...");
// Check if already initialized // Check if already initialized
if let Ok(existing_config) = self.load_existing_config().await { if let Ok(existing_config) = self.load_existing_config().await {
@ -106,17 +106,17 @@ impl EmailSetup {
// Create default domain // Create default domain
self.create_default_domain().await?; self.create_default_domain().await?;
log::info!(" Created default email domain: localhost"); log::info!(" Created default email domain: localhost");
// Set up Directory (Zitadel) integration if available // Set up Directory (Zitadel) integration if available
let directory_integration = if let Some(dir_config_path) = directory_config_path { let directory_integration = if let Some(dir_config_path) = directory_config_path {
match self.setup_directory_integration(&dir_config_path).await { match self.setup_directory_integration(&dir_config_path).await {
Ok(_) => { Ok(_) => {
log::info!(" Integrated with Directory for authentication"); log::info!(" Integrated with Directory for authentication");
true true
} }
Err(e) => { Err(e) => {
log::warn!("⚠️ Directory integration failed: {}", e); log::warn!(" Directory integration failed: {}", e);
false false
} }
} }
@ -126,7 +126,7 @@ impl EmailSetup {
// Create admin account // Create admin account
self.create_admin_account().await?; self.create_admin_account().await?;
log::info!(" Created admin email account: {}", self.admin_user); log::info!(" Created admin email account: {}", self.admin_user);
let config = EmailConfig { let config = EmailConfig {
base_url: self.base_url.clone(), base_url: self.base_url.clone(),
@ -141,9 +141,9 @@ impl EmailSetup {
// Save configuration // Save configuration
self.save_config(&config).await?; self.save_config(&config).await?;
log::info!(" Saved Email configuration"); log::info!(" Saved Email configuration");
log::info!("🎉 Email initialization complete!"); log::info!(" Email initialization complete!");
log::info!("📧 SMTP: localhost:25 (587 for TLS)"); log::info!("📧 SMTP: localhost:25 (587 for TLS)");
log::info!("📬 IMAP: localhost:143 (993 for TLS)"); log::info!("📬 IMAP: localhost:143 (993 for TLS)");
log::info!("👤 Admin: {} / {}", config.admin_user, config.admin_pass); log::info!("👤 Admin: {} / {}", config.admin_user, config.admin_pass);
@ -288,7 +288,7 @@ impl EmailSetup {
if !email.is_empty() { if !email.is_empty() {
self.create_user_mailbox(username, password, email).await?; self.create_user_mailbox(username, password, email).await?;
log::info!(" Created mailbox for: {}", email); log::info!(" Created mailbox for: {}", email);
} }
} }

View file

@ -258,7 +258,7 @@ pub async fn handle_save(
Ok(_) => { Ok(_) => {
let mut html = String::new(); let mut html = String::new();
html.push_str("<div class=\"save-result success\">"); html.push_str("<div class=\"save-result success\">");
html.push_str("<span class=\"save-icon\"></span>"); html.push_str("<span class=\"save-icon\">*</span>");
html.push_str("<span class=\"save-message\">Saved successfully</span>"); html.push_str("<span class=\"save-message\">Saved successfully</span>");
html.push_str("</div>"); html.push_str("</div>");
Html(html) Html(html)
@ -266,7 +266,7 @@ pub async fn handle_save(
Err(e) => { Err(e) => {
let mut html = String::new(); let mut html = String::new();
html.push_str("<div class=\"save-result error\">"); html.push_str("<div class=\"save-result error\">");
html.push_str("<span class=\"save-icon\"></span>"); html.push_str("<span class=\"save-icon\">x</span>");
html.push_str("<span class=\"save-message\">Save failed: "); html.push_str("<span class=\"save-message\">Save failed: ");
html.push_str(&html_escape(&e)); html.push_str(&html_escape(&e));
html.push_str("</span>"); html.push_str("</span>");
@ -289,13 +289,13 @@ pub async fn handle_validate(
if validation.valid { if validation.valid {
html.push_str("<div class=\"validation-success\">"); html.push_str("<div class=\"validation-success\">");
html.push_str("<span class=\"validation-icon\"></span>"); html.push_str("<span class=\"validation-icon\">*</span>");
html.push_str("<span class=\"validation-text\">Dialog is valid</span>"); html.push_str("<span class=\"validation-text\">Dialog is valid</span>");
html.push_str("</div>"); html.push_str("</div>");
} else { } else {
html.push_str("<div class=\"validation-errors\">"); html.push_str("<div class=\"validation-errors\">");
html.push_str("<div class=\"validation-header\">"); html.push_str("<div class=\"validation-header\">");
html.push_str("<span class=\"validation-icon\"></span>"); html.push_str("<span class=\"validation-icon\">x</span>");
html.push_str("<span class=\"validation-text\">"); html.push_str("<span class=\"validation-text\">");
html.push_str(&validation.errors.len().to_string()); html.push_str(&validation.errors.len().to_string());
html.push_str(" error(s) found</span>"); html.push_str(" error(s) found</span>");
@ -318,7 +318,7 @@ pub async fn handle_validate(
if !validation.warnings.is_empty() { if !validation.warnings.is_empty() {
html.push_str("<div class=\"validation-warnings\">"); html.push_str("<div class=\"validation-warnings\">");
html.push_str("<div class=\"validation-header\">"); html.push_str("<div class=\"validation-header\">");
html.push_str("<span class=\"validation-icon\"></span>"); html.push_str("<span class=\"validation-icon\">!</span>");
html.push_str("<span class=\"validation-text\">"); html.push_str("<span class=\"validation-text\">");
html.push_str(&validation.warnings.len().to_string()); html.push_str(&validation.warnings.len().to_string());
html.push_str(" warning(s)</span>"); html.push_str(" warning(s)</span>");

View file

@ -364,7 +364,7 @@ pub fn convert_tree_to_items(tree: &FileTree) -> Vec<FileItem> {
size: None, size: None,
modified: None, modified: None,
icon: if name.ends_with(".gbai") { icon: if name.ends_with(".gbai") {
"🤖".to_string() "".to_string()
} else { } else {
"📦".to_string() "📦".to_string()
}, },
@ -586,13 +586,13 @@ pub async fn create_folder(
/// Get appropriate icon for file based on extension /// Get appropriate icon for file based on extension
fn get_file_icon(path: &str) -> String { fn get_file_icon(path: &str) -> String {
if path.ends_with(".bas") { if path.ends_with(".bas") {
"⚙️".to_string() "".to_string()
} else if path.ends_with(".ast") { } else if path.ends_with(".ast") {
"🔧".to_string() "".to_string()
} else if path.ends_with(".csv") { } else if path.ends_with(".csv") {
"📊".to_string() "".to_string()
} else if path.ends_with(".gbkb") { } else if path.ends_with(".gbkb") {
"📚".to_string() "".to_string()
} else if path.ends_with(".json") { } else if path.ends_with(".json") {
"🔖".to_string() "🔖".to_string()
} else if path.ends_with(".txt") || path.ends_with(".md") { } else if path.ends_with(".txt") || path.ends_with(".md") {
@ -602,7 +602,7 @@ fn get_file_icon(path: &str) -> String {
} else if path.ends_with(".zip") || path.ends_with(".tar") || path.ends_with(".gz") { } else if path.ends_with(".zip") || path.ends_with(".tar") || path.ends_with(".gz") {
"📦".to_string() "📦".to_string()
} else if path.ends_with(".jpg") || path.ends_with(".png") || path.ends_with(".gif") { } else if path.ends_with(".jpg") || path.ends_with(".png") || path.ends_with(".gif") {
"🖼️".to_string() "".to_string()
} else { } else {
"📄".to_string() "📄".to_string()
} }

View file

@ -1782,10 +1782,10 @@ pub async fn list_folders_htmx(
let mut html = String::new(); let mut html = String::new();
for (folder_name, icon, count) in &[ for (folder_name, icon, count) in &[
("inbox", "📥", folder_counts.get("INBOX").unwrap_or(&0)), ("inbox", "", folder_counts.get("INBOX").unwrap_or(&0)),
("sent", "📤", folder_counts.get("Sent").unwrap_or(&0)), ("sent", "", folder_counts.get("Sent").unwrap_or(&0)),
("drafts", "📝", folder_counts.get("Drafts").unwrap_or(&0)), ("drafts", "", folder_counts.get("Drafts").unwrap_or(&0)),
("trash", "🗑️", folder_counts.get("Trash").unwrap_or(&0)), ("trash", "", folder_counts.get("Trash").unwrap_or(&0)),
] { ] {
let active = if *folder_name == "inbox" { "active" } else { "" }; let active = if *folder_name == "inbox" { "active" } else { "" };
let count_badge = if **count > 0 { let count_badge = if **count > 0 {

View file

@ -796,7 +796,7 @@ pub async fn handle_save_document(
log::info!("Document saved: {} at {}", doc_id, path); log::info!("Document saved: {} at {}", doc_id, path);
let mut html = String::new(); let mut html = String::new();
html.push_str("<div class=\"save-success\">"); html.push_str("<div class=\"save-success\">");
html.push_str("<span class=\"save-icon\"></span>"); html.push_str("<span class=\"save-icon\">*</span>");
html.push_str("<span>Saved</span>"); html.push_str("<span>Saved</span>");
html.push_str("</div>"); html.push_str("</div>");
Html(html) Html(html)
@ -1392,7 +1392,7 @@ fn format_ai_response(content: &str) -> String {
html.push_str("<div class=\"ai-response\">"); html.push_str("<div class=\"ai-response\">");
html.push_str("<div class=\"ai-response-header\">"); html.push_str("<div class=\"ai-response-header\">");
html.push_str("<span class=\"ai-icon\">🤖</span>"); html.push_str("<span class=\"ai-icon\"></span>");
html.push_str("<span>AI Response</span>"); html.push_str("<span>AI Response</span>");
html.push_str("</div>"); html.push_str("</div>");
html.push_str("<div class=\"ai-response-content\">"); html.push_str("<div class=\"ai-response-content\">");
@ -1415,7 +1415,7 @@ fn format_ai_response(content: &str) -> String {
fn format_error(message: &str) -> String { fn format_error(message: &str) -> String {
let mut html = String::new(); let mut html = String::new();
html.push_str("<div class=\"error-message\">"); html.push_str("<div class=\"error-message\">");
html.push_str("<span class=\"error-icon\">⚠️</span>"); html.push_str("<span class=\"error-icon\"></span>");
html.push_str("<span>"); html.push_str("<span>");
html.push_str(&html_escape(message)); html.push_str(&html_escape(message));
html.push_str("</span>"); html.push_str("</span>");

View file

@ -110,7 +110,7 @@ pub async fn handle_list_collections(State(state): State<Arc<AppState>>) -> impl
html.push_str("<div class=\"collection-item\" data-id=\""); html.push_str("<div class=\"collection-item\" data-id=\"");
html.push_str(&html_escape(id)); html.push_str(&html_escape(id));
html.push_str("\">"); html.push_str("\">");
html.push_str("<div class=\"collection-icon\">📁</div>"); html.push_str("<div class=\"collection-icon\"></div>");
html.push_str("<div class=\"collection-info\">"); html.push_str("<div class=\"collection-info\">");
html.push_str("<span class=\"collection-name\">"); html.push_str("<span class=\"collection-name\">");
html.push_str(&html_escape(name)); html.push_str(&html_escape(name));
@ -193,7 +193,7 @@ pub async fn handle_create_collection(
html.push_str("<div class=\"collection-item new-item\" data-id=\""); html.push_str("<div class=\"collection-item new-item\" data-id=\"");
html.push_str(&html_escape(&id_clone)); html.push_str(&html_escape(&id_clone));
html.push_str("\">"); html.push_str("\">");
html.push_str("<div class=\"collection-icon\">📁</div>"); html.push_str("<div class=\"collection-icon\"></div>");
html.push_str("<div class=\"collection-info\">"); html.push_str("<div class=\"collection-info\">");
html.push_str("<span class=\"collection-name\">"); html.push_str("<span class=\"collection-name\">");
html.push_str(&html_escape(&name_clone)); html.push_str(&html_escape(&name_clone));
@ -324,7 +324,7 @@ pub async fn handle_search(
if results.is_empty() { if results.is_empty() {
html.push_str("<div class=\"no-results\">"); html.push_str("<div class=\"no-results\">");
html.push_str("<div class=\"no-results-icon\">🔍</div>"); html.push_str("<div class=\"no-results-icon\"></div>");
html.push_str("<h4>No results found</h4>"); html.push_str("<h4>No results found</h4>");
html.push_str("<p>Try different keywords or check your spelling</p>"); html.push_str("<p>Try different keywords or check your spelling</p>");
html.push_str("</div>"); html.push_str("</div>");
@ -449,19 +449,19 @@ pub async fn handle_trending_tags(State(_state): State<Arc<AppState>>) -> impl I
pub async fn handle_prompts(State(_state): State<Arc<AppState>>) -> impl IntoResponse { pub async fn handle_prompts(State(_state): State<Arc<AppState>>) -> impl IntoResponse {
let prompts = vec![ let prompts = vec![
( (
"📚", "",
"Getting Started", "Getting Started",
"Learn the basics and set up your first bot", "Learn the basics and set up your first bot",
), ),
("🔧", "Configuration", "Customize settings and preferences"), ("", "Configuration", "Customize settings and preferences"),
( (
"🔌", "🔌",
"Integrations", "Integrations",
"Connect with external services and APIs", "Connect with external services and APIs",
), ),
("🚀", "Deployment", "Deploy your bot to production"), ("", "Deployment", "Deploy your bot to production"),
("🔒", "Security", "Best practices for securing your bot"), ("", "Security", "Best practices for securing your bot"),
("📊", "Analytics", "Monitor and analyze bot performance"), ("", "Analytics", "Monitor and analyze bot performance"),
]; ];
let mut html = String::new(); let mut html = String::new();

View file

@ -45,13 +45,13 @@ pub async fn handle_prompts(
html.push_str("<div class=\"category-list\">"); html.push_str("<div class=\"category-list\">");
let categories = vec![ let categories = vec![
("all", "All Prompts", "📋"), ("all", "All Prompts", ""),
("writing", "Writing", "✍️"), ("writing", "Writing", ""),
("coding", "Coding", "💻"), ("coding", "Coding", ""),
("analysis", "Analysis", "📊"), ("analysis", "Analysis", ""),
("creative", "Creative", "🎨"), ("creative", "Creative", ""),
("business", "Business", "💼"), ("business", "Business", ""),
("education", "Education", "📚"), ("education", "Education", ""),
]; ];
for (id, name, icon) in &categories { for (id, name, icon) in &categories {
@ -160,11 +160,11 @@ pub async fn handle_templates(State(_state): State<Arc<AppState>>) -> impl IntoR
/// GET /api/sources/news - News tab content /// GET /api/sources/news - News tab content
pub async fn handle_news(State(_state): State<Arc<AppState>>) -> impl IntoResponse { pub async fn handle_news(State(_state): State<Arc<AppState>>) -> impl IntoResponse {
let news_items = vec![ let news_items = vec![
("🚀", "General Bots 6.0 Released", "Major update with improved performance and new features", "2 hours ago"), ("", "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"), ("", "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"), ("", "Analytics Dashboard Update", "Real-time metrics and improved visualizations", "3 days ago"),
("🔒", "Security Enhancement", "Enhanced encryption and authentication options", "1 week ago"), ("", "Security Enhancement", "Enhanced encryption and authentication options", "1 week ago"),
("🌐", "Multi-language Support", "Now supporting 15+ languages for bot conversations", "2 weeks ago"), ("", "Multi-language Support", "Now supporting 15+ languages for bot conversations", "2 weeks ago"),
]; ];
let mut html = String::new(); let mut html = String::new();
@ -203,12 +203,12 @@ pub async fn handle_news(State(_state): State<Arc<AppState>>) -> impl IntoRespon
/// GET /api/sources/mcp-servers - MCP Servers tab content /// GET /api/sources/mcp-servers - MCP Servers tab content
pub async fn handle_mcp_servers(State(_state): State<Arc<AppState>>) -> impl IntoResponse { pub async fn handle_mcp_servers(State(_state): State<Arc<AppState>>) -> impl IntoResponse {
let servers = vec![ let servers = vec![
("🗄️", "Database Server", "PostgreSQL, MySQL, SQLite connections", "Active", true), ("", "Database Server", "PostgreSQL, MySQL, SQLite connections", "Active", true),
("📁", "Filesystem Server", "Local and cloud file access", "Active", true), ("", "Filesystem Server", "Local and cloud file access", "Active", true),
("🌐", "Web Server", "HTTP/REST API integrations", "Active", true), ("", "Web Server", "HTTP/REST API integrations", "Active", true),
("📧", "Email Server", "SMTP/IMAP email handling", "Inactive", false), ("", "Email Server", "SMTP/IMAP email handling", "Inactive", false),
("💬", "Slack Server", "Slack workspace integration", "Active", true), ("", "Slack Server", "Slack workspace integration", "Active", true),
("📊", "Analytics Server", "Data processing and reporting", "Active", true), ("", "Analytics Server", "Data processing and reporting", "Active", true),
]; ];
let mut html = String::new(); let mut html = String::new();
@ -259,14 +259,14 @@ pub async fn handle_mcp_servers(State(_state): State<Arc<AppState>>) -> impl Int
/// GET /api/sources/llm-tools - LLM Tools tab content /// GET /api/sources/llm-tools - LLM Tools tab content
pub async fn handle_llm_tools(State(_state): State<Arc<AppState>>) -> impl IntoResponse { pub async fn handle_llm_tools(State(_state): State<Arc<AppState>>) -> impl IntoResponse {
let tools = vec![ let tools = vec![
("🔍", "Web Search", "Search the web for real-time information", true), ("", "Web Search", "Search the web for real-time information", true),
("🧮", "Calculator", "Perform mathematical calculations", true), ("", "Calculator", "Perform mathematical calculations", true),
("📅", "Calendar", "Manage calendar events and schedules", true), ("", "Calendar", "Manage calendar events and schedules", true),
("📝", "Note Taking", "Create and manage notes", true), ("", "Note Taking", "Create and manage notes", true),
("🌤️", "Weather", "Get weather forecasts and conditions", false), ("", "Weather", "Get weather forecasts and conditions", false),
("📰", "News Reader", "Fetch and summarize news articles", false), ("", "News Reader", "Fetch and summarize news articles", false),
("🔗", "URL Fetcher", "Retrieve and parse web content", true), ("", "URL Fetcher", "Retrieve and parse web content", true),
("💾", "Code Executor", "Run code snippets safely", false), ("", "Code Executor", "Run code snippets safely", false),
]; ];
let mut html = String::new(); let mut html = String::new();
@ -318,7 +318,7 @@ pub async fn handle_models(State(_state): State<Arc<AppState>>) -> impl IntoResp
("🦙", "Llama 3.1 70B", "Meta", "Open source large language model", "Available"), ("🦙", "Llama 3.1 70B", "Meta", "Open source large language model", "Available"),
("🔷", "Claude 3.5 Sonnet", "Anthropic", "Advanced reasoning and analysis", "Available"), ("🔷", "Claude 3.5 Sonnet", "Anthropic", "Advanced reasoning and analysis", "Available"),
("💎", "Gemini Pro", "Google", "Multimodal AI with long context", "Available"), ("💎", "Gemini Pro", "Google", "Multimodal AI with long context", "Available"),
("🌐", "Mistral Large", "Mistral AI", "European AI model with strong performance", "Available"), ("", "Mistral Large", "Mistral AI", "European AI model with strong performance", "Available"),
]; ];
let mut html = String::new(); let mut html = String::new();
@ -496,42 +496,42 @@ fn get_prompts_data(category: &str) -> Vec<PromptData> {
title: "Summarize Text".to_string(), title: "Summarize Text".to_string(),
description: "Create concise summaries of long documents or articles".to_string(), description: "Create concise summaries of long documents or articles".to_string(),
category: "writing".to_string(), category: "writing".to_string(),
icon: "📝".to_string(), icon: "".to_string(),
}, },
PromptData { PromptData {
id: "code-review".to_string(), id: "code-review".to_string(),
title: "Code Review".to_string(), title: "Code Review".to_string(),
description: "Analyze code for bugs, improvements, and best practices".to_string(), description: "Analyze code for bugs, improvements, and best practices".to_string(),
category: "coding".to_string(), category: "coding".to_string(),
icon: "💻".to_string(), icon: "".to_string(),
}, },
PromptData { PromptData {
id: "data-analysis".to_string(), id: "data-analysis".to_string(),
title: "Data Analysis".to_string(), title: "Data Analysis".to_string(),
description: "Extract insights and patterns from data sets".to_string(), description: "Extract insights and patterns from data sets".to_string(),
category: "analysis".to_string(), category: "analysis".to_string(),
icon: "📊".to_string(), icon: "".to_string(),
}, },
PromptData { PromptData {
id: "creative-writing".to_string(), id: "creative-writing".to_string(),
title: "Creative Writing".to_string(), title: "Creative Writing".to_string(),
description: "Generate stories, poems, and creative content".to_string(), description: "Generate stories, poems, and creative content".to_string(),
category: "creative".to_string(), category: "creative".to_string(),
icon: "🎨".to_string(), icon: "".to_string(),
}, },
PromptData { PromptData {
id: "email-draft".to_string(), id: "email-draft".to_string(),
title: "Email Draft".to_string(), title: "Email Draft".to_string(),
description: "Compose professional emails quickly".to_string(), description: "Compose professional emails quickly".to_string(),
category: "business".to_string(), category: "business".to_string(),
icon: "📧".to_string(), icon: "".to_string(),
}, },
PromptData { PromptData {
id: "explain-concept".to_string(), id: "explain-concept".to_string(),
title: "Explain Concept".to_string(), title: "Explain Concept".to_string(),
description: "Break down complex topics into simple explanations".to_string(), description: "Break down complex topics into simple explanations".to_string(),
category: "education".to_string(), category: "education".to_string(),
icon: "📚".to_string(), icon: "".to_string(),
}, },
PromptData { PromptData {
id: "debug-code".to_string(), id: "debug-code".to_string(),
@ -545,7 +545,7 @@ fn get_prompts_data(category: &str) -> Vec<PromptData> {
title: "Meeting Notes".to_string(), title: "Meeting Notes".to_string(),
description: "Organize and format meeting discussions".to_string(), description: "Organize and format meeting discussions".to_string(),
category: "business".to_string(), category: "business".to_string(),
icon: "📋".to_string(), icon: "".to_string(),
}, },
]; ];
@ -571,13 +571,13 @@ fn get_templates_data() -> Vec<TemplateData> {
name: "FAQ Bot".to_string(), name: "FAQ Bot".to_string(),
description: "Answer frequently asked questions from your knowledge base".to_string(), description: "Answer frequently asked questions from your knowledge base".to_string(),
category: "Support".to_string(), category: "Support".to_string(),
icon: "".to_string(), icon: "".to_string(),
}, },
TemplateData { TemplateData {
name: "Lead Generation Bot".to_string(), name: "Lead Generation Bot".to_string(),
description: "Qualify leads and collect prospect information".to_string(), description: "Qualify leads and collect prospect information".to_string(),
category: "Sales".to_string(), category: "Sales".to_string(),
icon: "🎯".to_string(), icon: "".to_string(),
}, },
TemplateData { TemplateData {
name: "Onboarding Bot".to_string(), name: "Onboarding Bot".to_string(),
@ -589,13 +589,13 @@ fn get_templates_data() -> Vec<TemplateData> {
name: "Survey Bot".to_string(), name: "Survey Bot".to_string(),
description: "Collect feedback through conversational surveys".to_string(), description: "Collect feedback through conversational surveys".to_string(),
category: "Research".to_string(), category: "Research".to_string(),
icon: "📊".to_string(), icon: "".to_string(),
}, },
TemplateData { TemplateData {
name: "Appointment Scheduler".to_string(), name: "Appointment Scheduler".to_string(),
description: "Book and manage appointments automatically".to_string(), description: "Book and manage appointments automatically".to_string(),
category: "Productivity".to_string(), category: "Productivity".to_string(),
icon: "📅".to_string(), icon: "".to_string(),
}, },
] ]
} }

View file

@ -1371,12 +1371,12 @@ pub async fn handle_task_list_htmx(
<button class="action-btn edit-btn" <button class="action-btn edit-btn"
data-action="edit" data-action="edit"
data-task-id="{}"> data-task-id="{}">
</button> </button>
<button class="action-btn delete-btn" <button class="action-btn delete-btn"
data-action="delete" data-action="delete"
data-task-id="{}"> data-task-id="{}">
🗑
</button> </button>
</div> </div>
</div> </div>
@ -1392,7 +1392,7 @@ pub async fn handle_task_list_htmx(
}, },
if let Some(due) = &task.due_date { if let Some(due) = &task.due_date {
format!( format!(
r#"<span class="task-due-date">📅 {}</span>"#, r#"<span class="task-due-date"> {}</span>"#,
due.format("%Y-%m-%d") due.format("%Y-%m-%d")
) )
} else { } else {

View file

@ -120,7 +120,7 @@ impl VectorDBIndexer {
*running = true; *running = true;
drop(running); drop(running);
info!("🚀 Starting Vector DB Indexer background service"); info!(" Starting Vector DB Indexer background service");
let indexer = Arc::clone(&self); let indexer = Arc::clone(&self);
tokio::spawn(async move { tokio::spawn(async move {
@ -148,7 +148,7 @@ impl VectorDBIndexer {
} }
} }
info!("🔄 Running vector DB indexing cycle..."); info!(" Running vector DB indexing cycle...");
// Get all active users // Get all active users
match self.get_active_users().await { match self.get_active_users().await {
@ -166,7 +166,7 @@ impl VectorDBIndexer {
} }
} }
info!(" Indexing cycle complete"); info!(" Indexing cycle complete");
// Sleep until next cycle // Sleep until next cycle
sleep(Duration::from_secs(self.interval_seconds)).await; sleep(Duration::from_secs(self.interval_seconds)).await;
@ -325,7 +325,7 @@ impl VectorDBIndexer {
if let Err(e) = email_db.index_email(&email, embedding).await { if let Err(e) = email_db.index_email(&email, embedding).await {
error!("Failed to index email {}: {}", email.id, e); error!("Failed to index email {}: {}", email.id, e);
} else { } else {
info!(" Indexed email: {}", email.subject); info!(" Indexed email: {}", email.subject);
} }
} }
Err(e) => { Err(e) => {
@ -401,7 +401,7 @@ impl VectorDBIndexer {
if let Err(e) = drive_db.index_file(&file, embedding).await { if let Err(e) = drive_db.index_file(&file, embedding).await {
error!("Failed to index file {}: {}", file.id, e); error!("Failed to index file {}: {}", file.id, e);
} else { } else {
info!(" Indexed file: {}", file.file_name); info!(" Indexed file: {}", file.file_name);
} }
} }
Err(e) => { Err(e) => {
@ -531,7 +531,7 @@ impl VectorDBIndexer {
/// Trigger immediate indexing for a user /// Trigger immediate indexing for a user
pub async fn trigger_user_indexing(&self, user_id: Uuid, bot_id: Uuid) -> Result<()> { pub async fn trigger_user_indexing(&self, user_id: Uuid, bot_id: Uuid) -> Result<()> {
info!("🔄 Triggering immediate indexing for user {}", user_id); info!(" Triggering immediate indexing for user {}", user_id);
self.index_user_data(user_id, bot_id).await self.index_user_data(user_id, bot_id).await
} }
} }