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.
- **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 <token>`.
| 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
```
+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 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 {
+2 -2
View File
@@ -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<DataPage<Envelope>> {
+2 -2
View File
@@ -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<String>,
+2 -2
View File
@@ -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<AccountStats> {
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},
};
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 {
+3 -3
View File
@@ -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<String, Vec<PathBuf>>,
) {
+2 -2
View File
@@ -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,
) {
+5 -5
View File
@@ -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<BichonCtlConfig> = None;
let mut current_config: Option<BichonCliConfig> = None;
if config_path.exists() {
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!(" 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,
};
+3 -3
View File
@@ -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<String>,
) {
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::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<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)
.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<dyn Folder>,
parent_path: &'a str,
config: &'a BichonCtlConfig,
config: &'a BichonCliConfig,
account_id: u64,
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
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(
client: &Client,
config: &BichonCtlConfig,
config: &BichonCliConfig,
account_id: u64,
folder_path: &str,
emls: Vec<String>,
+2 -2
View File
@@ -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,
) {