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.
|
- **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
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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>> {
|
||||||
|
|||||||
@@ -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>,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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>>,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -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,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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>,
|
||||||
|
|||||||
@@ -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,
|
||||||
) {
|
) {
|
||||||
|
|||||||
Reference in New Issue
Block a user