Logging and Notifications

This commit is contained in:
2024-10-17 12:49:56 +02:00
parent 4e8ef3c0b4
commit 4c88ca0685
10 changed files with 247 additions and 35 deletions

89
Cargo.lock generated
View File

@@ -417,6 +417,7 @@ dependencies = [
"ctrlc",
"derive_more",
"directories",
"dotenvy",
"image",
"interprocess",
"open",
@@ -425,9 +426,13 @@ dependencies = [
"serde",
"serde_qs 0.13.0",
"sha256",
"tauri-winrt-notification",
"thiserror",
"time",
"tokio",
"toml",
"tracing",
"tracing-subscriber",
"tray-icon",
"uuid",
"windres",
@@ -2379,7 +2384,7 @@ checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba"
dependencies = [
"cfg-if",
"libc",
"windows",
"windows 0.52.0",
]
[[package]]
@@ -2522,7 +2527,7 @@ dependencies = [
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
"windows-core 0.52.0",
]
[[package]]
@@ -4467,6 +4472,15 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quick-xml"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33"
dependencies = [
"memchr",
]
[[package]]
name = "quick-xml"
version = "0.36.2"
@@ -5725,6 +5739,18 @@ version = "0.12.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]]
name = "tauri-winrt-notification"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3ecbfeedbd4e41fd66dc3c01951f49af81a824c4f493c02d91b837cf01caf69"
dependencies = [
"quick-xml 0.31.0",
"thiserror",
"windows 0.58.0",
"windows-version",
]
[[package]]
name = "tempfile"
version = "3.13.0"
@@ -6132,6 +6158,7 @@ dependencies = [
"sharded-slab",
"smallvec",
"thread_local",
"time",
"tracing",
"tracing-core",
"tracing-log",
@@ -6632,7 +6659,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3"
dependencies = [
"proc-macro2",
"quick-xml",
"quick-xml 0.36.2",
"quote",
]
@@ -6736,7 +6763,17 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
dependencies = [
"windows-core",
"windows-core 0.52.0",
"windows-targets 0.52.6",
]
[[package]]
name = "windows"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
dependencies = [
"windows-core 0.58.0",
"windows-targets 0.52.6",
]
@@ -6749,6 +6786,41 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-core"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
dependencies = [
"windows-implement",
"windows-interface",
"windows-result",
"windows-strings",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-implement"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.79",
]
[[package]]
name = "windows-interface"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.79",
]
[[package]]
name = "windows-registry"
version = "0.2.0"
@@ -6861,6 +6933,15 @@ dependencies = [
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows-version"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6998aa457c9ba8ff2fb9f13e9d2a930dabcea28f1d0ab94d687d8b3654844515"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"

View File

@@ -11,8 +11,10 @@ config = "0.14.0"
ctrlc = "3.4.5"
derive_more = { version = "1.0", features = ["full"] }
directories = "5.0"
dotenvy = "0.15.7"
image = "0.25"
interprocess = { version = "2.2.1", features = ["tokio"] }
tauri-winrt-notification = "0.6.0"
open = "5.3.0"
rand = "0.8.5"
reqwest = { version = "0.12.8", default-features = false, features = [
@@ -23,8 +25,11 @@ serde = { version = "1", features = ["derive"] }
serde_qs = "0.13.0"
sha256 = "1.5.0"
thiserror = { version = "1.0" }
time = "0.3.36"
tokio = { version = "1.40.0", features = ["full"] }
toml = "0.8"
tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["time"] }
tray-icon = "0.19"
uuid = { version = "1.10.0", features = ["fast-rng", "serde", "v4"] }
winit = "0.30"

View File

@@ -5,11 +5,25 @@ use tray_icon::menu::{MenuId, MenuItem};
use winit::{
application::ApplicationHandler,
event::StartCause,
event_loop::{ActiveEventLoop, ControlFlow},
event_loop::{ActiveEventLoop, ControlFlow, EventLoop},
};
use crate::{config::Config, icon::TrayIcon, oauth, state_machine::Event, BASE_URL};
pub async fn start(
config: Config,
sender: Sender<Event>,
receiver: Receiver<Event>,
) -> Result<(), anyhow::Error> {
let mut app = App::new(config, sender, receiver)?;
let event_loop = EventLoop::new()?;
event_loop.run_app(&mut app)?;
tracing::info!("EventLoop Shutdown");
Ok(())
}
pub struct App {
config: Config,
tray_icon: TrayIcon,
@@ -112,7 +126,7 @@ impl ApplicationHandler for App {
.unwrap();
}
Event::Quit => {
println!("Shutting down EventLoop");
tracing::info!("Shutting down EventLoop");
event_loop.exit()
}
_ => {}
@@ -126,6 +140,16 @@ impl ApplicationHandler for App {
let _ = self.sender.send(Event::Ready {
config: self.config.clone(),
});
if !self.config.toast_shown() {
tauri_winrt_notification::Toast::new(crate::AVAM_APP_ID)
.title("Avam lives in the system tray!")
.text1("(╯°□°)╯︵ ┻━┻")
.show()
.expect("unable to toast");
}
let _ = self.config.set_toast_shown(true);
}
fn window_event(

View File

@@ -15,6 +15,8 @@ use crate::{
pub struct Config {
#[serde(with = "arc_rwlock_serde")]
token: Arc<RwLock<Option<String>>>,
#[serde(with = "arc_rwlock_serde")]
toast_shown: Arc<RwLock<bool>>,
#[serde(skip)]
code_verifier: Arc<RwLock<Option<CodeVerifier>>>,
#[serde(skip)]
@@ -27,6 +29,7 @@ impl Default for Config {
fn default() -> Self {
Self {
token: Default::default(),
toast_shown: Default::default(),
code_verifier: Default::default(),
code_challenge_method: Default::default(),
open_browser: Arc::new(RwLock::new(true)),
@@ -49,6 +52,8 @@ pub enum ConfigError {
#[error(transparent)]
Toml(#[from] toml::ser::Error),
#[error(transparent)]
Config(#[from] config::ConfigError),
#[error(transparent)]
Unknown(#[from] anyhow::Error),
}
@@ -64,6 +69,18 @@ impl Config {
}
}
impl Config {
pub(crate) fn toast_shown(&self) -> bool {
*self.toast_shown.read().unwrap()
}
pub(crate) fn set_toast_shown(&self, value: bool) -> Result<(), ConfigError> {
*self.toast_shown.write().unwrap() = value;
self.write()?;
Ok(())
}
}
impl Config {
pub fn set_code_verifier(
&self,
@@ -110,7 +127,12 @@ impl Config {
.build()
.unwrap();
let config: Self = config.try_deserialize().unwrap_or_default();
let token = config.get_string("token").ok();
let toast_shown = config.get_bool("toast_shown").unwrap_or_default();
let config = Self::default();
config.set_token(token)?;
config.set_toast_shown(toast_shown)?;
config.write()?;

View File

@@ -40,4 +40,23 @@ impl Dirs {
c.push(".lock");
Ok(c)
}
#[cfg(not(debug_assertions))]
pub fn get_log_dir() -> Result<PathBuf, DirsError> {
let c = Self::get_config_dir()?;
let mut log_dir = c.parent().unwrap().to_path_buf();
log_dir.push("logs");
if !log_dir.exists() {
std::fs::create_dir_all(&log_dir)?;
}
Ok(log_dir.to_path_buf())
}
#[cfg(not(debug_assertions))]
pub fn get_log_file() -> Result<PathBuf, DirsError> {
let mut l = Self::get_log_dir()?;
l.push("avam.log");
Ok(l)
}
}

View File

@@ -97,7 +97,7 @@ impl TrayIcon {
if let Some(item) = self.menu_items.get(id) {
if let Some(i) = self.menu.items().iter().find(|i| i.id() == id) {
if let Err(e) = item(i) {
eprintln!("{:#?}", e);
tracing::error!("{:#?}", e);
}
}
}

View File

@@ -1,3 +1,4 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
#![allow(clippy::needless_return)]
mod app;
@@ -17,7 +18,9 @@ use oauth::{start_code_listener, start_code_to_token};
use pipe::Pipe;
use state_machine::Event;
use tokio::task::JoinSet;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
pub static AVAM_APP_ID: &str = "AvamToast-ECEB71694A5E6105";
pub static BASE_URL: &str = "https://avam.avii.nl";
pub static PROJECT_NAME: &str = "Avii's Virtual Airline Manager";
pub static COPYRIGHT: &str = "Avii's Virtual Airline Manager © 2024";
@@ -35,6 +38,10 @@ pub struct Arguments {
#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
dotenvy::dotenv().ok();
init_logging()?;
let (event_sender, event_receiver) = tokio::sync::broadcast::channel(1);
let args = Arguments::parse();
@@ -43,6 +50,7 @@ async fn main() -> Result<(), anyhow::Error> {
}
register_url_scheme()?;
register_notification_handler()?;
let config = Config::new()?;
@@ -52,7 +60,7 @@ async fn main() -> Result<(), anyhow::Error> {
let sender = event_sender.clone();
let mut ctrl_c_counter = 0;
ctrlc::set_handler(move || {
println!("CTRL_C: Quit singal sent");
tracing::info!("CTRL_C: Quit singal sent");
ctrl_c_counter += 1;
if ctrl_c_counter >= 3 {
let _ = unregister_url_scheme();
@@ -61,7 +69,7 @@ async fn main() -> Result<(), anyhow::Error> {
}
if let Err(e) = sender.send(Event::Quit) {
println!("{:#?}", e)
tracing::error!("{:#?}", e)
};
})?;
@@ -86,7 +94,7 @@ async fn main() -> Result<(), anyhow::Error> {
let c = config.clone();
let sender = event_sender.clone();
let receiver = event_receiver.resubscribe();
start_tray_icon(c, sender, receiver).await?;
app::start(c, sender, receiver).await?;
// Wait for everything to finish
while let Some(result) = futures.join_next().await {
@@ -97,28 +105,12 @@ async fn main() -> Result<(), anyhow::Error> {
// Cleanup
unregister_url_scheme()?;
unregister_notification_handler()?;
Lock::unlock();
Ok(())
}
use app::App;
use tokio::sync::broadcast::{Receiver, Sender};
use winit::event_loop::EventLoop;
async fn start_tray_icon(
config: Config,
sender: Sender<Event>,
receiver: Receiver<Event>,
) -> Result<(), anyhow::Error> {
let mut app = App::new(config, sender, receiver)?;
let event_loop = EventLoop::new()?;
event_loop.run_app(&mut app)?;
println!("EventLoop Shutdonw");
Ok(())
}
//
//
//
@@ -178,3 +170,72 @@ fn unregister_url_scheme() -> Result<(), anyhow::Error> {
Ok(())
}
fn register_notification_handler() -> Result<(), anyhow::Error> {
use winreg::enums::*;
use winreg::RegKey;
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
let avam_schema_root = hkcu.create_subkey(format!(
"Software\\Classes\\AppUserModelId\\{}",
AVAM_APP_ID
))?;
let current_exec = std::env::current_exe()?;
let mut icon = current_exec.parent().unwrap().to_path_buf();
icon.push("icon.png");
avam_schema_root
.0
.set_value("DisplayName", &crate::PROJECT_NAME)?;
avam_schema_root.0.set_value("IconBackgroundColor", &"0")?;
avam_schema_root
.0
.set_value("IconUri", &icon.to_str().unwrap())?;
Ok(())
}
fn unregister_notification_handler() -> Result<(), anyhow::Error> {
use winreg::enums::*;
use winreg::RegKey;
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
hkcu.delete_subkey_all(format!(
"Software\\Classes\\AppUserModelId\\{}",
AVAM_APP_ID
))
.ok();
Ok(())
}
fn init_logging() -> Result<(), anyhow::Error> {
#[cfg(not(debug_assertions))]
use dirs::Dirs;
#[cfg(not(debug_assertions))]
use std::{
fs::{self, File},
sync::Arc,
};
#[cfg(not(debug_assertions))]
let log_file = Dirs::get_log_file()?;
#[cfg(not(debug_assertions))]
if !log_file.exists() {
fs::write(&log_file, "")?;
}
#[cfg(not(debug_assertions))]
let file = File::options().append(true).open(&log_file)?;
let fmt = tracing_subscriber::fmt::layer();
#[cfg(not(debug_assertions))]
let fmt = fmt.with_ansi(false).with_writer(Arc::new(file));
tracing_subscriber::registry().with(fmt).init();
Ok(())
}

View File

@@ -60,7 +60,7 @@ pub async fn start_code_to_token(
tokio::select! {
_ = sleep(Duration::from_millis(100)) => {
if let Ok(Event::Quit) = event_receiver.try_recv() {
println!("Shutting down Code Transformer");
tracing::info!("Shutting down Code Transformer");
break;
}
}
@@ -90,6 +90,6 @@ pub async fn start_code_to_token(
}
}
println!("Code Transformer Shutdown");
tracing::info!("Code Transformer Shutdown");
Ok(())
}

View File

@@ -61,20 +61,20 @@ impl Pipe {
let new_sender = pipe_sender.clone();
tokio::spawn(async move {
if let Err(e) = Self::handle_conn(conn, new_sender).await {
eprintln!("error while handling connection: {e}");
tracing::error!("error while handling connection: {e}");
}
});
},
_ = sleep(Duration::from_millis(100)) => {
if let Ok(Event::Quit) = quit_signal.try_recv() {
println!("Shutting down Code Listener");
tracing::info!("Shutting down Code Listener");
break;
}
}
}
}
println!("Code Listener Shutdown");
tracing::info!("Code Listener Shutdown");
Ok(())
}

View File

@@ -137,7 +137,7 @@ pub async fn start(
loop {
if let Ok(event) = event_receiver.recv().await {
if event == Event::Quit {
println!("Shutting down State Machine");
tracing::info!("Shutting down State Machine");
break;
}
@@ -153,6 +153,6 @@ pub async fn start(
}
}
println!("State Machine Shutdown");
tracing::info!("State Machine Shutdown");
Ok(())
}