diff --git a/README.md b/README.md index b0ffb1b..167a6a2 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ - **Multi-User RBAC**: 5 built-in roles (Admin, Manager, Member, AccountManager, AccountViewer) plus custom roles with 22 granular permissions. - **Account-Level Isolation**: Grant users access to specific accounts with scoped roles. Permissions enforced at the API layer. - **CLI Import Tools**: Import from EML directories, MBOX files (including Gmail variants), Thunderbird profiles, and Outlook PST files. -- **CLI Export**: Download account data as MBOX via `bichonctl`. +- **CLI Export**: Download account data as MBOX via `bichon-cli`. - **Bulk Restore**: Restore emails in bulk back to their original IMAP accounts. - **Embedded SMTP Server**: Receive emails directly at the gateway level. STARTTLS or TLS encryption. AUTH PLAIN/LOGIN with API token authentication. - **Admin Tooling**: Password reset for locked-out admins. Non-destructive v0.3.7 to v1.0 data migration. @@ -340,10 +340,10 @@ On first start, Bichon creates a built-in admin user: ## CLI Tools -### bichonctl — Import & Export +### bichon-cli — Import & Export ```bash -./bichonctl --config config.toml +./bichon-cli --config config.toml ``` Creates a `config.toml` on first run with your server URL and API token. @@ -391,12 +391,12 @@ All `/api/v1/*` endpoints require `Authorization: Bearer `. | Format | Tool | Notes | |--------|------|-------| -| **EML Directory** | `bichonctl` | Recursive `.eml` scan; preserves folder hierarchy | -| **MBOX** | `bichonctl` | Single-file streaming import; supports Gmail's MBOX variant | -| **Thunderbird** | `bichonctl` | Reads directly from local Thunderbird profile directory | -| **PST** | `bichonctl` | Outlook Personal Storage (`.pst`) file parsing | +| **EML Directory** | `bichon-cli` | Recursive `.eml` scan; preserves folder hierarchy | +| **MBOX** | `bichon-cli` | Single-file streaming import; supports Gmail's MBOX variant | +| **Thunderbird** | `bichon-cli` | Reads directly from local Thunderbird profile directory | +| **PST** | `bichon-cli` | Outlook Personal Storage (`.pst`) file parsing | | **API Import** | `POST /api/v1/import` | Base64-encoded EML payloads for programmatic use | -| **MBOX Export** | `bichonctl` | Download account data as `.mbox` file | +| **MBOX Export** | `bichon-cli` | Download account data as `.mbox` file | All imports flow through the Bichon REST API. The server parses MIME, extracts metadata, indexes content into Tantivy, deduplicates by BLAKE3 content hash, and stores raw blobs in Fjall. @@ -410,7 +410,7 @@ bichon/ │ ├── memdb/ Embedded key-value database layer (WAL, transactions) │ ├── core/ Library — IMAP sync, search, storage, auth, models │ ├── server/ Binary — Poem web server + embedded WebUI (rust-embed) -│ ├── cli/ Binary — bichonctl import/export CLI +│ ├── cli/ Binary — bichon-cli import/export CLI │ └── admin/ Binary — bichon-admin password reset & migration └── web/ React + TypeScript + Vite + ShadCN UI frontend ``` diff --git a/crates/cli/src/api/download.rs b/crates/cli/src/api/download.rs index 4619c5e..c6e746d 100644 --- a/crates/cli/src/api/download.rs +++ b/crates/cli/src/api/download.rs @@ -1,4 +1,4 @@ -use crate::BichonCtlConfig; +use crate::BichonCliConfig; use bichon_core::{base64_encode, envelope::meta::BichonMetadata, store::envelope::Envelope}; use chrono::{TimeZone, Utc}; use reqwest::Client; @@ -6,7 +6,7 @@ use tokio::io::AsyncWriteExt; pub async fn download_and_export_with_json_header( client: &Client, - config: &BichonCtlConfig, + config: &BichonCliConfig, envelope: Envelope, file: &mut tokio::fs::File, ) -> bool { diff --git a/crates/cli/src/api/search.rs b/crates/cli/src/api/search.rs index b8e74ad..0729d59 100644 --- a/crates/cli/src/api/search.rs +++ b/crates/cli/src/api/search.rs @@ -5,11 +5,11 @@ use bichon_core::{ }; use reqwest::Client; -use crate::BichonCtlConfig; +use crate::BichonCliConfig; pub async fn search_messages( client: &Client, - config: &BichonCtlConfig, + config: &BichonCliConfig, page: u64, page_size: u64, ) -> Option> { diff --git a/crates/cli/src/api/sender.rs b/crates/cli/src/api/sender.rs index 761631f..1a2be79 100644 --- a/crates/cli/src/api/sender.rs +++ b/crates/cli/src/api/sender.rs @@ -21,11 +21,11 @@ use reqwest::Client; use bichon_core::import::BatchEmlRequest; -use crate::BichonCtlConfig; +use crate::BichonCliConfig; pub async fn send_batch_request( client: &Client, - config: &BichonCtlConfig, + config: &BichonCliConfig, account_id: u64, folder: &str, emls: Vec, diff --git a/crates/cli/src/api/stats.rs b/crates/cli/src/api/stats.rs index 65f4647..cd9e60d 100644 --- a/crates/cli/src/api/stats.rs +++ b/crates/cli/src/api/stats.rs @@ -1,11 +1,11 @@ use bichon_core::account::stats::AccountStats; use reqwest::Client; -use crate::BichonCtlConfig; +use crate::BichonCliConfig; pub async fn fetch_account_stats( client: &Client, - config: &BichonCtlConfig, + config: &BichonCliConfig, account_id: u64, ) -> Option { let url = format!("{}/api/v1/accounts/{}/stats", config.base_url, account_id); diff --git a/crates/cli/src/auth.rs b/crates/cli/src/auth.rs index 684c589..8d8837b 100644 --- a/crates/cli/src/auth.rs +++ b/crates/cli/src/auth.rs @@ -27,10 +27,10 @@ use bichon_core::{ users::{permissions::Permission, view::UserView}, }; -use crate::BichonCtlConfig; +use crate::BichonCliConfig as BichonCliConfig; pub async fn verify_user_and_get_account( - config: &BichonCtlConfig, + config: &BichonCliConfig, theme: &ColorfulTheme, only_nosync: bool, ) -> MinimalAccount { diff --git a/crates/cli/src/eml/mod.rs b/crates/cli/src/eml/mod.rs index b0f2e93..12fec82 100644 --- a/crates/cli/src/eml/mod.rs +++ b/crates/cli/src/eml/mod.rs @@ -29,10 +29,10 @@ use reqwest::Client; use bichon_core::base64_encode_url_safe; -use crate::{BichonCtlConfig, api::sender::send_batch_request}; +use crate::{BichonCliConfig, api::sender::send_batch_request}; pub async fn handle_eml_directory_import( - config: &BichonCtlConfig, + config: &BichonCliConfig, account_id: u64, theme: &ColorfulTheme, ) { @@ -100,7 +100,7 @@ fn scan_dir( } async fn process_and_upload( - config: &BichonCtlConfig, + config: &BichonCliConfig, account_id: u64, tasks: HashMap>, ) { diff --git a/crates/cli/src/export/mod.rs b/crates/cli/src/export/mod.rs index f1d9898..ef5fd7d 100644 --- a/crates/cli/src/export/mod.rs +++ b/crates/cli/src/export/mod.rs @@ -1,7 +1,7 @@ use crate::api::download::download_and_export_with_json_header; use crate::api::search::search_messages; use crate::api::stats::fetch_account_stats; -use crate::BichonCtlConfig; +use crate::BichonCliConfig; use bichon_core::account::payload::MinimalAccount; use console::style; use dialoguer::Confirm; @@ -12,7 +12,7 @@ use std::path::{Path, PathBuf}; use sysinfo::Disks; pub async fn handle_account_export( - config: &BichonCtlConfig, + config: &BichonCliConfig, account: MinimalAccount, theme: &ColorfulTheme, ) { diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index a4ffb34..e2de9d4 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -39,7 +39,7 @@ pub mod thunderbird; #[derive(Parser, Debug)] #[command( - name = "bichonctl", + name = "bichon-cli", author = "rustmailer", version = bichon_version!(), about = "A CLI tool to import email data into Bichon service" @@ -57,7 +57,7 @@ pub struct BichonCli { } #[derive(Serialize, Deserialize, Debug, Clone)] -pub struct BichonCtlConfig { +pub struct BichonCliConfig { pub base_url: String, pub api_token: String, } @@ -67,11 +67,11 @@ async fn main() { let cli = BichonCli::parse(); let theme = ColorfulTheme::default(); let config_path = &cli.config; - let mut current_config: Option = None; + let mut current_config: Option = None; if config_path.exists() { if let Ok(content) = fs::read_to_string(config_path) { - if let Ok(config) = toml::from_str::(&content) { + if let Ok(config) = toml::from_str::(&content) { println!("{}", style("✔ Existing configuration found:").green()); println!(" Base URL: {}", style(&config.base_url).yellow()); println!(" API Token: {}", style(&config.api_token).yellow()); @@ -105,7 +105,7 @@ async fn main() { .interact_text() .unwrap(); - let conf = BichonCtlConfig { + let conf = BichonCliConfig { base_url: url, api_token: token, }; diff --git a/crates/cli/src/mbox/mod.rs b/crates/cli/src/mbox/mod.rs index e57222a..2a4fb59 100644 --- a/crates/cli/src/mbox/mod.rs +++ b/crates/cli/src/mbox/mod.rs @@ -22,7 +22,7 @@ use std::path::PathBuf; use crate::api::sender::send_batch_request; use crate::mbox::gmail::determine_folder; use crate::mbox::reader::MboxFile; -use crate::BichonCtlConfig; +use crate::BichonCliConfig; use bichon_core::base64_encode_url_safe; use bichon_core::envelope::meta::{parse_bichon_metadata, BichonMetadata}; use console::style; @@ -36,7 +36,7 @@ pub mod gmail; pub mod reader; pub async fn handle_mbox_single_file_import( - config: &BichonCtlConfig, + config: &BichonCliConfig, account_id: u64, theme: &ColorfulTheme, ) { @@ -120,7 +120,7 @@ pub async fn handle_mbox_single_file_import( pub async fn run_import( account_id: u64, mbox_path: &PathBuf, - config: &BichonCtlConfig, + config: &BichonCliConfig, target_folder: Option, ) { let client = Client::new(); diff --git a/crates/cli/src/pst/mod.rs b/crates/cli/src/pst/mod.rs index c11ee4a..45e31ae 100644 --- a/crates/cli/src/pst/mod.rs +++ b/crates/cli/src/pst/mod.rs @@ -25,7 +25,7 @@ use outlook_pst::ltp::prop_context::PropertyValue; use crate::api::sender::send_batch_request; use crate::pst::encoding::decode_subject; -use crate::BichonCtlConfig; +use crate::BichonCliConfig; use bichon_core::base64_encode_url_safe; use dialoguer::Confirm; use outlook_pst::messaging::attachment::AttachmentProperties; @@ -60,7 +60,7 @@ pub struct EmailAttachment { pub data: Option>, } -pub async fn handle_pst_import(config: &BichonCtlConfig, account_id: u64, theme: &ColorfulTheme) { +pub async fn handle_pst_import(config: &BichonCliConfig, account_id: u64, theme: &ColorfulTheme) { let path_str: String = Input::with_theme(theme) .with_prompt("Enter the path to your SINGLE .pst file") .validate_with(|input: &String| { @@ -115,7 +115,7 @@ pub async fn handle_pst_import(config: &BichonCtlConfig, account_id: u64, theme: } } -async fn parse_pst(pst_path: PathBuf, config: &BichonCtlConfig, account_id: u64) { +async fn parse_pst(pst_path: PathBuf, config: &BichonCliConfig, account_id: u64) { let client = Client::new(); let pst_store = match outlook_pst::open_store(&pst_path) { @@ -161,7 +161,7 @@ fn process_folder_recursively<'a>( client: &'a Client, folder: &'a Rc, parent_path: &'a str, - config: &'a BichonCtlConfig, + config: &'a BichonCliConfig, account_id: u64, ) -> Pin + 'a>> { Box::pin(async move { @@ -407,7 +407,7 @@ fn extract_recipients_list(message: &Rc) -> (Vec, Vec, diff --git a/crates/cli/src/thunderbird/mod.rs b/crates/cli/src/thunderbird/mod.rs index c82d725..f194d82 100644 --- a/crates/cli/src/thunderbird/mod.rs +++ b/crates/cli/src/thunderbird/mod.rs @@ -18,12 +18,12 @@ use std::{collections::HashMap, path::PathBuf}; -use crate::{mbox::run_import, BichonCtlConfig}; +use crate::{mbox::run_import, BichonCliConfig}; use console::style; use dialoguer::{theme::ColorfulTheme, Confirm, Input}; pub async fn handle_thunderbird_import( - config: &BichonCtlConfig, + config: &BichonCliConfig, account_id: u64, theme: &ColorfulTheme, ) {