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:
parent
b204aebd50
commit
583e764bb9
24 changed files with 203 additions and 203 deletions
|
|
@ -631,10 +631,10 @@ pub async fn handle_recent_activity(State(state): State<Arc<AppState>>) -> impl
|
|||
|
||||
for activity in &activities {
|
||||
let icon = match activity.activity_type.as_str() {
|
||||
"session" => "💬",
|
||||
"error" => "⚠️",
|
||||
"bot" => "🤖",
|
||||
_ => "📌",
|
||||
"session" => "",
|
||||
"error" => "",
|
||||
"bot" => "",
|
||||
_ => "",
|
||||
};
|
||||
|
||||
html.push_str("<div class=\"activity-item\">");
|
||||
|
|
@ -773,7 +773,7 @@ pub async fn handle_analytics_chat(
|
|||
|
||||
let mut html = String::new();
|
||||
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(&html_escape(response));
|
||||
html.push_str("</div>");
|
||||
|
|
|
|||
|
|
@ -1059,10 +1059,10 @@ async fn handle_queue_command(state: &Arc<AppState>) -> Result<String, String> {
|
|||
.map_err(|e| e.to_string())??;
|
||||
|
||||
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() {
|
||||
let name = session
|
||||
|
|
@ -1144,12 +1144,12 @@ async fn handle_take_command(
|
|||
.unwrap_or("Unknown");
|
||||
|
||||
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,
|
||||
&session.id.to_string()[..8]
|
||||
))
|
||||
} else {
|
||||
Ok("📭 No conversations waiting in queue.".to_string())
|
||||
Ok(" No conversations waiting in queue.".to_string())
|
||||
}
|
||||
})
|
||||
.await
|
||||
|
|
@ -1165,17 +1165,17 @@ async fn handle_status_command(
|
|||
) -> Result<String, String> {
|
||||
if args.is_empty() {
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
let status = args[0].to_lowercase();
|
||||
let (emoji, text, status_value) = match status.as_str() {
|
||||
"online" => ("🟢", "Online - Available for conversations", "online"),
|
||||
"busy" => ("🟡", "Busy - Handling conversations", "busy"),
|
||||
"away" => ("🟠", "Away - Temporarily unavailable", "away"),
|
||||
"offline" => ("⚫", "Offline - Not available", "offline"),
|
||||
"online" => ("", "Online - Available for conversations", "online"),
|
||||
"busy" => ("", "Busy - Handling conversations", "busy"),
|
||||
"away" => ("", "Away - Temporarily unavailable", "away"),
|
||||
"offline" => ("", "Offline - Not available", "offline"),
|
||||
_ => {
|
||||
return Err(format!(
|
||||
"Invalid status: {}. Use online, busy, away, or offline.",
|
||||
|
|
@ -1306,7 +1306,7 @@ async fn handle_transfer_command(
|
|||
);
|
||||
|
||||
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],
|
||||
target_clean
|
||||
))
|
||||
|
|
@ -1345,7 +1345,7 @@ async fn handle_resolve_command(
|
|||
.map_err(|e| e.to_string())??;
|
||||
|
||||
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]
|
||||
))
|
||||
}
|
||||
|
|
@ -1361,7 +1361,7 @@ async fn handle_tips_command(
|
|||
|
||||
if history.is_empty() {
|
||||
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();
|
||||
|
||||
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 {
|
||||
let emoji = match tip.tip_type {
|
||||
TipType::Intent => "🎯",
|
||||
TipType::Action => "✅",
|
||||
TipType::Warning => "⚠️",
|
||||
TipType::Knowledge => "📚",
|
||||
TipType::History => "📜",
|
||||
TipType::General => "💡",
|
||||
TipType::Intent => "",
|
||||
TipType::Action => "",
|
||||
TipType::Warning => "",
|
||||
TipType::Knowledge => "",
|
||||
TipType::History => "",
|
||||
TipType::General => "",
|
||||
};
|
||||
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()));
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
if !polish_response.changes.is_empty() {
|
||||
|
|
@ -1461,10 +1461,10 @@ async fn handle_replies_command(
|
|||
let (_, Json(replies_response)) = response.into_response().into_parts();
|
||||
|
||||
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() {
|
||||
result.push_str(&format!(
|
||||
|
|
@ -1497,7 +1497,7 @@ async fn handle_summary_command(
|
|||
|
||||
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));
|
||||
|
||||
if !summary.key_points.is_empty() {
|
||||
|
|
@ -1525,13 +1525,13 @@ async fn handle_summary_command(
|
|||
}
|
||||
|
||||
result.push_str(&format!(
|
||||
"📊 {} messages | {} minutes | Sentiment: {}",
|
||||
" {} messages | {} minutes | Sentiment: {}",
|
||||
summary.message_count, summary.duration_minutes, summary.sentiment_trend
|
||||
));
|
||||
|
||||
if !summary.recommended_action.is_empty() {
|
||||
result.push_str(&format!(
|
||||
"\n\n💡 *Recommended:* {}",
|
||||
"\n\n *Recommended:* {}",
|
||||
summary.recommended_action
|
||||
));
|
||||
}
|
||||
|
|
@ -1540,7 +1540,7 @@ async fn handle_summary_command(
|
|||
}
|
||||
|
||||
fn get_help_text() -> String {
|
||||
r#"🤖 *Attendant Commands*
|
||||
r#" *Attendant Commands*
|
||||
|
||||
*Queue Management:*
|
||||
`/queue` - View waiting conversations
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ pub fn register_clear_kb_keyword(
|
|||
match result {
|
||||
Ok(Ok(_)) => {
|
||||
info!(
|
||||
"✅ KB '{}' removed from session {}",
|
||||
" KB '{}' removed from session {}",
|
||||
kb_name, session_clone.id
|
||||
);
|
||||
Ok(Dynamic::UNIT)
|
||||
|
|
@ -131,7 +131,7 @@ fn clear_specific_kb(
|
|||
);
|
||||
} else {
|
||||
info!(
|
||||
"✅ Cleared KB '{}' from session {}, {} KB(s) remaining active",
|
||||
" Cleared KB '{}' from session {}, {} KB(s) remaining active",
|
||||
kb_name, session_id, remaining_count
|
||||
);
|
||||
}
|
||||
|
|
@ -160,7 +160,7 @@ fn clear_all_kbs(
|
|||
|
||||
if rows_affected > 0 {
|
||||
info!(
|
||||
"✅ Cleared {} active KBs from session {}",
|
||||
" Cleared {} active KBs from session {}",
|
||||
rows_affected, session_id
|
||||
);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -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(state.clone(), user.clone(), engine);
|
||||
debug!(" ✓ Math functions registered");
|
||||
debug!(" * Math functions registered");
|
||||
|
||||
// Register date/time functions (NOW, TODAY, YEAR, MONTH, DAY, etc.)
|
||||
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(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(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_functions(state, user, engine);
|
||||
debug!(" ✓ Error handling functions registered");
|
||||
debug!(" * Error handling functions registered");
|
||||
|
||||
debug!("All core BASIC functions registered successfully");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ pub fn register_use_kb_keyword(
|
|||
|
||||
match result {
|
||||
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(Err(e)) => {
|
||||
|
|
@ -163,7 +163,7 @@ fn add_kb_to_session(
|
|||
.map_err(|e| format!("Failed to add KB association: {}", e))?;
|
||||
|
||||
info!(
|
||||
"✅ Added KB '{}' to session {} (collection: {}, path: {})",
|
||||
" Added KB '{}' to session {} (collection: {}, path: {})",
|
||||
kb_name, session_id, qdrant_collection, kb_folder_path
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -263,7 +263,7 @@ fn add_website_to_session(
|
|||
.map_err(|e| format!("Failed to add website to session: {}", e))?;
|
||||
|
||||
info!(
|
||||
"✅ Added website '{}' to session {} (collection: {})",
|
||||
" Added website '{}' to session {} (collection: {})",
|
||||
url, session_id, collection_name
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -350,12 +350,12 @@ async fn fetch_fallback_weather(location: &str) -> Result<String, String> {
|
|||
fn format_weather_response(weather: &WeatherData) -> String {
|
||||
format!(
|
||||
"Current weather in {}:\n\
|
||||
🌡️ Temperature: {:.1}{} (feels like {:.1}{})\n\
|
||||
☁️ Conditions: {}\n\
|
||||
💧 Humidity: {}%\n\
|
||||
💨 Wind: {:.1} m/s {}\n\
|
||||
🔍 Visibility: {:.1} km\n\
|
||||
📊 Pressure: {} hPa",
|
||||
Temperature: {:.1}{} (feels like {:.1}{})\n\
|
||||
Conditions: {}\n\
|
||||
Humidity: {}%\n\
|
||||
Wind: {:.1} m/s {}\n\
|
||||
Visibility: {:.1} km\n\
|
||||
Pressure: {} hPa",
|
||||
weather.location,
|
||||
weather.temperature,
|
||||
weather.temperature_unit,
|
||||
|
|
@ -375,10 +375,10 @@ fn format_forecast_response(weather: &WeatherData) -> String {
|
|||
|
||||
for day in &weather.forecast {
|
||||
response.push_str(&format!(
|
||||
"📅 {}\n\
|
||||
🌡️ High: {:.1}°C, Low: {:.1}°C\n\
|
||||
☁️ {}\n\
|
||||
☔ Rain chance: {}%\n\n",
|
||||
" {}\n\
|
||||
High: {:.1}°C, Low: {:.1}°C\n\
|
||||
{}\n\
|
||||
Rain chance: {}%\n\n",
|
||||
day.date, day.temp_high, day.temp_low, day.description, day.rain_chance
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ impl FileTree {
|
|||
for bucket in buckets {
|
||||
if let Some(name) = bucket.name() {
|
||||
let icon = if name.ends_with(".gbai") {
|
||||
"🤖"
|
||||
""
|
||||
} else {
|
||||
"📦"
|
||||
};
|
||||
|
|
@ -53,7 +53,7 @@ impl FileTree {
|
|||
}
|
||||
Err(e) => {
|
||||
self.items.push((
|
||||
format!("✗ Error: {}", e),
|
||||
format!("x Error: {}", e),
|
||||
TreeNode::Bucket {
|
||||
name: String::new(),
|
||||
},
|
||||
|
|
@ -62,7 +62,7 @@ impl FileTree {
|
|||
}
|
||||
} else {
|
||||
self.items.push((
|
||||
"✗ Drive not connected".to_string(),
|
||||
"x Drive not connected".to_string(),
|
||||
TreeNode::Bucket {
|
||||
name: String::new(),
|
||||
},
|
||||
|
|
@ -116,7 +116,7 @@ impl FileTree {
|
|||
async fn load_bucket_contents(&mut self, bucket: &str, prefix: &str) -> Result<()> {
|
||||
self.items.clear();
|
||||
self.items.push((
|
||||
"⬆️ .. (go back)".to_string(),
|
||||
" .. (go back)".to_string(),
|
||||
TreeNode::Folder {
|
||||
bucket: bucket.to_string(),
|
||||
path: "..".to_string(),
|
||||
|
|
@ -195,13 +195,13 @@ impl FileTree {
|
|||
files.sort_by(|(a, _), (b, _)| a.cmp(b));
|
||||
for (name, full_path) in files {
|
||||
let icon = if name.ends_with(".bas") {
|
||||
"⚙️"
|
||||
""
|
||||
} else if name.ends_with(".ast") {
|
||||
"🔧"
|
||||
""
|
||||
} else if name.ends_with(".csv") {
|
||||
"📊"
|
||||
""
|
||||
} else if name.ends_with(".gbkb") {
|
||||
"📚"
|
||||
""
|
||||
} else if name.ends_with(".json") {
|
||||
"🔖"
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -350,9 +350,9 @@ impl XtreeUI {
|
|||
.iter()
|
||||
.map(|(comp_name, process, _port)| {
|
||||
let status = if status_panel::StatusPanel::check_component_running(process) {
|
||||
format!("🟢 {}", comp_name)
|
||||
format!(" {}", comp_name)
|
||||
} else {
|
||||
format!("🔴 {}", comp_name)
|
||||
format!(" {}", comp_name)
|
||||
};
|
||||
status
|
||||
})
|
||||
|
|
@ -467,7 +467,7 @@ impl XtreeUI {
|
|||
.style(Style::default().bg(bg));
|
||||
|
||||
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
|
||||
);
|
||||
let status_para = Paragraph::new(status_text)
|
||||
|
|
|
|||
|
|
@ -104,9 +104,9 @@ impl StatusPanel {
|
|||
|
||||
for (comp_name, process, port) in components {
|
||||
let status = if Self::check_component_running(process) {
|
||||
format!("🟢 ONLINE [Port: {}]", port)
|
||||
format!(" ONLINE [Port: {}]", port)
|
||||
} else {
|
||||
"🔴 OFFLINE".to_string()
|
||||
" OFFLINE".to_string()
|
||||
};
|
||||
lines.push(format!(" {:<10} {}", comp_name, status));
|
||||
}
|
||||
|
|
@ -138,7 +138,7 @@ impl StatusPanel {
|
|||
} else {
|
||||
" "
|
||||
};
|
||||
lines.push(format!(" {} 🤖 {}", marker, bot_name));
|
||||
lines.push(format!(" {} {}", marker, bot_name));
|
||||
|
||||
if let Some(ref selected) = selected_bot {
|
||||
if selected == &bot_name {
|
||||
|
|
|
|||
|
|
@ -666,7 +666,7 @@ impl StartupWizard {
|
|||
stdout,
|
||||
cursor::MoveTo(4, 19 + i as u16),
|
||||
SetForegroundColor(Color::Green),
|
||||
Print("✓ "),
|
||||
Print("* "),
|
||||
ResetColor,
|
||||
Print(format!("{}", component))
|
||||
)?;
|
||||
|
|
@ -718,7 +718,7 @@ impl StartupWizard {
|
|||
execute!(
|
||||
stdout,
|
||||
SetForegroundColor(Color::Green),
|
||||
Print("▶ "),
|
||||
Print("> "),
|
||||
Print(format!("{:<25}", name)),
|
||||
SetForegroundColor(Color::DarkGrey),
|
||||
Print(format!(" {}", desc)),
|
||||
|
|
@ -775,8 +775,8 @@ impl StartupWizard {
|
|||
for (i, (component, _, can_toggle)) in options.iter().enumerate() {
|
||||
execute!(stdout, cursor::MoveTo(4, start_row + i as u16))?;
|
||||
|
||||
let checkbox = if selected[i] { "[✓]" } else { "[ ]" };
|
||||
let prefix = if i == cursor { "▶" } else { " " };
|
||||
let checkbox = if selected[i] { "[*]" } else { "[ ]" };
|
||||
let prefix = if i == cursor { ">" } else { " " };
|
||||
|
||||
if !can_toggle {
|
||||
execute!(
|
||||
|
|
@ -930,7 +930,7 @@ pub fn apply_wizard_config(config: &WizardConfig) -> io::Result<()> {
|
|||
|
||||
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!("\n Next steps:");
|
||||
println!(" 1. Run: botserver start");
|
||||
|
|
|
|||
|
|
@ -491,13 +491,13 @@ impl BootstrapManager {
|
|||
|
||||
pub async fn bootstrap(&mut self) -> Result<()> {
|
||||
// Generate certificates first (including for Vault)
|
||||
info!("🔒 Generating TLS certificates...");
|
||||
info!("Generating TLS certificates...");
|
||||
if let Err(e) = self.generate_certificates().await {
|
||||
error!("Failed to generate certificates: {}", e);
|
||||
}
|
||||
|
||||
// Create Vault configuration with mTLS
|
||||
info!("📝 Creating Vault configuration...");
|
||||
info!("Creating Vault configuration...");
|
||||
if let Err(e) = self.create_vault_config().await {
|
||||
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
|
||||
if component == "tables" {
|
||||
info!("🚀 Starting PostgreSQL database...");
|
||||
info!("Starting PostgreSQL database...");
|
||||
match pm.start("tables") {
|
||||
Ok(_) => {
|
||||
info!("PostgreSQL started successfully");
|
||||
|
|
@ -575,7 +575,7 @@ impl BootstrapManager {
|
|||
}
|
||||
|
||||
// Run migrations using direct connection (Vault not set up yet)
|
||||
info!("🔄 Running database migrations...");
|
||||
info!("Running database migrations...");
|
||||
let database_url =
|
||||
format!("postgres://gbuser:{}@localhost:5432/botserver", db_password);
|
||||
match diesel::PgConnection::establish(&database_url) {
|
||||
|
|
@ -583,7 +583,7 @@ impl BootstrapManager {
|
|||
if let Err(e) = self.apply_migrations(&mut conn) {
|
||||
error!("Failed to apply migrations: {}", e);
|
||||
} else {
|
||||
info!("✓ Database migrations applied");
|
||||
info!("Database migrations applied");
|
||||
}
|
||||
}
|
||||
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 {
|
||||
error!("Failed to create Directory config files: {}", e);
|
||||
}
|
||||
|
|
@ -599,7 +599,7 @@ impl BootstrapManager {
|
|||
|
||||
// Directory configuration - setup happens after install starts Zitadel
|
||||
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 {
|
||||
// Don't fail completely - Zitadel may still be usable with first instance setup
|
||||
warn!("Directory additional setup had issues: {}", e);
|
||||
|
|
@ -608,7 +608,7 @@ impl BootstrapManager {
|
|||
|
||||
// After Vault is installed, START the server then initialize it
|
||||
if component == "vault" {
|
||||
info!("🚀 Starting Vault server...");
|
||||
info!("Starting Vault server...");
|
||||
match pm.start("vault") {
|
||||
Ok(_) => {
|
||||
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
|
||||
.setup_vault(
|
||||
&db_password,
|
||||
|
|
@ -634,14 +634,14 @@ impl BootstrapManager {
|
|||
}
|
||||
|
||||
// Initialize the global SecretsManager so other components can use Vault
|
||||
info!("🔑 Initializing SecretsManager...");
|
||||
info!("Initializing SecretsManager...");
|
||||
debug!(
|
||||
"VAULT_ADDR={:?}, VAULT_TOKEN set={}",
|
||||
std::env::var("VAULT_ADDR").ok(),
|
||||
std::env::var("VAULT_TOKEN").is_ok()
|
||||
);
|
||||
match init_secrets_manager().await {
|
||||
Ok(_) => info!("✓ SecretsManager initialized successfully"),
|
||||
Ok(_) => info!("SecretsManager initialized successfully"),
|
||||
Err(e) => {
|
||||
error!("Failed to initialize SecretsManager: {}", e);
|
||||
// Don't continue if SecretsManager fails - it's required for DB connection
|
||||
|
|
@ -654,21 +654,21 @@ impl BootstrapManager {
|
|||
}
|
||||
|
||||
if component == "email" {
|
||||
info!("🔧 Auto-configuring Email (Stalwart)...");
|
||||
info!("Auto-configuring Email (Stalwart)...");
|
||||
if let Err(e) = self.setup_email().await {
|
||||
error!("Failed to setup Email: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
if component == "proxy" {
|
||||
info!("🔧 Configuring Caddy reverse proxy...");
|
||||
info!("Configuring Caddy reverse proxy...");
|
||||
if let Err(e) = self.setup_caddy_proxy().await {
|
||||
error!("Failed to setup Caddy: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
if component == "dns" {
|
||||
info!("🔧 Configuring CoreDNS for dynamic DNS...");
|
||||
info!("Configuring CoreDNS for dynamic DNS...");
|
||||
if let Err(e) = self.setup_coredns().await {
|
||||
error!("Failed to setup CoreDNS: {}", e);
|
||||
}
|
||||
|
|
@ -1217,7 +1217,7 @@ meet IN A 127.0.0.1
|
|||
vault_addr, root_token, db_password
|
||||
))
|
||||
.output()?;
|
||||
info!(" ✓ Stored database credentials");
|
||||
info!(" Stored database credentials");
|
||||
|
||||
// Drive credentials
|
||||
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
|
||||
))
|
||||
.output()?;
|
||||
info!(" ✓ Stored drive credentials");
|
||||
info!(" Stored drive credentials");
|
||||
|
||||
// Cache credentials
|
||||
let _ = std::process::Command::new("sh")
|
||||
|
|
@ -1237,7 +1237,7 @@ meet IN A 127.0.0.1
|
|||
vault_addr, root_token, cache_password
|
||||
))
|
||||
.output()?;
|
||||
info!(" ✓ Stored cache credentials");
|
||||
info!(" Stored cache credentials");
|
||||
|
||||
// Directory placeholder (will be updated after Zitadel setup)
|
||||
let _ = std::process::Command::new("sh")
|
||||
|
|
@ -1247,7 +1247,7 @@ meet IN A 127.0.0.1
|
|||
vault_addr, root_token
|
||||
))
|
||||
.output()?;
|
||||
info!(" ✓ Created directory placeholder");
|
||||
info!(" Created directory placeholder");
|
||||
|
||||
// LLM placeholder
|
||||
let _ = std::process::Command::new("sh")
|
||||
|
|
@ -1257,7 +1257,7 @@ meet IN A 127.0.0.1
|
|||
vault_addr, root_token
|
||||
))
|
||||
.output()?;
|
||||
info!(" ✓ Created LLM placeholder");
|
||||
info!(" Created LLM placeholder");
|
||||
|
||||
// Email placeholder
|
||||
let _ = std::process::Command::new("sh")
|
||||
|
|
@ -1267,7 +1267,7 @@ meet IN A 127.0.0.1
|
|||
vault_addr, root_token
|
||||
))
|
||||
.output()?;
|
||||
info!(" ✓ Created email placeholder");
|
||||
info!(" Created email placeholder");
|
||||
|
||||
// Encryption key
|
||||
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
|
||||
))
|
||||
.output()?;
|
||||
info!(" ✓ Generated and stored encryption key");
|
||||
info!(" Generated and stored encryption key");
|
||||
|
||||
// Write .env file with ONLY Vault variables - NO LEGACY FALLBACK
|
||||
info!("Writing .env file with Vault configuration...");
|
||||
|
|
@ -1308,9 +1308,9 @@ VAULT_CACHE_TTL=300
|
|||
);
|
||||
|
||||
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!(" 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!(" Admin: {} / {}", config.admin_user, config.admin_pass);
|
||||
if config.directory_integration {
|
||||
info!(" 🔗 Integrated with Directory for authentication");
|
||||
info!(" Integrated with Directory for authentication");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -1562,7 +1562,7 @@ VAULT_CACHE_TTL=300
|
|||
|
||||
if synced > 0 {
|
||||
info!(
|
||||
"✓ Synced {} config values for bot {} (skipped {} empty lines)",
|
||||
"Synced {} config values for bot {} (skipped {} empty lines)",
|
||||
synced, bot_id, skipped
|
||||
);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -79,12 +79,12 @@ impl OAuthProvider {
|
|||
/// Get icon/emoji for UI
|
||||
pub fn icon(&self) -> &'static str {
|
||||
match self {
|
||||
OAuthProvider::Google => "🔵",
|
||||
OAuthProvider::Discord => "🎮",
|
||||
OAuthProvider::Reddit => "🟠",
|
||||
OAuthProvider::Twitter => "🐦",
|
||||
OAuthProvider::Microsoft => "🪟",
|
||||
OAuthProvider::Facebook => "📘",
|
||||
OAuthProvider::Google => "",
|
||||
OAuthProvider::Discord => "",
|
||||
OAuthProvider::Reddit => "",
|
||||
OAuthProvider::Twitter => "",
|
||||
OAuthProvider::Microsoft => "",
|
||||
OAuthProvider::Facebook => "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,12 +32,12 @@ pub async fn run() -> Result<()> {
|
|||
for component in components {
|
||||
if pm.is_installed(component.name) {
|
||||
match pm.start(component.name) {
|
||||
Ok(_) => println!("✓ Started {}", component.name),
|
||||
Err(e) => eprintln!("✗ Failed to start {}: {}", component.name, e),
|
||||
Ok(_) => println!("* Started {}", component.name),
|
||||
Err(e) => eprintln!("x Failed to start {}: {}", component.name, e),
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("✓ BotServer components started");
|
||||
println!("* BotServer components started");
|
||||
}
|
||||
"stop" => {
|
||||
println!("Stopping all components...");
|
||||
|
|
@ -48,7 +48,7 @@ pub async fn run() -> Result<()> {
|
|||
.arg(component.termination_command)
|
||||
.output();
|
||||
}
|
||||
println!("✓ BotServer components stopped");
|
||||
println!("* BotServer components stopped");
|
||||
}
|
||||
"restart" => {
|
||||
println!("Restarting BotServer...");
|
||||
|
|
@ -77,7 +77,7 @@ pub async fn run() -> Result<()> {
|
|||
let _ = pm.start(component.name);
|
||||
}
|
||||
}
|
||||
println!("✓ BotServer restarted");
|
||||
println!("* BotServer restarted");
|
||||
}
|
||||
"install" => {
|
||||
if args.len() < 3 {
|
||||
|
|
@ -97,7 +97,7 @@ pub async fn run() -> Result<()> {
|
|||
};
|
||||
let pm = PackageManager::new(mode, tenant)?;
|
||||
pm.install(component).await?;
|
||||
println!("✓ Component '{}' installed successfully", component);
|
||||
println!("* Component '{}' installed successfully", component);
|
||||
}
|
||||
"remove" => {
|
||||
if args.len() < 3 {
|
||||
|
|
@ -117,7 +117,7 @@ pub async fn run() -> Result<()> {
|
|||
};
|
||||
let pm = PackageManager::new(mode, tenant)?;
|
||||
pm.remove(component)?;
|
||||
println!("✓ Component '{}' removed successfully", component);
|
||||
println!("* Component '{}' removed successfully", component);
|
||||
}
|
||||
"list" => {
|
||||
let mode = if args.contains(&"--container".to_string()) {
|
||||
|
|
@ -134,7 +134,7 @@ pub async fn run() -> Result<()> {
|
|||
println!("Available components:");
|
||||
for component in pm.list() {
|
||||
let status = if pm.is_installed(&component) {
|
||||
"✓ installed"
|
||||
"* installed"
|
||||
} else {
|
||||
" available"
|
||||
};
|
||||
|
|
@ -159,9 +159,9 @@ pub async fn run() -> Result<()> {
|
|||
};
|
||||
let pm = PackageManager::new(mode, tenant)?;
|
||||
if pm.is_installed(component) {
|
||||
println!("✓ Component '{}' is installed", component);
|
||||
println!("* Component '{}' is installed", component);
|
||||
} else {
|
||||
println!("✗ Component '{}' is not installed", component);
|
||||
println!("x Component '{}' is not installed", component);
|
||||
}
|
||||
}
|
||||
"--help" | "-h" => {
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ impl DirectorySetup {
|
|||
|
||||
/// Initialize directory with default configuration
|
||||
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
|
||||
if let Ok(existing_config) = self.load_existing_config().await {
|
||||
|
|
@ -133,19 +133,19 @@ impl DirectorySetup {
|
|||
|
||||
// Create default organization
|
||||
let org = self.create_default_organization().await?;
|
||||
log::info!("✅ Created default organization: {}", org.name);
|
||||
log::info!(" Created default organization: {}", org.name);
|
||||
|
||||
// Create default user
|
||||
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
|
||||
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
|
||||
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 {
|
||||
base_url: self.base_url.clone(),
|
||||
|
|
@ -159,15 +159,15 @@ impl DirectorySetup {
|
|||
|
||||
// Save configuration
|
||||
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!(
|
||||
"📧 Default user: {} / {}",
|
||||
" Default user: {} / {}",
|
||||
config.default_user.email,
|
||||
config.default_user.password
|
||||
);
|
||||
log::info!("🌐 Login at: {}", self.base_url);
|
||||
log::info!(" Login at: {}", self.base_url);
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ impl EmailSetup {
|
|||
&mut self,
|
||||
directory_config_path: Option<PathBuf>,
|
||||
) -> Result<EmailConfig> {
|
||||
log::info!("🔧 Initializing Email (Stalwart) server...");
|
||||
log::info!(" Initializing Email (Stalwart) server...");
|
||||
|
||||
// Check if already initialized
|
||||
if let Ok(existing_config) = self.load_existing_config().await {
|
||||
|
|
@ -106,17 +106,17 @@ impl EmailSetup {
|
|||
|
||||
// Create default domain
|
||||
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
|
||||
let directory_integration = if let Some(dir_config_path) = directory_config_path {
|
||||
match self.setup_directory_integration(&dir_config_path).await {
|
||||
Ok(_) => {
|
||||
log::info!("✅ Integrated with Directory for authentication");
|
||||
log::info!(" Integrated with Directory for authentication");
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("⚠️ Directory integration failed: {}", e);
|
||||
log::warn!(" Directory integration failed: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
@ -126,7 +126,7 @@ impl EmailSetup {
|
|||
|
||||
// Create admin account
|
||||
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 {
|
||||
base_url: self.base_url.clone(),
|
||||
|
|
@ -141,9 +141,9 @@ impl EmailSetup {
|
|||
|
||||
// Save configuration
|
||||
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!("📬 IMAP: localhost:143 (993 for TLS)");
|
||||
log::info!("👤 Admin: {} / {}", config.admin_user, config.admin_pass);
|
||||
|
|
@ -288,7 +288,7 @@ impl EmailSetup {
|
|||
|
||||
if !email.is_empty() {
|
||||
self.create_user_mailbox(username, password, email).await?;
|
||||
log::info!("✅ Created mailbox for: {}", email);
|
||||
log::info!(" Created mailbox for: {}", email);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ pub async fn handle_save(
|
|||
Ok(_) => {
|
||||
let mut html = String::new();
|
||||
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("</div>");
|
||||
Html(html)
|
||||
|
|
@ -266,7 +266,7 @@ pub async fn handle_save(
|
|||
Err(e) => {
|
||||
let mut html = String::new();
|
||||
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(&html_escape(&e));
|
||||
html.push_str("</span>");
|
||||
|
|
@ -289,13 +289,13 @@ pub async fn handle_validate(
|
|||
|
||||
if validation.valid {
|
||||
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("</div>");
|
||||
} else {
|
||||
html.push_str("<div class=\"validation-errors\">");
|
||||
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(&validation.errors.len().to_string());
|
||||
html.push_str(" error(s) found</span>");
|
||||
|
|
@ -318,7 +318,7 @@ pub async fn handle_validate(
|
|||
if !validation.warnings.is_empty() {
|
||||
html.push_str("<div class=\"validation-warnings\">");
|
||||
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(&validation.warnings.len().to_string());
|
||||
html.push_str(" warning(s)</span>");
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ pub fn convert_tree_to_items(tree: &FileTree) -> Vec<FileItem> {
|
|||
size: None,
|
||||
modified: None,
|
||||
icon: if name.ends_with(".gbai") {
|
||||
"🤖".to_string()
|
||||
"".to_string()
|
||||
} else {
|
||||
"📦".to_string()
|
||||
},
|
||||
|
|
@ -586,13 +586,13 @@ pub async fn create_folder(
|
|||
/// Get appropriate icon for file based on extension
|
||||
fn get_file_icon(path: &str) -> String {
|
||||
if path.ends_with(".bas") {
|
||||
"⚙️".to_string()
|
||||
"".to_string()
|
||||
} else if path.ends_with(".ast") {
|
||||
"🔧".to_string()
|
||||
"".to_string()
|
||||
} else if path.ends_with(".csv") {
|
||||
"📊".to_string()
|
||||
"".to_string()
|
||||
} else if path.ends_with(".gbkb") {
|
||||
"📚".to_string()
|
||||
"".to_string()
|
||||
} else if path.ends_with(".json") {
|
||||
"🔖".to_string()
|
||||
} 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") {
|
||||
"📦".to_string()
|
||||
} else if path.ends_with(".jpg") || path.ends_with(".png") || path.ends_with(".gif") {
|
||||
"🖼️".to_string()
|
||||
"".to_string()
|
||||
} else {
|
||||
"📄".to_string()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1782,10 +1782,10 @@ pub async fn list_folders_htmx(
|
|||
|
||||
let mut html = String::new();
|
||||
for (folder_name, icon, count) in &[
|
||||
("inbox", "📥", folder_counts.get("INBOX").unwrap_or(&0)),
|
||||
("sent", "📤", folder_counts.get("Sent").unwrap_or(&0)),
|
||||
("drafts", "📝", folder_counts.get("Drafts").unwrap_or(&0)),
|
||||
("trash", "🗑️", folder_counts.get("Trash").unwrap_or(&0)),
|
||||
("inbox", "", folder_counts.get("INBOX").unwrap_or(&0)),
|
||||
("sent", "", folder_counts.get("Sent").unwrap_or(&0)),
|
||||
("drafts", "", folder_counts.get("Drafts").unwrap_or(&0)),
|
||||
("trash", "", folder_counts.get("Trash").unwrap_or(&0)),
|
||||
] {
|
||||
let active = if *folder_name == "inbox" { "active" } else { "" };
|
||||
let count_badge = if **count > 0 {
|
||||
|
|
|
|||
|
|
@ -796,7 +796,7 @@ pub async fn handle_save_document(
|
|||
log::info!("Document saved: {} at {}", doc_id, path);
|
||||
let mut html = String::new();
|
||||
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("</div>");
|
||||
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-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("</div>");
|
||||
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 {
|
||||
let mut html = String::new();
|
||||
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(&html_escape(message));
|
||||
html.push_str("</span>");
|
||||
|
|
|
|||
|
|
@ -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(&html_escape(id));
|
||||
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("<span class=\"collection-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(&html_escape(&id_clone));
|
||||
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("<span class=\"collection-name\">");
|
||||
html.push_str(&html_escape(&name_clone));
|
||||
|
|
@ -324,7 +324,7 @@ pub async fn handle_search(
|
|||
|
||||
if results.is_empty() {
|
||||
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("<p>Try different keywords or check your spelling</p>");
|
||||
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 {
|
||||
let prompts = vec![
|
||||
(
|
||||
"📚",
|
||||
"",
|
||||
"Getting Started",
|
||||
"Learn the basics and set up your first bot",
|
||||
),
|
||||
("🔧", "Configuration", "Customize settings and preferences"),
|
||||
("", "Configuration", "Customize settings and preferences"),
|
||||
(
|
||||
"🔌",
|
||||
"Integrations",
|
||||
"Connect with external services and APIs",
|
||||
),
|
||||
("🚀", "Deployment", "Deploy your bot to production"),
|
||||
("🔒", "Security", "Best practices for securing your bot"),
|
||||
("📊", "Analytics", "Monitor and analyze bot performance"),
|
||||
("", "Deployment", "Deploy your bot to production"),
|
||||
("", "Security", "Best practices for securing your bot"),
|
||||
("", "Analytics", "Monitor and analyze bot performance"),
|
||||
];
|
||||
|
||||
let mut html = String::new();
|
||||
|
|
|
|||
|
|
@ -45,13 +45,13 @@ pub async fn handle_prompts(
|
|||
html.push_str("<div class=\"category-list\">");
|
||||
|
||||
let categories = vec![
|
||||
("all", "All Prompts", "📋"),
|
||||
("writing", "Writing", "✍️"),
|
||||
("coding", "Coding", "💻"),
|
||||
("analysis", "Analysis", "📊"),
|
||||
("creative", "Creative", "🎨"),
|
||||
("business", "Business", "💼"),
|
||||
("education", "Education", "📚"),
|
||||
("all", "All Prompts", ""),
|
||||
("writing", "Writing", ""),
|
||||
("coding", "Coding", ""),
|
||||
("analysis", "Analysis", ""),
|
||||
("creative", "Creative", ""),
|
||||
("business", "Business", ""),
|
||||
("education", "Education", ""),
|
||||
];
|
||||
|
||||
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
|
||||
pub async fn handle_news(State(_state): State<Arc<AppState>>) -> 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"),
|
||||
("", "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();
|
||||
|
|
@ -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
|
||||
pub async fn handle_mcp_servers(State(_state): State<Arc<AppState>>) -> 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),
|
||||
("", "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();
|
||||
|
|
@ -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
|
||||
pub async fn handle_llm_tools(State(_state): State<Arc<AppState>>) -> 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),
|
||||
("", "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();
|
||||
|
|
@ -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"),
|
||||
("🔷", "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"),
|
||||
("", "Mistral Large", "Mistral AI", "European AI model with strong performance", "Available"),
|
||||
];
|
||||
|
||||
let mut html = String::new();
|
||||
|
|
@ -496,42 +496,42 @@ fn get_prompts_data(category: &str) -> Vec<PromptData> {
|
|||
title: "Summarize Text".to_string(),
|
||||
description: "Create concise summaries of long documents or articles".to_string(),
|
||||
category: "writing".to_string(),
|
||||
icon: "📝".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(),
|
||||
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(),
|
||||
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(),
|
||||
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(),
|
||||
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(),
|
||||
icon: "".to_string(),
|
||||
},
|
||||
PromptData {
|
||||
id: "debug-code".to_string(),
|
||||
|
|
@ -545,7 +545,7 @@ fn get_prompts_data(category: &str) -> Vec<PromptData> {
|
|||
title: "Meeting Notes".to_string(),
|
||||
description: "Organize and format meeting discussions".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(),
|
||||
description: "Answer frequently asked questions from your knowledge base".to_string(),
|
||||
category: "Support".to_string(),
|
||||
icon: "❓".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(),
|
||||
icon: "".to_string(),
|
||||
},
|
||||
TemplateData {
|
||||
name: "Onboarding Bot".to_string(),
|
||||
|
|
@ -589,13 +589,13 @@ fn get_templates_data() -> Vec<TemplateData> {
|
|||
name: "Survey Bot".to_string(),
|
||||
description: "Collect feedback through conversational surveys".to_string(),
|
||||
category: "Research".to_string(),
|
||||
icon: "📊".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(),
|
||||
icon: "".to_string(),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1371,12 +1371,12 @@ pub async fn handle_task_list_htmx(
|
|||
<button class="action-btn edit-btn"
|
||||
data-action="edit"
|
||||
data-task-id="{}">
|
||||
✏️
|
||||
|
||||
</button>
|
||||
<button class="action-btn delete-btn"
|
||||
data-action="delete"
|
||||
data-task-id="{}">
|
||||
🗑️
|
||||
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1392,7 +1392,7 @@ pub async fn handle_task_list_htmx(
|
|||
},
|
||||
if let Some(due) = &task.due_date {
|
||||
format!(
|
||||
r#"<span class="task-due-date">📅 {}</span>"#,
|
||||
r#"<span class="task-due-date"> {}</span>"#,
|
||||
due.format("%Y-%m-%d")
|
||||
)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ impl VectorDBIndexer {
|
|||
*running = true;
|
||||
drop(running);
|
||||
|
||||
info!("🚀 Starting Vector DB Indexer background service");
|
||||
info!(" Starting Vector DB Indexer background service");
|
||||
|
||||
let indexer = Arc::clone(&self);
|
||||
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
|
||||
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(Duration::from_secs(self.interval_seconds)).await;
|
||||
|
|
@ -325,7 +325,7 @@ impl VectorDBIndexer {
|
|||
if let Err(e) = email_db.index_email(&email, embedding).await {
|
||||
error!("Failed to index email {}: {}", email.id, e);
|
||||
} else {
|
||||
info!("✅ Indexed email: {}", email.subject);
|
||||
info!(" Indexed email: {}", email.subject);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
|
|
@ -401,7 +401,7 @@ impl VectorDBIndexer {
|
|||
if let Err(e) = drive_db.index_file(&file, embedding).await {
|
||||
error!("Failed to index file {}: {}", file.id, e);
|
||||
} else {
|
||||
info!("✅ Indexed file: {}", file.file_name);
|
||||
info!(" Indexed file: {}", file.file_name);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
|
|
@ -531,7 +531,7 @@ impl VectorDBIndexer {
|
|||
|
||||
/// Trigger immediate indexing for a user
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue