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 {
|
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>");
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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 => "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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" => {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>");
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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>");
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue