mirror of
https://github.com/rustmailer/bichon.git
synced 2026-08-03 07:48:34 +02:00
- Implement `bichonctl` interactive CLI using `dialoguer`. - Support recursive EML directory scanning with folder structure preservation. - Support single MBOX file streaming import. - Support Thunderbird profile import with automatic `.sbd` hierarchy detection. - Add batch processing (Base64 encoding & batch API requests) for improved performance.
170 lines
4.9 KiB
Rust
170 lines
4.9 KiB
Rust
use std::process;
|
|
|
|
use console::style;
|
|
use dialoguer::{theme::ColorfulTheme, Select};
|
|
use reqwest::Client;
|
|
|
|
use crate::modules::{
|
|
account::payload::MinimalAccount,
|
|
cli::BichonCtlConfig,
|
|
users::{permissions::Permission, view::UserView},
|
|
};
|
|
|
|
pub async fn verify_user_and_get_account(config: &BichonCtlConfig, theme: &ColorfulTheme) -> u64 {
|
|
let client = Client::new();
|
|
let url = format!("{}/api/v1/current-user", config.base_url);
|
|
|
|
let response = match client
|
|
.get(&url)
|
|
.header("Authorization", format!("Bearer {}", config.api_token))
|
|
.send()
|
|
.await
|
|
{
|
|
Ok(res) => res,
|
|
Err(e) => {
|
|
eprintln!(
|
|
"\n{} {}",
|
|
style("✘ Network Error:").red().bold(),
|
|
"Could not connect to Bichon service."
|
|
);
|
|
eprintln!("{} {}", style("Details:").dim(), e);
|
|
eprintln!(
|
|
"\n{} Please check if the Base URL is correct and the server is running.",
|
|
style("Tip:").cyan()
|
|
);
|
|
process::exit(1);
|
|
}
|
|
};
|
|
|
|
if !response.status().is_success() {
|
|
let status = response.status();
|
|
let error_body = response
|
|
.text()
|
|
.await
|
|
.unwrap_or_else(|_| "No error detail provided".to_string());
|
|
|
|
eprintln!(
|
|
"\n{} Server returned an error (Status: {})",
|
|
style("✘ API Error:").red().bold(),
|
|
style(status).yellow()
|
|
);
|
|
|
|
if status == 401 {
|
|
eprintln!(
|
|
"{} Your API Token seems to be invalid or expired.",
|
|
style("Context:").dim()
|
|
);
|
|
} else if status == 404 {
|
|
eprintln!(
|
|
"{} The endpoint was not found. Please check your Base URL.",
|
|
style("Context:").dim()
|
|
);
|
|
}
|
|
|
|
eprintln!("{} {}", style("Response:").dim(), error_body);
|
|
process::exit(1);
|
|
}
|
|
|
|
let user: UserView = response.json().await.expect("Failed to parse user data");
|
|
println!("Welcome, {}!", style(&user.username).cyan());
|
|
|
|
let account_list_url = format!(
|
|
"{}/api/v1/minimal-account-list?only_nosync=true",
|
|
config.base_url
|
|
);
|
|
let acc_response = client
|
|
.get(&account_list_url)
|
|
.header("Authorization", format!("Bearer {}", config.api_token))
|
|
.send()
|
|
.await
|
|
.expect("Failed to fetch account list");
|
|
|
|
if !acc_response.status().is_success() {
|
|
panic!(
|
|
"Failed to retrieve accounts. Status: {}",
|
|
acc_response.status()
|
|
);
|
|
}
|
|
|
|
let accounts: Vec<MinimalAccount> = acc_response
|
|
.json()
|
|
.await
|
|
.expect("Failed to parse minimal account list");
|
|
|
|
if accounts.is_empty() {
|
|
println!(
|
|
"\n{}",
|
|
style("Error: No 'nosync' accounts found.").red().bold()
|
|
);
|
|
println!(
|
|
"{}",
|
|
style("Mail import is only supported for 'nosync' type accounts.").dim()
|
|
);
|
|
println!(
|
|
"Please create a new {} account in the Bichon web interface first.",
|
|
style("Nosync").bold().yellow()
|
|
);
|
|
process::exit(1);
|
|
}
|
|
let required_permission = Permission::DATA_IMPORT_BATCH;
|
|
let mut selectable_accounts = Vec::new();
|
|
let mut options = Vec::new();
|
|
|
|
for acc in accounts {
|
|
let has_permission = if let Some(perms) = user.account_permissions.get(&acc.id) {
|
|
perms.iter().any(|p| p == required_permission)
|
|
} else {
|
|
user.global_permissions
|
|
.iter()
|
|
.any(|p| p == Permission::DATA_MANAGE_ALL || p == Permission::ROOT)
|
|
};
|
|
|
|
let status_prefix = if has_permission {
|
|
style(" [READY] ").green()
|
|
} else {
|
|
style(" [NO PERMISSION] ").red()
|
|
};
|
|
|
|
options.push(format!(
|
|
"{}{} - {}",
|
|
status_prefix,
|
|
style(&acc.email).bold(),
|
|
style(format!("ID: {}", acc.id)).dim()
|
|
));
|
|
|
|
selectable_accounts.push((acc, has_permission));
|
|
}
|
|
|
|
let selection = Select::with_theme(theme)
|
|
.with_prompt("Select the target account for import")
|
|
.items(&options)
|
|
.default(0)
|
|
.max_length(10)
|
|
.interact()
|
|
.unwrap();
|
|
|
|
let (selected_acc, can_import) = &selectable_accounts[selection];
|
|
|
|
if !*can_import {
|
|
eprintln!(
|
|
"\n{} You do not have '{}' permission for account {}.",
|
|
style("✘ Permission Denied:").red().bold(),
|
|
style(required_permission).yellow(),
|
|
style(&selected_acc.email).cyan()
|
|
);
|
|
eprintln!(
|
|
"{} Please contact your administrator to upgrade your role for this account.",
|
|
style("Tip:").dim()
|
|
);
|
|
process::exit(1);
|
|
}
|
|
|
|
println!(
|
|
"{} Targeting account: {}",
|
|
style("✔").green(),
|
|
style(&selected_acc.email).cyan().bold()
|
|
);
|
|
|
|
selected_acc.id
|
|
}
|