fix: zero warnings in botserver
- drive/vectordb.rs: fix struct field name db_path -> _db_path - meet/conversations.rs: prefix 38 unused variables with underscore - console/mod.rs: rename border_active/inactive, title_bg/fg - console/mod.rs: fix unnested or-pattern KeyCode::Char - email/mod.rs: remove unnecessary raw string hashes, fix unused state - attendance/drive.rs: update deprecated aws_config::from_env() - Cargo.toml: attendance feature extends drive for AWS deps
This commit is contained in:
parent
d09a584443
commit
db6d7a9859
6 changed files with 159 additions and 217 deletions
|
|
@ -70,7 +70,7 @@ mail = ["email"]
|
||||||
|
|
||||||
# ===== ENTERPRISE FEATURES =====
|
# ===== ENTERPRISE FEATURES =====
|
||||||
compliance = ["dep:csv"]
|
compliance = ["dep:csv"]
|
||||||
attendance = []
|
attendance = ["drive"]
|
||||||
directory = []
|
directory = []
|
||||||
weba = []
|
weba = []
|
||||||
timeseries = []
|
timeseries = []
|
||||||
|
|
@ -171,7 +171,7 @@ livekit = { version = "0.7", optional = true }
|
||||||
qdrant-client = { version = "1.12", optional = true }
|
qdrant-client = { version = "1.12", optional = true }
|
||||||
|
|
||||||
# File Storage & Drive (drive feature)
|
# File Storage & Drive (drive feature)
|
||||||
aws-config = { version = "1.8.8", optional = true }
|
aws-config = { version = "1.8.8", features = ["behavior-version-latest"], optional = true }
|
||||||
aws-sdk-s3 = { version = "1.109.0", features = ["behavior-version-latest"], optional = true }
|
aws-sdk-s3 = { version = "1.109.0", features = ["behavior-version-latest"], optional = true }
|
||||||
pdf-extract = { version = "0.10.0", optional = true }
|
pdf-extract = { version = "0.10.0", optional = true }
|
||||||
zip = { version = "2.2", optional = true }
|
zip = { version = "2.2", optional = true }
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
use aws_config::BehaviorVersion;
|
||||||
use aws_sdk_s3::primitives::ByteStream;
|
use aws_sdk_s3::primitives::ByteStream;
|
||||||
use aws_sdk_s3::Client;
|
use aws_sdk_s3::Client;
|
||||||
use chrono::TimeZone;
|
use chrono::TimeZone;
|
||||||
|
|
@ -9,7 +7,6 @@ use serde::{Deserialize, Serialize};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct AttendanceDriveConfig {
|
pub struct AttendanceDriveConfig {
|
||||||
pub bucket_name: String,
|
pub bucket_name: String,
|
||||||
|
|
@ -29,7 +26,6 @@ impl Default for AttendanceDriveConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AttendanceDriveService {
|
pub struct AttendanceDriveService {
|
||||||
config: AttendanceDriveConfig,
|
config: AttendanceDriveConfig,
|
||||||
|
|
@ -37,15 +33,14 @@ pub struct AttendanceDriveService {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AttendanceDriveService {
|
impl AttendanceDriveService {
|
||||||
|
|
||||||
pub async fn new(config: AttendanceDriveConfig) -> Result<Self> {
|
pub async fn new(config: AttendanceDriveConfig) -> Result<Self> {
|
||||||
let sdk_config = if let Some(region) = &config.region {
|
let sdk_config = if let Some(region) = &config.region {
|
||||||
aws_config::from_env()
|
aws_config::defaults(BehaviorVersion::latest())
|
||||||
.region(aws_config::Region::new(region.clone()))
|
.region(aws_config::Region::new(region.clone()))
|
||||||
.load()
|
.load()
|
||||||
.await
|
.await
|
||||||
} else {
|
} else {
|
||||||
aws_config::from_env().load().await
|
aws_config::defaults(BehaviorVersion::latest()).load().await
|
||||||
};
|
};
|
||||||
|
|
||||||
let client = Client::new(&sdk_config);
|
let client = Client::new(&sdk_config);
|
||||||
|
|
@ -53,17 +48,14 @@ impl AttendanceDriveService {
|
||||||
Ok(Self { config, client })
|
Ok(Self { config, client })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn with_client(config: AttendanceDriveConfig, client: Client) -> Self {
|
pub fn with_client(config: AttendanceDriveConfig, client: Client) -> Self {
|
||||||
Self { config, client }
|
Self { config, client }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn get_record_key(&self, record_id: &str) -> String {
|
fn get_record_key(&self, record_id: &str) -> String {
|
||||||
format!("{}{}", self.config.prefix, record_id)
|
format!("{}{}", self.config.prefix, record_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn upload_record(&self, record_id: &str, data: Vec<u8>) -> Result<()> {
|
pub async fn upload_record(&self, record_id: &str, data: Vec<u8>) -> Result<()> {
|
||||||
let key = self.get_record_key(record_id);
|
let key = self.get_record_key(record_id);
|
||||||
|
|
||||||
|
|
@ -90,7 +82,6 @@ impl AttendanceDriveService {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn download_record(&self, record_id: &str) -> Result<Vec<u8>> {
|
pub async fn download_record(&self, record_id: &str) -> Result<Vec<u8>> {
|
||||||
let key = self.get_record_key(record_id);
|
let key = self.get_record_key(record_id);
|
||||||
|
|
||||||
|
|
@ -120,7 +111,6 @@ impl AttendanceDriveService {
|
||||||
Ok(data.into_bytes().to_vec())
|
Ok(data.into_bytes().to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn list_records(&self, prefix: Option<&str>) -> Result<Vec<String>> {
|
pub async fn list_records(&self, prefix: Option<&str>) -> Result<Vec<String>> {
|
||||||
let list_prefix = if let Some(p) = prefix {
|
let list_prefix = if let Some(p) = prefix {
|
||||||
format!("{}{}", self.config.prefix, p)
|
format!("{}{}", self.config.prefix, p)
|
||||||
|
|
@ -157,7 +147,6 @@ impl AttendanceDriveService {
|
||||||
if let Some(contents) = result.contents {
|
if let Some(contents) = result.contents {
|
||||||
for obj in contents {
|
for obj in contents {
|
||||||
if let Some(key) = obj.key {
|
if let Some(key) = obj.key {
|
||||||
|
|
||||||
if let Some(record_id) = key.strip_prefix(&self.config.prefix) {
|
if let Some(record_id) = key.strip_prefix(&self.config.prefix) {
|
||||||
records.push(record_id.to_string());
|
records.push(record_id.to_string());
|
||||||
}
|
}
|
||||||
|
|
@ -176,7 +165,6 @@ impl AttendanceDriveService {
|
||||||
Ok(records)
|
Ok(records)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn delete_record(&self, record_id: &str) -> Result<()> {
|
pub async fn delete_record(&self, record_id: &str) -> Result<()> {
|
||||||
let key = self.get_record_key(record_id);
|
let key = self.get_record_key(record_id);
|
||||||
|
|
||||||
|
|
@ -199,7 +187,6 @@ impl AttendanceDriveService {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn delete_records(&self, record_ids: &[String]) -> Result<()> {
|
pub async fn delete_records(&self, record_ids: &[String]) -> Result<()> {
|
||||||
if record_ids.is_empty() {
|
if record_ids.is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|
@ -211,7 +198,6 @@ impl AttendanceDriveService {
|
||||||
self.config.bucket_name
|
self.config.bucket_name
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
for chunk in record_ids.chunks(1000) {
|
for chunk in record_ids.chunks(1000) {
|
||||||
let objects: Vec<_> = chunk
|
let objects: Vec<_> = chunk
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -244,7 +230,6 @@ impl AttendanceDriveService {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn record_exists(&self, record_id: &str) -> Result<bool> {
|
pub async fn record_exists(&self, record_id: &str) -> Result<bool> {
|
||||||
let key = self.get_record_key(record_id);
|
let key = self.get_record_key(record_id);
|
||||||
|
|
||||||
|
|
@ -270,7 +255,6 @@ impl AttendanceDriveService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn sync_records(&self, local_path: PathBuf) -> Result<SyncResult> {
|
pub async fn sync_records(&self, local_path: PathBuf) -> Result<SyncResult> {
|
||||||
if !self.config.sync_enabled {
|
if !self.config.sync_enabled {
|
||||||
log::debug!("Attendance drive sync is disabled");
|
log::debug!("Attendance drive sync is disabled");
|
||||||
|
|
@ -315,14 +299,12 @@ impl AttendanceDriveService {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
if self.record_exists(&file_name).await? {
|
if self.record_exists(&file_name).await? {
|
||||||
log::debug!("Record {} already exists in drive, skipping", file_name);
|
log::debug!("Record {} already exists in drive, skipping", file_name);
|
||||||
skipped += 1;
|
skipped += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
match fs::read(&path).await {
|
match fs::read(&path).await {
|
||||||
Ok(data) => match self.upload_record(&file_name, data).await {
|
Ok(data) => match self.upload_record(&file_name, data).await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
|
|
@ -357,7 +339,6 @@ impl AttendanceDriveService {
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn get_record_metadata(&self, record_id: &str) -> Result<RecordMetadata> {
|
pub async fn get_record_metadata(&self, record_id: &str) -> Result<RecordMetadata> {
|
||||||
let key = self.get_record_key(record_id);
|
let key = self.get_record_key(record_id);
|
||||||
|
|
||||||
|
|
@ -382,7 +363,6 @@ impl AttendanceDriveService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||||
pub struct SyncResult {
|
pub struct SyncResult {
|
||||||
pub uploaded: usize,
|
pub uploaded: usize,
|
||||||
|
|
@ -390,7 +370,6 @@ pub struct SyncResult {
|
||||||
pub skipped: usize,
|
pub skipped: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct RecordMetadata {
|
pub struct RecordMetadata {
|
||||||
pub size: usize,
|
pub size: usize,
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,6 @@ impl XtreeUI {
|
||||||
let backend = CrosstermBackend::new(stdout);
|
let backend = CrosstermBackend::new(stdout);
|
||||||
let mut terminal = Terminal::new(backend)?;
|
let mut terminal = Terminal::new(backend)?;
|
||||||
|
|
||||||
|
|
||||||
if let Err(e) = init_logger(self.log_panel.clone()) {
|
if let Err(e) = init_logger(self.log_panel.clone()) {
|
||||||
eprintln!("Warning: Could not initialize UI logger: {}", e);
|
eprintln!("Warning: Could not initialize UI logger: {}", e);
|
||||||
}
|
}
|
||||||
|
|
@ -123,12 +122,10 @@ impl XtreeUI {
|
||||||
let mut last_blink = std::time::Instant::now();
|
let mut last_blink = std::time::Instant::now();
|
||||||
let rt = tokio::runtime::Runtime::new()?;
|
let rt = tokio::runtime::Runtime::new()?;
|
||||||
loop {
|
loop {
|
||||||
|
|
||||||
if self.app_state.is_none() {
|
if self.app_state.is_none() {
|
||||||
if let Some(ref state_rx) = self.state_channel {
|
if let Some(ref state_rx) = self.state_channel {
|
||||||
if let Ok(mut rx) = state_rx.try_lock() {
|
if let Ok(mut rx) = state_rx.try_lock() {
|
||||||
if let Ok(app_state) = rx.try_recv() {
|
if let Ok(app_state) = rx.try_recv() {
|
||||||
|
|
||||||
self.file_tree = Some(FileTree::new(app_state.clone()));
|
self.file_tree = Some(FileTree::new(app_state.clone()));
|
||||||
self.status_panel = Some(StatusPanel::new(app_state.clone()));
|
self.status_panel = Some(StatusPanel::new(app_state.clone()));
|
||||||
self.chat_panel = Some(ChatPanel::new(app_state.clone()));
|
self.chat_panel = Some(ChatPanel::new(app_state.clone()));
|
||||||
|
|
@ -136,7 +133,6 @@ impl XtreeUI {
|
||||||
self.active_panel = ActivePanel::FileTree;
|
self.active_panel = ActivePanel::FileTree;
|
||||||
self.bootstrap_status = "Ready".to_string();
|
self.bootstrap_status = "Ready".to_string();
|
||||||
|
|
||||||
|
|
||||||
if let Ok(mut log_panel) = self.log_panel.lock() {
|
if let Ok(mut log_panel) = self.log_panel.lock() {
|
||||||
log_panel.add_log("AppState received - UI fully initialized");
|
log_panel.add_log("AppState received - UI fully initialized");
|
||||||
}
|
}
|
||||||
|
|
@ -205,14 +201,14 @@ impl XtreeUI {
|
||||||
}
|
}
|
||||||
fn render(&mut self, f: &mut Frame, cursor_blink: bool) {
|
fn render(&mut self, f: &mut Frame, cursor_blink: bool) {
|
||||||
let bg = Color::Rgb(0, 30, 100);
|
let bg = Color::Rgb(0, 30, 100);
|
||||||
let border_active = Color::Rgb(85, 255, 255);
|
let border_focused = Color::Rgb(85, 255, 255);
|
||||||
let border_inactive = Color::Rgb(170, 170, 170);
|
let border_dim = Color::Rgb(170, 170, 170);
|
||||||
let text = Color::Rgb(255, 255, 255);
|
let text = Color::Rgb(255, 255, 255);
|
||||||
let highlight = Color::Rgb(0, 170, 170);
|
let highlight = Color::Rgb(0, 170, 170);
|
||||||
let title_bg = Color::Rgb(170, 170, 170);
|
let header_bg_color = Color::Rgb(170, 170, 170);
|
||||||
let title_fg = Color::Rgb(0, 0, 0);
|
let header_text_color = Color::Rgb(0, 0, 0);
|
||||||
if self.app_state.is_none() {
|
if self.app_state.is_none() {
|
||||||
self.render_loading(f, bg, text, border_active, title_bg, title_fg);
|
self.render_loading(f, bg, text, border_focused, header_bg_color, header_text_color);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let main_chunks = Layout::default()
|
let main_chunks = Layout::default()
|
||||||
|
|
@ -223,7 +219,7 @@ impl XtreeUI {
|
||||||
Constraint::Length(12),
|
Constraint::Length(12),
|
||||||
])
|
])
|
||||||
.split(f.area());
|
.split(f.area());
|
||||||
self.render_header(f, main_chunks[0], bg, title_bg, title_fg);
|
self.render_header(f, main_chunks[0], bg, header_bg_color, header_text_color);
|
||||||
if self.editor.is_some() {
|
if self.editor.is_some() {
|
||||||
let content_chunks = Layout::default()
|
let content_chunks = Layout::default()
|
||||||
.direction(Direction::Horizontal)
|
.direction(Direction::Horizontal)
|
||||||
|
|
@ -238,28 +234,28 @@ impl XtreeUI {
|
||||||
content_chunks[0],
|
content_chunks[0],
|
||||||
bg,
|
bg,
|
||||||
text,
|
text,
|
||||||
border_active,
|
border_focused,
|
||||||
border_inactive,
|
border_dim,
|
||||||
highlight,
|
highlight,
|
||||||
title_bg,
|
header_bg_color,
|
||||||
title_fg,
|
header_text_color,
|
||||||
);
|
);
|
||||||
if let Some(editor) = &mut self.editor {
|
if let Some(editor) = &mut self.editor {
|
||||||
let area = content_chunks[1];
|
let area = content_chunks[1];
|
||||||
editor.set_visible_lines(area.height.saturating_sub(4) as usize);
|
editor.set_visible_lines(area.height.saturating_sub(4) as usize);
|
||||||
let is_active = self.active_panel == ActivePanel::Editor;
|
let is_active = self.active_panel == ActivePanel::Editor;
|
||||||
let border_color = if is_active {
|
let border_color = if is_active {
|
||||||
border_active
|
border_focused
|
||||||
} else {
|
} else {
|
||||||
border_inactive
|
border_dim
|
||||||
};
|
};
|
||||||
let title_style = if is_active {
|
let title_style = if is_active {
|
||||||
Style::default()
|
Style::default()
|
||||||
.fg(title_fg)
|
.fg(header_text_color)
|
||||||
.bg(title_bg)
|
.bg(header_bg_color)
|
||||||
.add_modifier(Modifier::BOLD)
|
.add_modifier(Modifier::BOLD)
|
||||||
} else {
|
} else {
|
||||||
Style::default().fg(title_fg).bg(title_bg)
|
Style::default().fg(header_text_color).bg(header_bg_color)
|
||||||
};
|
};
|
||||||
let title_text = format!(" EDITOR: {} ", editor.file_path());
|
let title_text = format!(" EDITOR: {} ", editor.file_path());
|
||||||
let block = Block::default()
|
let block = Block::default()
|
||||||
|
|
@ -279,11 +275,11 @@ impl XtreeUI {
|
||||||
content_chunks[2],
|
content_chunks[2],
|
||||||
bg,
|
bg,
|
||||||
text,
|
text,
|
||||||
border_active,
|
border_focused,
|
||||||
border_inactive,
|
border_dim,
|
||||||
highlight,
|
highlight,
|
||||||
title_bg,
|
header_bg_color,
|
||||||
title_fg,
|
header_text_color,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let content_chunks = Layout::default()
|
let content_chunks = Layout::default()
|
||||||
|
|
@ -299,11 +295,11 @@ impl XtreeUI {
|
||||||
content_chunks[0],
|
content_chunks[0],
|
||||||
bg,
|
bg,
|
||||||
text,
|
text,
|
||||||
border_active,
|
border_focused,
|
||||||
border_inactive,
|
border_dim,
|
||||||
highlight,
|
highlight,
|
||||||
title_bg,
|
header_bg_color,
|
||||||
title_fg,
|
header_text_color,
|
||||||
);
|
);
|
||||||
let right_chunks = Layout::default()
|
let right_chunks = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
|
|
@ -314,22 +310,22 @@ impl XtreeUI {
|
||||||
right_chunks[0],
|
right_chunks[0],
|
||||||
bg,
|
bg,
|
||||||
text,
|
text,
|
||||||
border_active,
|
border_focused,
|
||||||
border_inactive,
|
border_dim,
|
||||||
highlight,
|
highlight,
|
||||||
title_bg,
|
header_bg_color,
|
||||||
title_fg,
|
header_text_color,
|
||||||
);
|
);
|
||||||
self.render_chat(
|
self.render_status(
|
||||||
f,
|
f,
|
||||||
content_chunks[2],
|
content_chunks[1],
|
||||||
bg,
|
bg,
|
||||||
text,
|
text,
|
||||||
border_active,
|
border_focused,
|
||||||
border_inactive,
|
border_dim,
|
||||||
highlight,
|
highlight,
|
||||||
title_bg,
|
header_bg_color,
|
||||||
title_fg,
|
header_text_color,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
self.render_logs(
|
self.render_logs(
|
||||||
|
|
@ -337,11 +333,11 @@ impl XtreeUI {
|
||||||
main_chunks[2],
|
main_chunks[2],
|
||||||
bg,
|
bg,
|
||||||
text,
|
text,
|
||||||
border_active,
|
border_focused,
|
||||||
border_inactive,
|
border_dim,
|
||||||
highlight,
|
highlight,
|
||||||
title_bg,
|
header_bg_color,
|
||||||
title_fg,
|
header_text_color,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
fn render_header(
|
fn render_header(
|
||||||
|
|
@ -349,10 +345,10 @@ impl XtreeUI {
|
||||||
f: &mut Frame,
|
f: &mut Frame,
|
||||||
area: Rect,
|
area: Rect,
|
||||||
_bg: Color,
|
_bg: Color,
|
||||||
title_bg: Color,
|
header_bg_color: Color,
|
||||||
title_fg: Color,
|
header_text_color: Color,
|
||||||
) {
|
) {
|
||||||
let block = Block::default().style(Style::default().bg(title_bg));
|
let block = Block::default().style(Style::default().bg(header_bg_color));
|
||||||
f.render_widget(block, area);
|
f.render_widget(block, area);
|
||||||
let title = if self.app_state.is_some() {
|
let title = if self.app_state.is_some() {
|
||||||
let components = vec![
|
let components = vec![
|
||||||
|
|
@ -385,8 +381,8 @@ impl XtreeUI {
|
||||||
let title_span = Span::styled(
|
let title_span = Span::styled(
|
||||||
title,
|
title,
|
||||||
Style::default()
|
Style::default()
|
||||||
.fg(title_fg)
|
.fg(header_text_color)
|
||||||
.bg(title_bg)
|
.bg(header_bg_color)
|
||||||
.add_modifier(Modifier::BOLD),
|
.add_modifier(Modifier::BOLD),
|
||||||
);
|
);
|
||||||
f.render_widget(
|
f.render_widget(
|
||||||
|
|
@ -405,10 +401,9 @@ impl XtreeUI {
|
||||||
bg: Color,
|
bg: Color,
|
||||||
text: Color,
|
text: Color,
|
||||||
border: Color,
|
border: Color,
|
||||||
title_bg: Color,
|
header_bg_color: Color,
|
||||||
title_fg: Color,
|
header_text_color: Color,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
let main_chunks = Layout::default()
|
let main_chunks = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints([
|
.constraints([
|
||||||
|
|
@ -418,8 +413,7 @@ impl XtreeUI {
|
||||||
])
|
])
|
||||||
.split(f.area());
|
.split(f.area());
|
||||||
|
|
||||||
|
let header_block = Block::default().style(Style::default().bg(header_bg_color));
|
||||||
let header_block = Block::default().style(Style::default().bg(title_bg));
|
|
||||||
f.render_widget(header_block, main_chunks[0]);
|
f.render_widget(header_block, main_chunks[0]);
|
||||||
|
|
||||||
let title = " GENERAL BOTS ";
|
let title = " GENERAL BOTS ";
|
||||||
|
|
@ -428,8 +422,8 @@ impl XtreeUI {
|
||||||
let title_span = Span::styled(
|
let title_span = Span::styled(
|
||||||
title,
|
title,
|
||||||
Style::default()
|
Style::default()
|
||||||
.fg(title_fg)
|
.fg(header_text_color)
|
||||||
.bg(title_bg)
|
.bg(header_bg_color)
|
||||||
.add_modifier(Modifier::BOLD),
|
.add_modifier(Modifier::BOLD),
|
||||||
);
|
);
|
||||||
f.render_widget(
|
f.render_widget(
|
||||||
|
|
@ -442,7 +436,6 @@ impl XtreeUI {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
let content_chunks = Layout::default()
|
let content_chunks = Layout::default()
|
||||||
.direction(Direction::Horizontal)
|
.direction(Direction::Horizontal)
|
||||||
.constraints([
|
.constraints([
|
||||||
|
|
@ -452,11 +445,10 @@ impl XtreeUI {
|
||||||
])
|
])
|
||||||
.split(main_chunks[1]);
|
.split(main_chunks[1]);
|
||||||
|
|
||||||
|
|
||||||
let file_block = Block::default()
|
let file_block = Block::default()
|
||||||
.title(Span::styled(
|
.title(Span::styled(
|
||||||
" FILE EXPLORER ",
|
" FILE EXPLORER ",
|
||||||
Style::default().fg(title_fg).bg(title_bg),
|
Style::default().fg(header_text_color).bg(header_bg_color),
|
||||||
))
|
))
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.border_style(Style::default().fg(border))
|
.border_style(Style::default().fg(border))
|
||||||
|
|
@ -466,7 +458,6 @@ impl XtreeUI {
|
||||||
.style(Style::default().fg(Color::DarkGray));
|
.style(Style::default().fg(Color::DarkGray));
|
||||||
f.render_widget(file_text, content_chunks[0]);
|
f.render_widget(file_text, content_chunks[0]);
|
||||||
|
|
||||||
|
|
||||||
let middle_chunks = Layout::default()
|
let middle_chunks = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
|
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
|
||||||
|
|
@ -475,7 +466,7 @@ impl XtreeUI {
|
||||||
let status_block = Block::default()
|
let status_block = Block::default()
|
||||||
.title(Span::styled(
|
.title(Span::styled(
|
||||||
" STATUS ",
|
" STATUS ",
|
||||||
Style::default().fg(title_fg).bg(title_bg),
|
Style::default().fg(header_text_color).bg(header_bg_color),
|
||||||
))
|
))
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.border_style(Style::default().fg(border))
|
.border_style(Style::default().fg(border))
|
||||||
|
|
@ -490,18 +481,16 @@ impl XtreeUI {
|
||||||
.style(Style::default().fg(text));
|
.style(Style::default().fg(text));
|
||||||
f.render_widget(status_para, middle_chunks[0]);
|
f.render_widget(status_para, middle_chunks[0]);
|
||||||
|
|
||||||
|
|
||||||
let empty_block = Block::default()
|
let empty_block = Block::default()
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.border_style(Style::default().fg(Color::DarkGray))
|
.border_style(Style::default().fg(Color::DarkGray))
|
||||||
.style(Style::default().bg(bg));
|
.style(Style::default().bg(bg));
|
||||||
f.render_widget(empty_block, middle_chunks[1]);
|
f.render_widget(empty_block, middle_chunks[1]);
|
||||||
|
|
||||||
|
|
||||||
let chat_block = Block::default()
|
let chat_block = Block::default()
|
||||||
.title(Span::styled(
|
.title(Span::styled(
|
||||||
" CHAT ",
|
" CHAT ",
|
||||||
Style::default().fg(title_fg).bg(title_bg),
|
Style::default().fg(header_text_color).bg(header_bg_color),
|
||||||
))
|
))
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.border_style(Style::default().fg(border))
|
.border_style(Style::default().fg(border))
|
||||||
|
|
@ -511,17 +500,15 @@ impl XtreeUI {
|
||||||
.style(Style::default().fg(Color::DarkGray));
|
.style(Style::default().fg(Color::DarkGray));
|
||||||
f.render_widget(chat_text, content_chunks[2]);
|
f.render_widget(chat_text, content_chunks[2]);
|
||||||
|
|
||||||
|
|
||||||
let logs_block = Block::default()
|
let logs_block = Block::default()
|
||||||
.title(Span::styled(
|
.title(Span::styled(
|
||||||
" SYSTEM LOGS ",
|
" SYSTEM LOGS ",
|
||||||
Style::default().fg(title_fg).bg(title_bg),
|
Style::default().fg(header_text_color).bg(header_bg_color),
|
||||||
))
|
))
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.border_style(Style::default().fg(border))
|
.border_style(Style::default().fg(border))
|
||||||
.style(Style::default().bg(bg));
|
.style(Style::default().bg(bg));
|
||||||
|
|
||||||
|
|
||||||
let logs_visible_lines = main_chunks[2].height.saturating_sub(2) as usize;
|
let logs_visible_lines = main_chunks[2].height.saturating_sub(2) as usize;
|
||||||
let logs_content = if let Ok(panel) = self.log_panel.lock() {
|
let logs_content = if let Ok(panel) = self.log_panel.lock() {
|
||||||
panel.render(logs_visible_lines)
|
panel.render(logs_visible_lines)
|
||||||
|
|
@ -541,11 +528,11 @@ impl XtreeUI {
|
||||||
area: Rect,
|
area: Rect,
|
||||||
bg: Color,
|
bg: Color,
|
||||||
text: Color,
|
text: Color,
|
||||||
border_active: Color,
|
border_focused: Color,
|
||||||
border_inactive: Color,
|
border_dim: Color,
|
||||||
highlight: Color,
|
highlight: Color,
|
||||||
title_bg: Color,
|
header_bg_color: Color,
|
||||||
title_fg: Color,
|
header_text_color: Color,
|
||||||
) {
|
) {
|
||||||
if let Some(file_tree) = &self.file_tree {
|
if let Some(file_tree) = &self.file_tree {
|
||||||
let items = file_tree.render_items();
|
let items = file_tree.render_items();
|
||||||
|
|
@ -569,17 +556,17 @@ impl XtreeUI {
|
||||||
.collect();
|
.collect();
|
||||||
let is_active = self.active_panel == ActivePanel::FileTree;
|
let is_active = self.active_panel == ActivePanel::FileTree;
|
||||||
let border_color = if is_active {
|
let border_color = if is_active {
|
||||||
border_active
|
border_focused
|
||||||
} else {
|
} else {
|
||||||
border_inactive
|
border_dim
|
||||||
};
|
};
|
||||||
let title_style = if is_active {
|
let title_style = if is_active {
|
||||||
Style::default()
|
Style::default()
|
||||||
.fg(title_fg)
|
.fg(header_text_color)
|
||||||
.bg(title_bg)
|
.bg(header_bg_color)
|
||||||
.add_modifier(Modifier::BOLD)
|
.add_modifier(Modifier::BOLD)
|
||||||
} else {
|
} else {
|
||||||
Style::default().fg(title_fg).bg(title_bg)
|
Style::default().fg(header_text_color).bg(header_bg_color)
|
||||||
};
|
};
|
||||||
let block = Block::default()
|
let block = Block::default()
|
||||||
.title(Span::styled(" FILE EXPLORER ", title_style))
|
.title(Span::styled(" FILE EXPLORER ", title_style))
|
||||||
|
|
@ -596,11 +583,11 @@ impl XtreeUI {
|
||||||
area: Rect,
|
area: Rect,
|
||||||
bg: Color,
|
bg: Color,
|
||||||
text: Color,
|
text: Color,
|
||||||
border_active: Color,
|
border_focused: Color,
|
||||||
border_inactive: Color,
|
border_dim: Color,
|
||||||
_highlight: Color,
|
_highlight: Color,
|
||||||
title_bg: Color,
|
header_bg_color: Color,
|
||||||
title_fg: Color,
|
header_text_color: Color,
|
||||||
) {
|
) {
|
||||||
let selected_bot_opt = self.file_tree.as_ref().and_then(|ft| ft.get_selected_bot());
|
let selected_bot_opt = self.file_tree.as_ref().and_then(|ft| ft.get_selected_bot());
|
||||||
let status_text = if let Some(status_panel) = &mut self.status_panel {
|
let status_text = if let Some(status_panel) = &mut self.status_panel {
|
||||||
|
|
@ -613,17 +600,17 @@ impl XtreeUI {
|
||||||
};
|
};
|
||||||
let is_active = self.active_panel == ActivePanel::Status;
|
let is_active = self.active_panel == ActivePanel::Status;
|
||||||
let border_color = if is_active {
|
let border_color = if is_active {
|
||||||
border_active
|
border_focused
|
||||||
} else {
|
} else {
|
||||||
border_inactive
|
border_dim
|
||||||
};
|
};
|
||||||
let title_style = if is_active {
|
let title_style = if is_active {
|
||||||
Style::default()
|
Style::default()
|
||||||
.fg(title_fg)
|
.fg(header_text_color)
|
||||||
.bg(title_bg)
|
.bg(header_bg_color)
|
||||||
.add_modifier(Modifier::BOLD)
|
.add_modifier(Modifier::BOLD)
|
||||||
} else {
|
} else {
|
||||||
Style::default().fg(title_fg).bg(title_bg)
|
Style::default().fg(header_text_color).bg(header_bg_color)
|
||||||
};
|
};
|
||||||
let block = Block::default()
|
let block = Block::default()
|
||||||
.title(Span::styled(" SYSTEM STATUS ", title_style))
|
.title(Span::styled(" SYSTEM STATUS ", title_style))
|
||||||
|
|
@ -642,26 +629,26 @@ impl XtreeUI {
|
||||||
area: Rect,
|
area: Rect,
|
||||||
bg: Color,
|
bg: Color,
|
||||||
text: Color,
|
text: Color,
|
||||||
border_active: Color,
|
border_focused: Color,
|
||||||
border_inactive: Color,
|
border_dim: Color,
|
||||||
_highlight: Color,
|
_highlight: Color,
|
||||||
title_bg: Color,
|
header_bg_color: Color,
|
||||||
title_fg: Color,
|
header_text_color: Color,
|
||||||
) {
|
) {
|
||||||
if let Some(chat_panel) = &self.chat_panel {
|
if let Some(chat_panel) = &self.chat_panel {
|
||||||
let is_active = self.active_panel == ActivePanel::Chat;
|
let is_active = self.active_panel == ActivePanel::Chat;
|
||||||
let border_color = if is_active {
|
let border_color = if is_active {
|
||||||
border_active
|
border_focused
|
||||||
} else {
|
} else {
|
||||||
border_inactive
|
border_dim
|
||||||
};
|
};
|
||||||
let title_style = if is_active {
|
let title_style = if is_active {
|
||||||
Style::default()
|
Style::default()
|
||||||
.fg(title_fg)
|
.fg(header_text_color)
|
||||||
.bg(title_bg)
|
.bg(header_bg_color)
|
||||||
.add_modifier(Modifier::BOLD)
|
.add_modifier(Modifier::BOLD)
|
||||||
} else {
|
} else {
|
||||||
Style::default().fg(title_fg).bg(title_bg)
|
Style::default().fg(header_text_color).bg(header_bg_color)
|
||||||
};
|
};
|
||||||
let selected_bot = if let Some(file_tree) = &self.file_tree {
|
let selected_bot = if let Some(file_tree) = &self.file_tree {
|
||||||
file_tree
|
file_tree
|
||||||
|
|
@ -690,13 +677,12 @@ impl XtreeUI {
|
||||||
area: Rect,
|
area: Rect,
|
||||||
bg: Color,
|
bg: Color,
|
||||||
text: Color,
|
text: Color,
|
||||||
border_active: Color,
|
border_focused: Color,
|
||||||
border_inactive: Color,
|
border_dim: Color,
|
||||||
_highlight: Color,
|
_highlight: Color,
|
||||||
title_bg: Color,
|
header_bg_color: Color,
|
||||||
title_fg: Color,
|
header_text_color: Color,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
let visible_lines = area.height.saturating_sub(2) as usize;
|
let visible_lines = area.height.saturating_sub(2) as usize;
|
||||||
|
|
||||||
let log_panel = self.log_panel.try_lock();
|
let log_panel = self.log_panel.try_lock();
|
||||||
|
|
@ -716,20 +702,19 @@ impl XtreeUI {
|
||||||
|
|
||||||
let is_active = self.active_panel == ActivePanel::Logs;
|
let is_active = self.active_panel == ActivePanel::Logs;
|
||||||
let border_color = if is_active {
|
let border_color = if is_active {
|
||||||
border_active
|
border_focused
|
||||||
} else {
|
} else {
|
||||||
border_inactive
|
border_dim
|
||||||
};
|
};
|
||||||
let title_style = if is_active {
|
let title_style = if is_active {
|
||||||
Style::default()
|
Style::default()
|
||||||
.fg(title_fg)
|
.fg(header_text_color)
|
||||||
.bg(title_bg)
|
.bg(header_bg_color)
|
||||||
.add_modifier(Modifier::BOLD)
|
.add_modifier(Modifier::BOLD)
|
||||||
} else {
|
} else {
|
||||||
Style::default().fg(title_fg).bg(title_bg)
|
Style::default().fg(header_text_color).bg(header_bg_color)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
let scroll_indicator = if can_scroll_up && can_scroll_down {
|
let scroll_indicator = if can_scroll_up && can_scroll_down {
|
||||||
" [^v] "
|
" [^v] "
|
||||||
} else if can_scroll_up {
|
} else if can_scroll_up {
|
||||||
|
|
@ -760,7 +745,7 @@ impl XtreeUI {
|
||||||
async fn handle_input(&mut self, key: KeyCode, modifiers: KeyModifiers) -> Result<()> {
|
async fn handle_input(&mut self, key: KeyCode, modifiers: KeyModifiers) -> Result<()> {
|
||||||
if modifiers.contains(KeyModifiers::CONTROL) {
|
if modifiers.contains(KeyModifiers::CONTROL) {
|
||||||
match key {
|
match key {
|
||||||
KeyCode::Char('c') | KeyCode::Char('q') => {
|
KeyCode::Char('c' | 'q') => {
|
||||||
self.should_quit = true;
|
self.should_quit = true;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ impl UserDriveVectorDB {
|
||||||
user_id,
|
user_id,
|
||||||
bot_id,
|
bot_id,
|
||||||
collection_name,
|
collection_name,
|
||||||
db_path,
|
_db_path: db_path,
|
||||||
#[cfg(feature = "vectordb")]
|
#[cfg(feature = "vectordb")]
|
||||||
client: None,
|
client: None,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1197,7 +1197,7 @@ fn update_email_read_status(
|
||||||
let now = Utc::now();
|
let now = Utc::now();
|
||||||
|
|
||||||
diesel::sql_query(
|
diesel::sql_query(
|
||||||
r#"UPDATE sent_email_tracking
|
r"UPDATE sent_email_tracking
|
||||||
SET
|
SET
|
||||||
is_read = true,
|
is_read = true,
|
||||||
read_count = read_count + 1,
|
read_count = read_count + 1,
|
||||||
|
|
@ -1206,7 +1206,7 @@ fn update_email_read_status(
|
||||||
last_read_ip = $3,
|
last_read_ip = $3,
|
||||||
user_agent = COALESCE(user_agent, $4),
|
user_agent = COALESCE(user_agent, $4),
|
||||||
updated_at = $2
|
updated_at = $2
|
||||||
WHERE tracking_id = $1"#,
|
WHERE tracking_id = $1",
|
||||||
)
|
)
|
||||||
.bind::<diesel::sql_types::Uuid, _>(tracking_id)
|
.bind::<diesel::sql_types::Uuid, _>(tracking_id)
|
||||||
.bind::<diesel::sql_types::Timestamptz, _>(now)
|
.bind::<diesel::sql_types::Timestamptz, _>(now)
|
||||||
|
|
@ -1266,8 +1266,8 @@ fn get_tracking_record(
|
||||||
}
|
}
|
||||||
|
|
||||||
let row: TrackingRow = diesel::sql_query(
|
let row: TrackingRow = diesel::sql_query(
|
||||||
r#"SELECT tracking_id, to_email, subject, sent_at, is_read, read_at, read_count
|
r"SELECT tracking_id, to_email, subject, sent_at, is_read, read_at, read_count
|
||||||
FROM sent_email_tracking WHERE tracking_id = $1"#,
|
FROM sent_email_tracking WHERE tracking_id = $1",
|
||||||
)
|
)
|
||||||
.bind::<diesel::sql_types::Uuid, _>(tracking_id)
|
.bind::<diesel::sql_types::Uuid, _>(tracking_id)
|
||||||
.get_result(&mut db_conn)
|
.get_result(&mut db_conn)
|
||||||
|
|
@ -1402,11 +1402,11 @@ fn calculate_tracking_stats(
|
||||||
}
|
}
|
||||||
|
|
||||||
let stats: StatsRow = diesel::sql_query(
|
let stats: StatsRow = diesel::sql_query(
|
||||||
r#"SELECT
|
r"SELECT
|
||||||
COUNT(*) as total_sent,
|
COUNT(*) as total_sent,
|
||||||
COUNT(*) FILTER (WHERE is_read = true) as total_read,
|
COUNT(*) FILTER (WHERE is_read = true) as total_read,
|
||||||
AVG(EXTRACT(EPOCH FROM (read_at - sent_at)) / 3600) FILTER (WHERE is_read = true) as avg_time_hours
|
AVG(EXTRACT(EPOCH FROM (read_at - sent_at)) / 3600) FILTER (WHERE is_read = true) as avg_time_hours
|
||||||
FROM sent_email_tracking"#
|
FROM sent_email_tracking",
|
||||||
)
|
)
|
||||||
.get_result(&mut db_conn)
|
.get_result(&mut db_conn)
|
||||||
.map_err(|e| format!("Stats query failed: {}", e))?;
|
.map_err(|e| format!("Stats query failed: {}", e))?;
|
||||||
|
|
@ -1831,10 +1831,10 @@ pub async fn list_emails_htmx(
|
||||||
|
|
||||||
if html.is_empty() {
|
if html.is_empty() {
|
||||||
html = format!(
|
html = format!(
|
||||||
r##"<div class="empty-state">
|
r#"<div class="empty-state">
|
||||||
<h3>No emails in {}</h3>
|
<h3>No emails in {}</h3>
|
||||||
<p>This folder is empty</p>
|
<p>This folder is empty</p>
|
||||||
</div>"##,
|
</div>"#,
|
||||||
folder
|
folder
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1899,7 +1899,7 @@ pub async fn list_folders_htmx(
|
||||||
};
|
};
|
||||||
let count_badge = if **count > 0 {
|
let count_badge = if **count > 0 {
|
||||||
format!(
|
format!(
|
||||||
r##"<span style="margin-left: auto; font-size: 0.875rem; color: #64748b;">{}</span>"##,
|
r#"<span style="margin-left: auto; font-size: 0.875rem; color: #64748b;">{}</span>"#,
|
||||||
count
|
count
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1992,9 +1992,9 @@ pub async fn get_email_content_htmx(
|
||||||
|
|
||||||
let Some(account) = account else {
|
let Some(account) = account else {
|
||||||
return Ok(axum::response::Html(
|
return Ok(axum::response::Html(
|
||||||
r##"<div class="mail-content-view">
|
r#"<div class="mail-content-view">
|
||||||
<p>No email account configured</p>
|
<p>No email account configured</p>
|
||||||
</div>"##
|
</div>"#
|
||||||
.to_string(),
|
.to_string(),
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
@ -2137,7 +2137,7 @@ pub async fn get_email(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn track_click(
|
pub async fn track_click(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path((campaign_id, email)): Path<(String, String)>,
|
Path((campaign_id, email)): Path<(String, String)>,
|
||||||
) -> Result<Json<ApiResponse<()>>, EmailError> {
|
) -> Result<Json<ApiResponse<()>>, EmailError> {
|
||||||
info!(
|
info!(
|
||||||
|
|
@ -2338,7 +2338,7 @@ pub async fn search_emails_htmx(
|
||||||
|
|
||||||
if results.is_empty() {
|
if results.is_empty() {
|
||||||
return axum::response::Html(format!(
|
return axum::response::Html(format!(
|
||||||
r##"
|
r#"
|
||||||
<div class="empty-state">
|
<div class="empty-state">
|
||||||
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||||
<circle cx="11" cy="11" r="8"></circle>
|
<circle cx="11" cy="11" r="8"></circle>
|
||||||
|
|
@ -2347,12 +2347,12 @@ pub async fn search_emails_htmx(
|
||||||
<h3>No results for "{}"</h3>
|
<h3>No results for "{}"</h3>
|
||||||
<p>Try different keywords or check your spelling.</p>
|
<p>Try different keywords or check your spelling.</p>
|
||||||
</div>
|
</div>
|
||||||
"##,
|
"#,
|
||||||
query
|
query
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut html = String::from(r##"<div class="search-results">"##);
|
let mut html = String::from(r#"<div class="search-results">"#);
|
||||||
html.push_str(&format!(
|
html.push_str(&format!(
|
||||||
r#"<div class="result-stats">Found {} results for "{}"</div>"#,
|
r#"<div class="result-stats">Found {} results for "{}"</div>"#,
|
||||||
results.len(),
|
results.len(),
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{Path, Query, State},
|
extract::{Path, Query, State},
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
|
|
@ -15,8 +10,6 @@ use uuid::Uuid;
|
||||||
|
|
||||||
use crate::shared::state::AppState;
|
use crate::shared::state::AppState;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct CreateConversationRequest {
|
pub struct CreateConversationRequest {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
@ -188,11 +181,8 @@ pub struct SuccessResponse {
|
||||||
pub message: Option<String>,
|
pub message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub async fn create_conversation(
|
pub async fn create_conversation(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Json(req): Json<CreateConversationRequest>,
|
Json(req): Json<CreateConversationRequest>,
|
||||||
) -> Result<Json<ConversationResponse>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<ConversationResponse>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
let conversation_id = Uuid::new_v4();
|
let conversation_id = Uuid::new_v4();
|
||||||
|
|
@ -216,34 +206,37 @@ pub async fn create_conversation(
|
||||||
Ok(Json(conversation))
|
Ok(Json(conversation))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn join_conversation(
|
pub async fn join_conversation(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path(conversation_id): Path<Uuid>,
|
Path(conversation_id): Path<Uuid>,
|
||||||
Json(req): Json<JoinConversationRequest>,
|
Json(req): Json<JoinConversationRequest>,
|
||||||
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
Ok(Json(SuccessResponse {
|
Ok(Json(SuccessResponse {
|
||||||
success: true,
|
success: true,
|
||||||
message: Some(format!("User {} joined conversation {}", req.user_id, conversation_id)),
|
message: Some(format!(
|
||||||
|
"User {} joined conversation {}",
|
||||||
|
req.user_id, conversation_id
|
||||||
|
)),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn leave_conversation(
|
pub async fn leave_conversation(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path(conversation_id): Path<Uuid>,
|
Path(conversation_id): Path<Uuid>,
|
||||||
Json(req): Json<LeaveConversationRequest>,
|
Json(req): Json<LeaveConversationRequest>,
|
||||||
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
Ok(Json(SuccessResponse {
|
Ok(Json(SuccessResponse {
|
||||||
success: true,
|
success: true,
|
||||||
message: Some(format!("User {} left conversation {}", req.user_id, conversation_id)),
|
message: Some(format!(
|
||||||
|
"User {} left conversation {}",
|
||||||
|
req.user_id, conversation_id
|
||||||
|
)),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn get_conversation_members(
|
pub async fn get_conversation_members(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path(conversation_id): Path<Uuid>,
|
Path(_conversation_id): Path<Uuid>,
|
||||||
) -> Result<Json<Vec<ParticipantResponse>>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<Vec<ParticipantResponse>>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
let members = vec![ParticipantResponse {
|
let members = vec![ParticipantResponse {
|
||||||
user_id: Uuid::new_v4(),
|
user_id: Uuid::new_v4(),
|
||||||
|
|
@ -258,19 +251,17 @@ pub async fn get_conversation_members(
|
||||||
Ok(Json(members))
|
Ok(Json(members))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn get_conversation_messages(
|
pub async fn get_conversation_messages(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path(conversation_id): Path<Uuid>,
|
Path(_conversation_id): Path<Uuid>,
|
||||||
) -> Result<Json<Vec<MessageResponse>>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<Vec<MessageResponse>>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
let messages = vec![];
|
let messages = vec![];
|
||||||
|
|
||||||
Ok(Json(messages))
|
Ok(Json(messages))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn send_message(
|
pub async fn send_message(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path(conversation_id): Path<Uuid>,
|
Path(conversation_id): Path<Uuid>,
|
||||||
Json(req): Json<SendMessageRequest>,
|
Json(req): Json<SendMessageRequest>,
|
||||||
) -> Result<Json<MessageResponse>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<MessageResponse>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
|
|
@ -297,9 +288,8 @@ pub async fn send_message(
|
||||||
Ok(Json(message))
|
Ok(Json(message))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn edit_message(
|
pub async fn edit_message(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path((conversation_id, message_id)): Path<(Uuid, Uuid)>,
|
Path((conversation_id, message_id)): Path<(Uuid, Uuid)>,
|
||||||
Json(req): Json<EditMessageRequest>,
|
Json(req): Json<EditMessageRequest>,
|
||||||
) -> Result<Json<MessageResponse>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<MessageResponse>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
|
|
@ -324,10 +314,9 @@ pub async fn edit_message(
|
||||||
Ok(Json(message))
|
Ok(Json(message))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn delete_message(
|
pub async fn delete_message(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path((conversation_id, message_id)): Path<(Uuid, Uuid)>,
|
Path((_conversation_id, message_id)): Path<(Uuid, Uuid)>,
|
||||||
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
Ok(Json(SuccessResponse {
|
Ok(Json(SuccessResponse {
|
||||||
success: true,
|
success: true,
|
||||||
|
|
@ -335,22 +324,23 @@ pub async fn delete_message(
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn react_to_message(
|
pub async fn react_to_message(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path((conversation_id, message_id)): Path<(Uuid, Uuid)>,
|
Path((_conversation_id, message_id)): Path<(Uuid, Uuid)>,
|
||||||
Json(req): Json<ReactToMessageRequest>,
|
Json(req): Json<ReactToMessageRequest>,
|
||||||
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
Ok(Json(SuccessResponse {
|
Ok(Json(SuccessResponse {
|
||||||
success: true,
|
success: true,
|
||||||
message: Some(format!("Reaction '{}' added to message {}", req.reaction, message_id)),
|
message: Some(format!(
|
||||||
|
"Reaction '{}' added to message {}",
|
||||||
|
req.reaction, message_id
|
||||||
|
)),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn pin_message(
|
pub async fn pin_message(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path((conversation_id, message_id)): Path<(Uuid, Uuid)>,
|
Path((_conversation_id, message_id)): Path<(Uuid, Uuid)>,
|
||||||
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
Ok(Json(SuccessResponse {
|
Ok(Json(SuccessResponse {
|
||||||
success: true,
|
success: true,
|
||||||
|
|
@ -358,20 +348,18 @@ pub async fn pin_message(
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn search_messages(
|
pub async fn search_messages(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path(conversation_id): Path<Uuid>,
|
Path(_conversation_id): Path<Uuid>,
|
||||||
Query(params): Query<SearchMessagesQuery>,
|
Query(_params): Query<SearchMessagesQuery>,
|
||||||
) -> Result<Json<Vec<MessageResponse>>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<Vec<MessageResponse>>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
let messages = vec![];
|
let messages = vec![];
|
||||||
|
|
||||||
Ok(Json(messages))
|
Ok(Json(messages))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn start_call(
|
pub async fn start_call(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path(conversation_id): Path<Uuid>,
|
Path(conversation_id): Path<Uuid>,
|
||||||
Json(req): Json<StartCallRequest>,
|
Json(req): Json<StartCallRequest>,
|
||||||
) -> Result<Json<CallResponse>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<CallResponse>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
|
|
@ -395,10 +383,9 @@ pub async fn start_call(
|
||||||
Ok(Json(call))
|
Ok(Json(call))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn join_call(
|
pub async fn join_call(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path(conversation_id): Path<Uuid>,
|
Path(_conversation_id): Path<Uuid>,
|
||||||
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
Ok(Json(SuccessResponse {
|
Ok(Json(SuccessResponse {
|
||||||
success: true,
|
success: true,
|
||||||
|
|
@ -406,10 +393,9 @@ pub async fn join_call(
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn leave_call(
|
pub async fn leave_call(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path(conversation_id): Path<Uuid>,
|
Path(_conversation_id): Path<Uuid>,
|
||||||
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
Ok(Json(SuccessResponse {
|
Ok(Json(SuccessResponse {
|
||||||
success: true,
|
success: true,
|
||||||
|
|
@ -417,10 +403,9 @@ pub async fn leave_call(
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn mute_call(
|
pub async fn mute_call(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path(conversation_id): Path<Uuid>,
|
Path(_conversation_id): Path<Uuid>,
|
||||||
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
Ok(Json(SuccessResponse {
|
Ok(Json(SuccessResponse {
|
||||||
success: true,
|
success: true,
|
||||||
|
|
@ -428,10 +413,9 @@ pub async fn mute_call(
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn unmute_call(
|
pub async fn unmute_call(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path(conversation_id): Path<Uuid>,
|
Path(_conversation_id): Path<Uuid>,
|
||||||
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
Ok(Json(SuccessResponse {
|
Ok(Json(SuccessResponse {
|
||||||
success: true,
|
success: true,
|
||||||
|
|
@ -439,9 +423,8 @@ pub async fn unmute_call(
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn start_screen_share(
|
pub async fn start_screen_share(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path(conversation_id): Path<Uuid>,
|
Path(conversation_id): Path<Uuid>,
|
||||||
Json(req): Json<ScreenShareRequest>,
|
Json(req): Json<ScreenShareRequest>,
|
||||||
) -> Result<Json<ScreenShareResponse>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<ScreenShareResponse>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
|
|
@ -462,10 +445,9 @@ pub async fn start_screen_share(
|
||||||
Ok(Json(screen_share))
|
Ok(Json(screen_share))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn stop_screen_share(
|
pub async fn stop_screen_share(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path(conversation_id): Path<Uuid>,
|
Path(_conversation_id): Path<Uuid>,
|
||||||
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
Ok(Json(SuccessResponse {
|
Ok(Json(SuccessResponse {
|
||||||
success: true,
|
success: true,
|
||||||
|
|
@ -473,10 +455,9 @@ pub async fn stop_screen_share(
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn start_recording(
|
pub async fn start_recording(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path(conversation_id): Path<Uuid>,
|
Path(_conversation_id): Path<Uuid>,
|
||||||
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
Ok(Json(SuccessResponse {
|
Ok(Json(SuccessResponse {
|
||||||
success: true,
|
success: true,
|
||||||
|
|
@ -484,10 +465,9 @@ pub async fn start_recording(
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn stop_recording(
|
pub async fn stop_recording(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path(conversation_id): Path<Uuid>,
|
Path(_conversation_id): Path<Uuid>,
|
||||||
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
Ok(Json(SuccessResponse {
|
Ok(Json(SuccessResponse {
|
||||||
success: true,
|
success: true,
|
||||||
|
|
@ -495,9 +475,8 @@ pub async fn stop_recording(
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn create_whiteboard(
|
pub async fn create_whiteboard(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path(conversation_id): Path<Uuid>,
|
Path(conversation_id): Path<Uuid>,
|
||||||
) -> Result<Json<WhiteboardResponse>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<WhiteboardResponse>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
let whiteboard_id = Uuid::new_v4();
|
let whiteboard_id = Uuid::new_v4();
|
||||||
|
|
@ -518,11 +497,10 @@ pub async fn create_whiteboard(
|
||||||
Ok(Json(whiteboard))
|
Ok(Json(whiteboard))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn collaborate_whiteboard(
|
pub async fn collaborate_whiteboard(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
Path(conversation_id): Path<Uuid>,
|
Path(_conversation_id): Path<Uuid>,
|
||||||
Json(data): Json<serde_json::Value>,
|
Json(_data): Json<serde_json::Value>,
|
||||||
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
|
||||||
Ok(Json(SuccessResponse {
|
Ok(Json(SuccessResponse {
|
||||||
success: true,
|
success: true,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue