mirror of
https://github.com/rustmailer/bichon.git
synced 2026-08-03 07:48:34 +02:00
fix: rename bichonctl to bichon-cli
This commit is contained in:
@@ -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
|
||||
```
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>> {
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>>,
|
||||
) {
|
||||
|
||||
@@ -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,
|
||||
) {
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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,
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user