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