fix: rename bichonctl to bichon-cli

This commit is contained in:
rustmailer
2026-05-15 12:07:26 +08:00
parent 7afb1e29aa
commit 1ee2eade3a
12 changed files with 39 additions and 39 deletions
+9 -9
View File
@@ -94,7 +94,7 @@
- **Multi-User RBAC**: 5 built-in roles (Admin, Manager, Member, AccountManager, AccountViewer) plus custom roles with 22 granular permissions. - **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. - **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 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. - **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. - **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. - **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 ## CLI Tools
### bichonctl — Import & Export ### bichon-cli — Import & Export
```bash ```bash
./bichonctl --config config.toml ./bichon-cli --config config.toml
``` ```
Creates a `config.toml` on first run with your server URL and API token. 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 <token>`.
| Format | Tool | Notes | | Format | Tool | Notes |
|--------|------|-------| |--------|------|-------|
| **EML Directory** | `bichonctl` | Recursive `.eml` scan; preserves folder hierarchy | | **EML Directory** | `bichon-cli` | Recursive `.eml` scan; preserves folder hierarchy |
| **MBOX** | `bichonctl` | Single-file streaming import; supports Gmail's MBOX variant | | **MBOX** | `bichon-cli` | Single-file streaming import; supports Gmail's MBOX variant |
| **Thunderbird** | `bichonctl` | Reads directly from local Thunderbird profile directory | | **Thunderbird** | `bichon-cli` | Reads directly from local Thunderbird profile directory |
| **PST** | `bichonctl` | Outlook Personal Storage (`.pst`) file parsing | | **PST** | `bichon-cli` | Outlook Personal Storage (`.pst`) file parsing |
| **API Import** | `POST /api/v1/import` | Base64-encoded EML payloads for programmatic use | | **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. 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) │ ├── memdb/ Embedded key-value database layer (WAL, transactions)
│ ├── core/ Library — IMAP sync, search, storage, auth, models │ ├── core/ Library — IMAP sync, search, storage, auth, models
│ ├── server/ Binary — Poem web server + embedded WebUI (rust-embed) │ ├── 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 │ └── admin/ Binary — bichon-admin password reset & migration
└── web/ React + TypeScript + Vite + ShadCN UI frontend └── web/ React + TypeScript + Vite + ShadCN UI frontend
``` ```
+2 -2
View File
@@ -1,4 +1,4 @@
use crate::BichonCtlConfig; use crate::BichonCliConfig;
use bichon_core::{base64_encode, envelope::meta::BichonMetadata, store::envelope::Envelope}; use bichon_core::{base64_encode, envelope::meta::BichonMetadata, store::envelope::Envelope};
use chrono::{TimeZone, Utc}; use chrono::{TimeZone, Utc};
use reqwest::Client; use reqwest::Client;
@@ -6,7 +6,7 @@ use tokio::io::AsyncWriteExt;
pub async fn download_and_export_with_json_header( pub async fn download_and_export_with_json_header(
client: &Client, client: &Client,
config: &BichonCtlConfig, config: &BichonCliConfig,
envelope: Envelope, envelope: Envelope,
file: &mut tokio::fs::File, file: &mut tokio::fs::File,
) -> bool { ) -> bool {
+2 -2
View File
@@ -5,11 +5,11 @@ use bichon_core::{
}; };
use reqwest::Client; use reqwest::Client;
use crate::BichonCtlConfig; use crate::BichonCliConfig;
pub async fn search_messages( pub async fn search_messages(
client: &Client, client: &Client,
config: &BichonCtlConfig, config: &BichonCliConfig,
page: u64, page: u64,
page_size: u64, page_size: u64,
) -> Option<DataPage<Envelope>> { ) -> Option<DataPage<Envelope>> {
+2 -2
View File
@@ -21,11 +21,11 @@ use reqwest::Client;
use bichon_core::import::BatchEmlRequest; use bichon_core::import::BatchEmlRequest;
use crate::BichonCtlConfig; use crate::BichonCliConfig;
pub async fn send_batch_request( pub async fn send_batch_request(
client: &Client, client: &Client,
config: &BichonCtlConfig, config: &BichonCliConfig,
account_id: u64, account_id: u64,
folder: &str, folder: &str,
emls: Vec<String>, emls: Vec<String>,
+2 -2
View File
@@ -1,11 +1,11 @@
use bichon_core::account::stats::AccountStats; use bichon_core::account::stats::AccountStats;
use reqwest::Client; use reqwest::Client;
use crate::BichonCtlConfig; use crate::BichonCliConfig;
pub async fn fetch_account_stats( pub async fn fetch_account_stats(
client: &Client, client: &Client,
config: &BichonCtlConfig, config: &BichonCliConfig,
account_id: u64, account_id: u64,
) -> Option<AccountStats> { ) -> Option<AccountStats> {
let url = format!("{}/api/v1/accounts/{}/stats", config.base_url, account_id); let url = format!("{}/api/v1/accounts/{}/stats", config.base_url, account_id);
+2 -2
View File
@@ -27,10 +27,10 @@ use bichon_core::{
users::{permissions::Permission, view::UserView}, users::{permissions::Permission, view::UserView},
}; };
use crate::BichonCtlConfig; use crate::BichonCliConfig as BichonCliConfig;
pub async fn verify_user_and_get_account( pub async fn verify_user_and_get_account(
config: &BichonCtlConfig, config: &BichonCliConfig,
theme: &ColorfulTheme, theme: &ColorfulTheme,
only_nosync: bool, only_nosync: bool,
) -> MinimalAccount { ) -> MinimalAccount {
+3 -3
View File
@@ -29,10 +29,10 @@ use reqwest::Client;
use bichon_core::base64_encode_url_safe; 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( pub async fn handle_eml_directory_import(
config: &BichonCtlConfig, config: &BichonCliConfig,
account_id: u64, account_id: u64,
theme: &ColorfulTheme, theme: &ColorfulTheme,
) { ) {
@@ -100,7 +100,7 @@ fn scan_dir(
} }
async fn process_and_upload( async fn process_and_upload(
config: &BichonCtlConfig, config: &BichonCliConfig,
account_id: u64, account_id: u64,
tasks: HashMap<String, Vec<PathBuf>>, tasks: HashMap<String, Vec<PathBuf>>,
) { ) {
+2 -2
View File
@@ -1,7 +1,7 @@
use crate::api::download::download_and_export_with_json_header; use crate::api::download::download_and_export_with_json_header;
use crate::api::search::search_messages; use crate::api::search::search_messages;
use crate::api::stats::fetch_account_stats; use crate::api::stats::fetch_account_stats;
use crate::BichonCtlConfig; use crate::BichonCliConfig;
use bichon_core::account::payload::MinimalAccount; use bichon_core::account::payload::MinimalAccount;
use console::style; use console::style;
use dialoguer::Confirm; use dialoguer::Confirm;
@@ -12,7 +12,7 @@ use std::path::{Path, PathBuf};
use sysinfo::Disks; use sysinfo::Disks;
pub async fn handle_account_export( pub async fn handle_account_export(
config: &BichonCtlConfig, config: &BichonCliConfig,
account: MinimalAccount, account: MinimalAccount,
theme: &ColorfulTheme, theme: &ColorfulTheme,
) { ) {
+5 -5
View File
@@ -39,7 +39,7 @@ pub mod thunderbird;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command( #[command(
name = "bichonctl", name = "bichon-cli",
author = "rustmailer", author = "rustmailer",
version = bichon_version!(), version = bichon_version!(),
about = "A CLI tool to import email data into Bichon service" about = "A CLI tool to import email data into Bichon service"
@@ -57,7 +57,7 @@ pub struct BichonCli {
} }
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BichonCtlConfig { pub struct BichonCliConfig {
pub base_url: String, pub base_url: String,
pub api_token: String, pub api_token: String,
} }
@@ -67,11 +67,11 @@ async fn main() {
let cli = BichonCli::parse(); let cli = BichonCli::parse();
let theme = ColorfulTheme::default(); let theme = ColorfulTheme::default();
let config_path = &cli.config; let config_path = &cli.config;
let mut current_config: Option<BichonCtlConfig> = None; let mut current_config: Option<BichonCliConfig> = None;
if config_path.exists() { if config_path.exists() {
if let Ok(content) = fs::read_to_string(config_path) { if let Ok(content) = fs::read_to_string(config_path) {
if let Ok(config) = toml::from_str::<BichonCtlConfig>(&content) { if let Ok(config) = toml::from_str::<BichonCliConfig>(&content) {
println!("{}", style("✔ Existing configuration found:").green()); println!("{}", style("✔ Existing configuration found:").green());
println!(" Base URL: {}", style(&config.base_url).yellow()); println!(" Base URL: {}", style(&config.base_url).yellow());
println!(" API Token: {}", style(&config.api_token).yellow()); println!(" API Token: {}", style(&config.api_token).yellow());
@@ -105,7 +105,7 @@ async fn main() {
.interact_text() .interact_text()
.unwrap(); .unwrap();
let conf = BichonCtlConfig { let conf = BichonCliConfig {
base_url: url, base_url: url,
api_token: token, api_token: token,
}; };
+3 -3
View File
@@ -22,7 +22,7 @@ use std::path::PathBuf;
use crate::api::sender::send_batch_request; use crate::api::sender::send_batch_request;
use crate::mbox::gmail::determine_folder; use crate::mbox::gmail::determine_folder;
use crate::mbox::reader::MboxFile; use crate::mbox::reader::MboxFile;
use crate::BichonCtlConfig; use crate::BichonCliConfig;
use bichon_core::base64_encode_url_safe; use bichon_core::base64_encode_url_safe;
use bichon_core::envelope::meta::{parse_bichon_metadata, BichonMetadata}; use bichon_core::envelope::meta::{parse_bichon_metadata, BichonMetadata};
use console::style; use console::style;
@@ -36,7 +36,7 @@ pub mod gmail;
pub mod reader; pub mod reader;
pub async fn handle_mbox_single_file_import( pub async fn handle_mbox_single_file_import(
config: &BichonCtlConfig, config: &BichonCliConfig,
account_id: u64, account_id: u64,
theme: &ColorfulTheme, theme: &ColorfulTheme,
) { ) {
@@ -120,7 +120,7 @@ pub async fn handle_mbox_single_file_import(
pub async fn run_import( pub async fn run_import(
account_id: u64, account_id: u64,
mbox_path: &PathBuf, mbox_path: &PathBuf,
config: &BichonCtlConfig, config: &BichonCliConfig,
target_folder: Option<String>, target_folder: Option<String>,
) { ) {
let client = Client::new(); let client = Client::new();
+5 -5
View File
@@ -25,7 +25,7 @@ use outlook_pst::ltp::prop_context::PropertyValue;
use crate::api::sender::send_batch_request; use crate::api::sender::send_batch_request;
use crate::pst::encoding::decode_subject; use crate::pst::encoding::decode_subject;
use crate::BichonCtlConfig; use crate::BichonCliConfig;
use bichon_core::base64_encode_url_safe; use bichon_core::base64_encode_url_safe;
use dialoguer::Confirm; use dialoguer::Confirm;
use outlook_pst::messaging::attachment::AttachmentProperties; use outlook_pst::messaging::attachment::AttachmentProperties;
@@ -60,7 +60,7 @@ pub struct EmailAttachment {
pub data: Option<Vec<u8>>, pub data: Option<Vec<u8>>,
} }
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) let path_str: String = Input::with_theme(theme)
.with_prompt("Enter the path to your SINGLE .pst file") .with_prompt("Enter the path to your SINGLE .pst file")
.validate_with(|input: &String| { .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 client = Client::new();
let pst_store = match outlook_pst::open_store(&pst_path) { let pst_store = match outlook_pst::open_store(&pst_path) {
@@ -161,7 +161,7 @@ fn process_folder_recursively<'a>(
client: &'a Client, client: &'a Client,
folder: &'a Rc<dyn Folder>, folder: &'a Rc<dyn Folder>,
parent_path: &'a str, parent_path: &'a str,
config: &'a BichonCtlConfig, config: &'a BichonCliConfig,
account_id: u64, account_id: u64,
) -> Pin<Box<dyn Future<Output = ()> + 'a>> { ) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
Box::pin(async move { Box::pin(async move {
@@ -407,7 +407,7 @@ fn extract_recipients_list(message: &Rc<dyn Message>) -> (Vec<String>, Vec<Strin
async fn send_to_bichon( async fn send_to_bichon(
client: &Client, client: &Client,
config: &BichonCtlConfig, config: &BichonCliConfig,
account_id: u64, account_id: u64,
folder_path: &str, folder_path: &str,
emls: Vec<String>, emls: Vec<String>,
+2 -2
View File
@@ -18,12 +18,12 @@
use std::{collections::HashMap, path::PathBuf}; use std::{collections::HashMap, path::PathBuf};
use crate::{mbox::run_import, BichonCtlConfig}; use crate::{mbox::run_import, BichonCliConfig};
use console::style; use console::style;
use dialoguer::{theme::ColorfulTheme, Confirm, Input}; use dialoguer::{theme::ColorfulTheme, Confirm, Input};
pub async fn handle_thunderbird_import( pub async fn handle_thunderbird_import(
config: &BichonCtlConfig, config: &BichonCliConfig,
account_id: u64, account_id: u64,
theme: &ColorfulTheme, theme: &ColorfulTheme,
) { ) {