Skip to content

Commit aa6110d

Browse files
authored
Merge pull request #13 from Lissy93/FEAT/version-check
[Fix] Adds a check to ensure using a compatible version of AdGuard API
2 parents 9c89fa2 + 7c5d566 commit aa6110d

File tree

4 files changed

+97
-14
lines changed

4 files changed

+97
-14
lines changed

.github/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
Enables support for HTTPS, via optional user-defined protocol (#7)
1+
Adds a check to ensure that the users version of AdGuard Home is recent enough to support the required API calls (#5)
2+
Allows for graceful exit, instead of panicking

Cargo.lock

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "adguardian"
3-
version = "1.3.0"
3+
version = "1.4.0"
44
edition = "2021"
55
authors = ["Alicia Sykes"]
66
description = "Terminal-based, real-time traffic monitoring and statistics for your AdGuard Home instance "
@@ -25,8 +25,12 @@ crossterm = { version = "0.22.0", features = ["serde"] }
2525
futures = "0.3"
2626
# HTTP client
2727
reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "rustls-tls"] }
28-
# Decerilization of JSON responses
28+
# Decerilization of responses
2929
serde = { version = "1.0", features = ["derive"] }
30+
# Decerilization of JSON responses
31+
serde_json = "1.0"
32+
# Read and calculate semantic version numbers
33+
semver = "1.0"
3034
# Date + time manipulation
3135
tokio = { version = "1", features = ["full"] }
3236
# Terminal UI library

src/welcome.rs

Lines changed: 80 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ use std::{
66
use reqwest::{Client, Error};
77
use colored::*;
88

9+
use serde_json::Value;
10+
use semver::{Version};
11+
12+
/// Reusable function that just prints success messages to the console
913
fn print_info(text: &str, is_secondary: bool) {
1014
if is_secondary {
1115
println!("{}", text.green().italic().dimmed());
@@ -14,7 +18,7 @@ fn print_info(text: &str, is_secondary: bool) {
1418
};
1519
}
1620

17-
21+
/// Prints the AdGuardian ASCII art to console
1822
fn print_ascii_art() {
1923
let art = r"
2024
█████╗ ██████╗ ██████╗ ██╗ ██╗ █████╗ ██████╗ ██████╗ ██╗ █████╗ ███╗ ██╗
@@ -30,19 +34,22 @@ fn print_ascii_art() {
3034
print_info("For documentation and support, please visit: https://github.com/lissy93/adguardian-term\n", true);
3135
}
3236

33-
fn print_error(address: &str, error: Option<&Error>) {
37+
/// Print error message, along with (optional) stack trace, then exit
38+
fn print_error(message: &str, sub_message: &str, error: Option<&Error>) {
3439
eprintln!(
35-
"{}{}",
36-
format!("Failed to connect to AdGuard at {}", address).red(),
40+
"{}{}{}",
41+
format!("{}", message).red(),
3742
match error {
3843
Some(err) => format!("\n{}", err).red().dimmed(),
3944
None => "".red().dimmed(),
4045
},
46+
format!("\n{}", sub_message).yellow(),
4147
);
42-
eprintln!("{}\n{}", "\nPlease check your environmental variables and try again.".yellow(), "Exiting...".blue());
43-
}
4448

49+
std::process::exit(1);
50+
}
4551

52+
/// Given a key, get the value from the environmental variables, and print it to the console
4653
fn get_env(key: &str) -> Result<String, env::VarError> {
4754
env::var(key).map(|v| {
4855
println!(
@@ -58,6 +65,39 @@ fn get_env(key: &str) -> Result<String, env::VarError> {
5865
})
5966
}
6067

68+
/// Given a possibly undefined version number, check if it's present and supported
69+
fn check_version(version: Option<&str>) {
70+
let min_version = Version::parse("0.107.29").unwrap();
71+
72+
match version {
73+
Some(version_str) => {
74+
let adguard_version = Version::parse(&version_str[1..]).unwrap();
75+
76+
if adguard_version < min_version {
77+
print_error(
78+
"AdGuard Home version is too old, and is now unsupported",
79+
format!("You're running AdGuard {}. Please upgrade to v{} or later.", version_str, min_version.to_string()).as_str(),
80+
None,
81+
);
82+
}
83+
},
84+
None => {
85+
print_error(
86+
"Unsupported AdGuard Home version",
87+
format!(
88+
concat!(
89+
"Failed to get the version number of your AdGuard Home instance.\n",
90+
"This usually means you're running an old, and unsupported version.\n",
91+
"Please upgrade to v{} or later."
92+
), min_version.to_string()
93+
).as_str(),
94+
None,
95+
);
96+
}
97+
}
98+
}
99+
100+
/// With the users specified AdGuard details, verify the connection (exit on fail)
61101
async fn verify_connection(
62102
client: &Client,
63103
ip: String,
@@ -82,16 +122,44 @@ async fn verify_connection(
82122
.send()
83123
.await {
84124
Ok(res) if res.status().is_success() => {
85-
println!("{}", "AdGuard connection successful!\n".green());
125+
// Get version string (if present), and check if valid - exit if not
126+
let body: Value = res.json().await?;
127+
check_version(body["version"].as_str());
128+
// All good! Print success message :)
129+
let safe_version = body["version"].as_str().unwrap_or("mystery version");
130+
println!("{}", format!("AdGuard ({}) connection successful!\n", safe_version).green());
86131
Ok(())
87132
}
88-
Ok(_) | Err(_) => {
89-
print_error(&format!("{}:{}", ip, port), None);
90-
std::process::exit(1);
133+
// Connection failed to authenticate. Print error and exit
134+
Ok(_) => {
135+
print_error(
136+
&format!("Authentication with AdGuard at {}:{} failed", ip, port),
137+
"Please check your environmental variables and try again.",
138+
None,
139+
);
140+
Ok(())
141+
},
142+
// Connection failed to establish. Print error and exit
143+
Err(e) => {
144+
print_error(
145+
&format!("Failed to connect to AdGuard at: {}:{}", ip, port),
146+
"Please check your environmental variables and try again.",
147+
Some(&e),
148+
);
149+
Ok(())
91150
}
92151
}
93152
}
94153

154+
/// Initiate the welcome script
155+
/// This function will:
156+
/// - Print the AdGuardian ASCII art
157+
/// - Check for the required environmental variables
158+
/// - Prompt the user to enter any missing variables
159+
/// - Verify the connection to the AdGuard instance
160+
/// - Verify authentication is successful
161+
/// - Verify the AdGuard Home version is supported
162+
/// - Then either print a success message, or show instructions to fix and exit
95163
pub async fn welcome() -> Result<(), Box<dyn std::error::Error>> {
96164
print_ascii_art();
97165
println!("{}", "Starting initialization checks...".blue());
@@ -138,11 +206,13 @@ pub async fn welcome() -> Result<(), Box<dyn std::error::Error>> {
138206
}
139207
}
140208

209+
// Grab the values of the (now set) environmental variables
141210
let ip = get_env("ADGUARD_IP")?;
142211
let port = get_env("ADGUARD_PORT")?;
143212
let protocol = get_env("ADGUARD_PROTOCOL")?;
144213
let username = get_env("ADGUARD_USERNAME")?;
145214
let password = get_env("ADGUARD_PASSWORD")?;
146215

216+
// Verify that we can connect, authenticate, and that version is supported (exit on failure)
147217
verify_connection(&client, ip, port, protocol, username, password).await
148218
}

0 commit comments

Comments
 (0)