add mail dir and db path to the config
This commit is contained in:
parent
0690c46f66
commit
14b07adf66
9 changed files with 102 additions and 23 deletions
43
Cargo.lock
generated
43
Cargo.lock
generated
|
@ -769,7 +769,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"redox_users 0.3.5",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-next"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"dirs-sys-next",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys-next"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users 0.4.0",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
|
@ -1964,6 +1985,7 @@ dependencies = [
|
|||
"quoted_printable",
|
||||
"serde",
|
||||
"sha2",
|
||||
"shellexpand",
|
||||
"sqlx",
|
||||
"structopt",
|
||||
"tokio 1.3.0",
|
||||
|
@ -2376,6 +2398,16 @@ dependencies = [
|
|||
"rust-argon2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
|
||||
dependencies = [
|
||||
"getrandom 0.2.2",
|
||||
"redox_syscall 0.2.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.4.5"
|
||||
|
@ -2621,6 +2653,15 @@ version = "0.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
|
||||
|
||||
[[package]]
|
||||
name = "shellexpand"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83bdb7831b2d85ddf4a7b148aa19d0587eddbe8671a436b7bd1182eaad0f2829"
|
||||
dependencies = [
|
||||
"dirs-next",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.1.17"
|
||||
|
|
|
@ -44,6 +44,7 @@ quoted_printable = "0.4.2"
|
|||
sqlx = { version = "0.5.1", features = ["runtime-tokio-rustls", "sqlite"] }
|
||||
sha2 = "0.9.3"
|
||||
hex = "0.4.3"
|
||||
shellexpand = "2.1.0"
|
||||
|
||||
[dependencies.panorama-imap]
|
||||
path = "imap"
|
||||
|
|
|
@ -10,6 +10,9 @@ Example configuration:
|
|||
```toml
|
||||
version = "0.1"
|
||||
|
||||
mail_dir = "~/.local/share/panorama/mail"
|
||||
db_path = "~/.local/share/panorama/panorama.db"
|
||||
|
||||
[[mail]]
|
||||
imap.server = "mail.example.com"
|
||||
imap.port = 143
|
||||
|
|
|
@ -25,6 +25,12 @@ pub struct Config {
|
|||
/// (potentially for migration later?)
|
||||
pub version: String,
|
||||
|
||||
/// Directory to store mail in
|
||||
pub mail_dir: PathBuf,
|
||||
|
||||
/// SQLite database path
|
||||
pub db_path: PathBuf,
|
||||
|
||||
/// Mail accounts
|
||||
#[serde(rename = "mail")]
|
||||
pub mail_accounts: HashMap<String, MailAccountConfig>,
|
||||
|
|
|
@ -23,6 +23,7 @@ use super::{MailCommand, MailEvent, MailStore};
|
|||
|
||||
/// The main function for the IMAP syncing thread
|
||||
pub async fn sync_main(
|
||||
config: Config,
|
||||
acct_name: impl AsRef<str>,
|
||||
acct: MailAccountConfig,
|
||||
mail2ui_tx: UnboundedSender<MailEvent>,
|
||||
|
|
|
@ -67,16 +67,18 @@ pub async fn run_mail(
|
|||
conn.abort();
|
||||
}
|
||||
|
||||
let mail_store = MailStore::new().await?;
|
||||
for (acct_name, acct) in config.mail_accounts.into_iter() {
|
||||
let mail_store = MailStore::new(config.clone()).await?;
|
||||
for (acct_name, acct) in config.mail_accounts.clone().into_iter() {
|
||||
let mail2ui_tx = mail2ui_tx.clone();
|
||||
let mail_store = mail_store.clone();
|
||||
let config2 = config.clone();
|
||||
let handle = tokio::spawn(async move {
|
||||
// debug!("opening imap connection for {:?}", acct);
|
||||
|
||||
// this loop is to make sure accounts are restarted on error
|
||||
loop {
|
||||
match client::sync_main(
|
||||
config2.clone(),
|
||||
&acct_name,
|
||||
acct.clone(),
|
||||
mail2ui_tx.clone(),
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
//! Package for managing the offline storage of emails
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::path::{PathBuf, Path};
|
||||
|
||||
use anyhow::Result;
|
||||
use panorama_imap::response::AttributeValue;
|
||||
use sha2::{Digest, Sha256, Sha512};
|
||||
use sha2::{Digest, Sha256};
|
||||
use sqlx::{
|
||||
migrate::{MigrateDatabase, Migrator},
|
||||
sqlite::{Sqlite, SqlitePool},
|
||||
|
@ -12,6 +12,8 @@ use sqlx::{
|
|||
};
|
||||
use tokio::fs;
|
||||
|
||||
use crate::config::Config;
|
||||
|
||||
static MIGRATOR: Migrator = sqlx::migrate!();
|
||||
|
||||
/// Manages email storage on disk, for both database and caches
|
||||
|
@ -19,30 +21,45 @@ static MIGRATOR: Migrator = sqlx::migrate!();
|
|||
/// This struct is clone-safe: cloning it will just return a reference to the same data structure
|
||||
#[derive(Clone)]
|
||||
pub struct MailStore {
|
||||
config: Config,
|
||||
mail_dir: PathBuf,
|
||||
pool: SqlitePool,
|
||||
}
|
||||
|
||||
impl MailStore {
|
||||
/// Creates a new MailStore
|
||||
pub async fn new() -> Result<Self> {
|
||||
let db_path = "sqlite:hellosu.db";
|
||||
|
||||
// create the database file if it doesn't already exist -_ -
|
||||
if !Sqlite::database_exists(db_path).await? {
|
||||
Sqlite::create_database(db_path).await?;
|
||||
}
|
||||
|
||||
let pool = SqlitePool::connect(db_path).await?;
|
||||
MIGRATOR.run(&pool).await?;
|
||||
debug!("run migrations : {:?}", MIGRATOR);
|
||||
|
||||
let mail_dir = PathBuf::from("hellosu/");
|
||||
pub async fn new(config: Config) -> Result<Self> {
|
||||
let mail_dir = config.mail_dir.to_string_lossy();
|
||||
let mail_dir_str = shellexpand::tilde(mail_dir.as_ref());
|
||||
let mail_dir = PathBuf::from(mail_dir_str.as_ref());
|
||||
if !mail_dir.exists() {
|
||||
fs::create_dir_all(&mail_dir).await?;
|
||||
}
|
||||
info!("using mail dir: {:?}", mail_dir);
|
||||
|
||||
Ok(MailStore { mail_dir, pool })
|
||||
// create database parent
|
||||
let db_path = config.db_path.to_string_lossy();
|
||||
let db_path_str = shellexpand::tilde(db_path.as_ref());
|
||||
|
||||
let db_path = PathBuf::from(db_path_str.as_ref());
|
||||
let db_parent = db_path.parent();
|
||||
if let Some(path) = db_parent {
|
||||
fs::create_dir_all(path).await?;
|
||||
}
|
||||
|
||||
let db_path = format!("sqlite:{}", db_path_str);
|
||||
info!("using database path: {}", db_path_str);
|
||||
|
||||
// create the database file if it doesn't already exist -_ -
|
||||
if !Sqlite::database_exists(&db_path_str).await? {
|
||||
Sqlite::create_database(&db_path_str).await?;
|
||||
}
|
||||
|
||||
let pool = SqlitePool::connect(&db_path_str).await?;
|
||||
MIGRATOR.run(&pool).await?;
|
||||
debug!("run migrations : {:?}", MIGRATOR);
|
||||
|
||||
Ok(MailStore { config, mail_dir, pool })
|
||||
}
|
||||
|
||||
/// Gets the list of all the UIDs in the given folder that need to be updated
|
||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -5,7 +5,7 @@ use anyhow::Result;
|
|||
use fern::colors::{Color, ColoredLevelConfig};
|
||||
use futures::future::TryFutureExt;
|
||||
use panorama::{
|
||||
config::spawn_config_watcher_system,
|
||||
config::{spawn_config_watcher_system, ConfigWatcher},
|
||||
mail::{self, MailEvent},
|
||||
report_err,
|
||||
ui::{self, UiParams},
|
||||
|
@ -63,15 +63,16 @@ async fn run(opt: Opt) -> Result<()> {
|
|||
// send messages from the UI thread to the vm thread
|
||||
let (ui2vm_tx, _ui2vm_rx) = mpsc::unbounded_channel();
|
||||
|
||||
let config_update2 = config_update.clone();
|
||||
tokio::spawn(async move {
|
||||
let config_update = config_update.clone();
|
||||
mail::run_mail(config_update, ui2mail_rx, mail2ui_tx)
|
||||
mail::run_mail(config_update2, ui2mail_rx, mail2ui_tx)
|
||||
.unwrap_or_else(report_err)
|
||||
.await;
|
||||
});
|
||||
|
||||
if !opt.headless {
|
||||
run_ui(exit_tx, mail2ui_rx, ui2vm_tx);
|
||||
let config_update2 = config_update.clone();
|
||||
run_ui(config_update2, exit_tx, mail2ui_rx, ui2vm_tx);
|
||||
}
|
||||
|
||||
exit_rx.recv().await;
|
||||
|
@ -85,6 +86,7 @@ async fn run(opt: Opt) -> Result<()> {
|
|||
|
||||
// Spawns the entire UI in a different thread, since it must be thread-local
|
||||
fn run_ui(
|
||||
config_update: ConfigWatcher,
|
||||
exit_tx: mpsc::Sender<()>,
|
||||
mail2ui_rx: mpsc::UnboundedReceiver<MailEvent>,
|
||||
_ui2vm_tx: mpsc::UnboundedSender<()>,
|
||||
|
@ -99,6 +101,7 @@ fn run_ui(
|
|||
thread::spawn(move || {
|
||||
let localset = LocalSet::new();
|
||||
let params = UiParams {
|
||||
config_update,
|
||||
stdout,
|
||||
exit_tx,
|
||||
mail2ui_rx,
|
||||
|
@ -123,6 +126,7 @@ fn setup_logger(log_file: Option<impl AsRef<Path>>) -> Result<()> {
|
|||
.filter(|meta| {
|
||||
meta.target() != "tokio_util::codec::framed_impl"
|
||||
&& !meta.target().starts_with("rustls::client")
|
||||
&& !meta.target().starts_with("sqlx::query")
|
||||
})
|
||||
.format(move |out, message, record| {
|
||||
out.finish(format_args!(
|
||||
|
|
|
@ -38,6 +38,7 @@ use tui::{
|
|||
Frame, Terminal,
|
||||
};
|
||||
|
||||
use crate::config::ConfigWatcher;
|
||||
use crate::mail::{EmailMetadata, MailEvent};
|
||||
|
||||
use self::colon_prompt::ColonPrompt;
|
||||
|
@ -52,6 +53,9 @@ pub(crate) type TermType<'a, 'b> = &'b mut Terminal<CrosstermBackend<&'a mut Std
|
|||
|
||||
/// Parameters for passing to the UI thread
|
||||
pub struct UiParams {
|
||||
/// Config updates
|
||||
pub config_update: ConfigWatcher,
|
||||
|
||||
/// Handle to the screen
|
||||
pub stdout: Stdout,
|
||||
|
||||
|
|
Loading…
Reference in a new issue