Logging and Notifications
This commit is contained in:
89
Cargo.lock
generated
89
Cargo.lock
generated
@@ -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"
|
||||
|
@@ -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"
|
||||
|
@@ -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(
|
||||
|
@@ -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()?;
|
||||
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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(())
|
||||
}
|
||||
|
@@ -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(())
|
||||
}
|
||||
|
@@ -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(())
|
||||
}
|
||||
|
||||
|
@@ -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(())
|
||||
}
|
||||
|
Reference in New Issue
Block a user