add idle timeout and version on settings screen
This commit is contained in:
parent
69f6a39396
commit
141334f7e2
27
src-tauri/Cargo.lock
generated
27
src-tauri/Cargo.lock
generated
@ -1035,7 +1035,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "creddy"
|
name = "creddy"
|
||||||
version = "0.4.5"
|
version = "0.4.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argon2",
|
"argon2",
|
||||||
"auto-launch",
|
"auto-launch",
|
||||||
@ -1059,6 +1059,7 @@ dependencies = [
|
|||||||
"tauri-build",
|
"tauri-build",
|
||||||
"tauri-plugin-single-instance",
|
"tauri-plugin-single-instance",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
"which",
|
"which",
|
||||||
"windows 0.51.1",
|
"windows 0.51.1",
|
||||||
@ -1202,10 +1203,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deranged"
|
name = "deranged"
|
||||||
version = "0.3.8"
|
version = "0.3.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946"
|
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"powerfmt",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3158,6 +3160,12 @@ dependencies = [
|
|||||||
"universal-hash",
|
"universal-hash",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "powerfmt"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
@ -4536,12 +4544,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.28"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48"
|
checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"deranged",
|
"deranged",
|
||||||
"itoa 1.0.9",
|
"itoa 1.0.9",
|
||||||
|
"powerfmt",
|
||||||
"serde",
|
"serde",
|
||||||
"time-core",
|
"time-core",
|
||||||
"time-macros",
|
"time-macros",
|
||||||
@ -4549,15 +4558,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time-core"
|
name = "time-core"
|
||||||
version = "0.1.1"
|
version = "0.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
|
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time-macros"
|
name = "time-macros"
|
||||||
version = "0.2.14"
|
version = "0.2.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a942f44339478ef67935ab2bbaec2fb0322496cf3cbe84b261e06ac3814c572"
|
checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"time-core",
|
"time-core",
|
||||||
]
|
]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "creddy"
|
name = "creddy"
|
||||||
version = "0.4.5"
|
version = "0.4.6"
|
||||||
description = "A friendly AWS credentials manager"
|
description = "A friendly AWS credentials manager"
|
||||||
authors = ["Joseph Montanaro"]
|
authors = ["Joseph Montanaro"]
|
||||||
license = ""
|
license = ""
|
||||||
@ -25,7 +25,7 @@ tauri-build = { version = "1.0.4", features = [] }
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
tauri = { version = "1.2", features = ["dialog", "dialog-open", "global-shortcut", "os-all", "system-tray"] }
|
tauri = { version = "1.2", features = [ "app-all", "dialog", "dialog-open", "global-shortcut", "os-all", "system-tray"] }
|
||||||
tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev" }
|
tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev" }
|
||||||
sodiumoxide = "0.2.7"
|
sodiumoxide = "0.2.7"
|
||||||
tokio = { version = ">=1.19", features = ["full"] }
|
tokio = { version = ">=1.19", features = ["full"] }
|
||||||
@ -47,6 +47,7 @@ argon2 = { version = "0.5.0", features = ["std"] }
|
|||||||
chacha20poly1305 = { version = "0.10.1", features = ["std"] }
|
chacha20poly1305 = { version = "0.10.1", features = ["std"] }
|
||||||
which = "4.4.0"
|
which = "4.4.0"
|
||||||
windows = { version = "0.51.1", features = ["Win32_Foundation", "Win32_System_Pipes"] }
|
windows = { version = "0.51.1", features = ["Win32_Foundation", "Win32_System_Pipes"] }
|
||||||
|
time = "0.3.31"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# by default Tauri runs in production mode
|
# by default Tauri runs in production mode
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use sqlx::{
|
use sqlx::{
|
||||||
@ -40,6 +41,7 @@ pub fn run() -> tauri::Result<()> {
|
|||||||
ipc::unlock,
|
ipc::unlock,
|
||||||
ipc::respond,
|
ipc::respond,
|
||||||
ipc::get_session_status,
|
ipc::get_session_status,
|
||||||
|
ipc::signal_activity,
|
||||||
ipc::save_credentials,
|
ipc::save_credentials,
|
||||||
ipc::get_config,
|
ipc::get_config,
|
||||||
ipc::save_config,
|
ipc::save_config,
|
||||||
@ -119,10 +121,30 @@ async fn setup(app: &mut App) -> Result<(), Box<dyn Error>> {
|
|||||||
|
|
||||||
let state = AppState::new(conf, session, pool, setup_errors, desktop_is_gnome);
|
let state = AppState::new(conf, session, pool, setup_errors, desktop_is_gnome);
|
||||||
app.manage(state);
|
app.manage(state);
|
||||||
|
|
||||||
|
// make sure we do this after managing app state, so that it doesn't panic
|
||||||
|
start_auto_locker(app.app_handle());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn start_auto_locker(app: AppHandle) {
|
||||||
|
rt::spawn(async move {
|
||||||
|
let state = app.state::<AppState>();
|
||||||
|
loop {
|
||||||
|
// this gives our session-timeout a minimum resolution of 10s, which seems fine?
|
||||||
|
let delay = Duration::from_secs(10);
|
||||||
|
tokio::time::sleep(delay).await;
|
||||||
|
|
||||||
|
if state.should_auto_lock().await {
|
||||||
|
state.lock().await.error_popup("Failed to lock Creddy");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn show_main_window(app: &AppHandle) -> Result<(), WindowError> {
|
pub fn show_main_window(app: &AppHandle) -> Result<(), WindowError> {
|
||||||
let w = app.get_window("main").ok_or(WindowError::NoMainWindow)?;
|
let w = app.get_window("main").ok_or(WindowError::NoMainWindow)?;
|
||||||
w.show()?;
|
w.show()?;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use auto_launch::AutoLaunchBuilder;
|
use auto_launch::AutoLaunchBuilder;
|
||||||
use is_terminal::IsTerminal;
|
use is_terminal::IsTerminal;
|
||||||
@ -45,6 +46,10 @@ impl HotkeysConfig {
|
|||||||
pub struct AppConfig {
|
pub struct AppConfig {
|
||||||
#[serde(default = "default_rehide_ms")]
|
#[serde(default = "default_rehide_ms")]
|
||||||
pub rehide_ms: u64,
|
pub rehide_ms: u64,
|
||||||
|
#[serde(default = "default_auto_lock")]
|
||||||
|
pub auto_lock: bool,
|
||||||
|
#[serde(default = "default_lock_after")]
|
||||||
|
pub lock_after: Duration,
|
||||||
#[serde(default = "default_start_minimized")]
|
#[serde(default = "default_start_minimized")]
|
||||||
pub start_minimized: bool,
|
pub start_minimized: bool,
|
||||||
#[serde(default = "default_start_on_login")]
|
#[serde(default = "default_start_on_login")]
|
||||||
@ -60,6 +65,8 @@ impl Default for AppConfig {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
AppConfig {
|
AppConfig {
|
||||||
rehide_ms: default_rehide_ms(),
|
rehide_ms: default_rehide_ms(),
|
||||||
|
auto_lock: default_auto_lock(),
|
||||||
|
lock_after: default_lock_after(),
|
||||||
start_minimized: default_start_minimized(),
|
start_minimized: default_start_minimized(),
|
||||||
start_on_login: default_start_on_login(),
|
start_on_login: default_start_on_login(),
|
||||||
terminal: default_term_config(),
|
terminal: default_term_config(),
|
||||||
@ -187,6 +194,30 @@ fn default_hotkey_config() -> HotkeysConfig {
|
|||||||
|
|
||||||
|
|
||||||
fn default_rehide_ms() -> u64 { 1000 }
|
fn default_rehide_ms() -> u64 { 1000 }
|
||||||
|
fn default_auto_lock() -> bool { true }
|
||||||
|
fn default_lock_after() -> Duration { Duration::from_secs(43200) }
|
||||||
// start minimized and on login only in production mode
|
// start minimized and on login only in production mode
|
||||||
fn default_start_minimized() -> bool { !cfg!(debug_assertions) }
|
fn default_start_minimized() -> bool { !cfg!(debug_assertions) }
|
||||||
fn default_start_on_login() -> bool { !cfg!(debug_assertions) }
|
fn default_start_on_login() -> bool { !cfg!(debug_assertions) }
|
||||||
|
|
||||||
|
|
||||||
|
// struct DurationVisitor;
|
||||||
|
|
||||||
|
// impl<'de> Visitor<'de> for DurationVisitor {
|
||||||
|
// type Value = Duration;
|
||||||
|
|
||||||
|
// fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
|
||||||
|
// write!(formatter, "an integer between 0 and 2^64 - 1")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn visit_u64<E: de::Error>(self, v: u64) -> Result<Duration, E> {
|
||||||
|
// Ok(Duration::from_secs(v))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// fn duration_from_secs<'de, D>(deserializer: D) -> Result<Duration, D::Error>
|
||||||
|
// where D: Deserializer<'de>
|
||||||
|
// {
|
||||||
|
// deserializer.deserialize_u64(DurationVisitor)
|
||||||
|
// }
|
||||||
|
@ -247,6 +247,19 @@ pub enum UnlockError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, ThisError, AsRefStr)]
|
||||||
|
pub enum LockError {
|
||||||
|
#[error("App is not unlocked")]
|
||||||
|
NotUnlocked,
|
||||||
|
#[error("Database error: {0}")]
|
||||||
|
DbError(#[from] SqlxError),
|
||||||
|
#[error(transparent)]
|
||||||
|
Setup(#[from] SetupError),
|
||||||
|
#[error(transparent)]
|
||||||
|
TauriError(#[from] tauri::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, ThisError, AsRefStr)]
|
#[derive(Debug, ThisError, AsRefStr)]
|
||||||
pub enum CryptoError {
|
pub enum CryptoError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
@ -369,6 +382,7 @@ impl_serialize_basic!(SetupError);
|
|||||||
impl_serialize_basic!(GetCredentialsError);
|
impl_serialize_basic!(GetCredentialsError);
|
||||||
impl_serialize_basic!(ClientInfoError);
|
impl_serialize_basic!(ClientInfoError);
|
||||||
impl_serialize_basic!(WindowError);
|
impl_serialize_basic!(WindowError);
|
||||||
|
impl_serialize_basic!(LockError);
|
||||||
|
|
||||||
|
|
||||||
impl Serialize for HandlerError {
|
impl Serialize for HandlerError {
|
||||||
|
@ -56,6 +56,13 @@ pub async fn get_session_status(app_state: State<'_, AppState>) -> Result<String
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn signal_activity(app_state: State<'_, AppState>) -> Result<(), ()> {
|
||||||
|
app_state.signal_activity().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn save_credentials(
|
pub async fn save_credentials(
|
||||||
credentials: BaseCredentials,
|
credentials: BaseCredentials,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
use tokio::{
|
use tokio::{
|
||||||
sync::RwLock,
|
sync::RwLock,
|
||||||
@ -102,6 +103,7 @@ impl VisibilityLease {
|
|||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub config: RwLock<AppConfig>,
|
pub config: RwLock<AppConfig>,
|
||||||
pub session: RwLock<Session>,
|
pub session: RwLock<Session>,
|
||||||
|
pub last_activity: RwLock<OffsetDateTime>,
|
||||||
pub request_count: RwLock<u64>,
|
pub request_count: RwLock<u64>,
|
||||||
pub waiting_requests: RwLock<HashMap<u64, Sender<RequestResponse>>>,
|
pub waiting_requests: RwLock<HashMap<u64, Sender<RequestResponse>>>,
|
||||||
pub pending_terminal_request: RwLock<bool>,
|
pub pending_terminal_request: RwLock<bool>,
|
||||||
@ -123,6 +125,7 @@ impl AppState {
|
|||||||
AppState {
|
AppState {
|
||||||
config: RwLock::new(config),
|
config: RwLock::new(config),
|
||||||
session: RwLock::new(session),
|
session: RwLock::new(session),
|
||||||
|
last_activity: RwLock::new(OffsetDateTime::now_utc()),
|
||||||
request_count: RwLock::new(0),
|
request_count: RwLock::new(0),
|
||||||
waiting_requests: RwLock::new(HashMap::new()),
|
waiting_requests: RwLock::new(HashMap::new()),
|
||||||
pending_terminal_request: RwLock::new(false),
|
pending_terminal_request: RwLock::new(false),
|
||||||
@ -210,6 +213,38 @@ impl AppState {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn lock(&self) -> Result<(), LockError> {
|
||||||
|
let mut session = self.session.write().await;
|
||||||
|
match *session {
|
||||||
|
Session::Empty => Err(LockError::NotUnlocked),
|
||||||
|
Session::Locked(_) => Err(LockError::NotUnlocked),
|
||||||
|
Session::Unlocked{..} => {
|
||||||
|
*session = Session::load(&self.pool).await?;
|
||||||
|
|
||||||
|
let app_handle = app::APP.get().unwrap();
|
||||||
|
app_handle.emit_all("locked", None::<usize>)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn signal_activity(&self) {
|
||||||
|
let mut last_activity = self.last_activity.write().await;
|
||||||
|
*last_activity = OffsetDateTime::now_utc();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn should_auto_lock(&self) -> bool {
|
||||||
|
let config = self.config.read().await;
|
||||||
|
if !config.auto_lock || !self.is_unlocked().await {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let last_activity = self.last_activity.read().await;
|
||||||
|
let elapsed = OffsetDateTime::now_utc() - *last_activity;
|
||||||
|
elapsed >= config.lock_after
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn is_unlocked(&self) -> bool {
|
pub async fn is_unlocked(&self) -> bool {
|
||||||
let session = self.session.read().await;
|
let session = self.session.read().await;
|
||||||
matches!(*session, Session::Unlocked{..})
|
matches!(*session, Session::Unlocked{..})
|
||||||
@ -223,7 +258,7 @@ impl AppState {
|
|||||||
|
|
||||||
pub async fn session_creds_cloned(&self) -> Result<SessionCredentials, GetCredentialsError> {
|
pub async fn session_creds_cloned(&self) -> Result<SessionCredentials, GetCredentialsError> {
|
||||||
let app_session = self.session.read().await;
|
let app_session = self.session.read().await;
|
||||||
let (_bsae, session) = app_session.try_get()?;
|
let (_base, session) = app_session.try_get()?;
|
||||||
Ok(session.clone())
|
Ok(session.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
use tauri::{
|
use tauri::{
|
||||||
AppHandle,
|
AppHandle,
|
||||||
|
CustomMenuItem,
|
||||||
|
Manager,
|
||||||
SystemTray,
|
SystemTray,
|
||||||
SystemTrayEvent,
|
SystemTrayEvent,
|
||||||
SystemTrayMenu,
|
SystemTrayMenu,
|
||||||
CustomMenuItem,
|
async_runtime as rt,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::app;
|
use crate::app;
|
||||||
|
use crate::state::AppState;
|
||||||
|
|
||||||
|
|
||||||
pub fn create() -> SystemTray {
|
pub fn create() -> SystemTray {
|
||||||
@ -21,13 +24,18 @@ pub fn create() -> SystemTray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn handle_event(app: &AppHandle, event: SystemTrayEvent) {
|
pub fn handle_event(app_handle: &AppHandle, event: SystemTrayEvent) {
|
||||||
match event {
|
match event {
|
||||||
SystemTrayEvent::MenuItemClick{ id, .. } => {
|
SystemTrayEvent::MenuItemClick{ id, .. } => {
|
||||||
match id.as_str() {
|
match id.as_str() {
|
||||||
"exit" => app.exit(0),
|
"exit" => app_handle.exit(0),
|
||||||
"show_hide" => {
|
"show_hide" => {
|
||||||
let _ = app::toggle_main_window(app);
|
let _ = app::toggle_main_window(app_handle);
|
||||||
|
let new_handle = app_handle.app_handle();
|
||||||
|
rt::spawn(async move {
|
||||||
|
let state = new_handle.state::<AppState>();
|
||||||
|
state.signal_activity().await;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,15 @@
|
|||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "creddy",
|
"productName": "creddy",
|
||||||
"version": "0.4.5"
|
"version": "0.4.6"
|
||||||
},
|
},
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"allowlist": {
|
"allowlist": {
|
||||||
|
"app": {
|
||||||
|
"all": true,
|
||||||
|
"show": false,
|
||||||
|
"hide": false
|
||||||
|
},
|
||||||
"os": {"all": true},
|
"os": {"all": true},
|
||||||
"dialog": {"open": true}
|
"dialog": {"open": true}
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user