use std::{
io:: {self, Write},
env,
time::Duration
};
use reqwest::{Client, Error};
use colored::*;
use serde_json::Value;
use semver::{Version};
fn print_info(text: &str, is_secondary: bool) {
if is_secondary {
println!("{}", text.green().italic().dimmed());
} else {
println!("{}", text.green());
};
}
fn print_ascii_art() {
let art = r"
█████╗ ██████╗ ██████╗ ██╗ ██╗ █████╗ ██████╗ ██████╗ ██╗ █████╗ ███╗ ██╗
██╔══██╗██╔══██╗██╔════╝ ██║ ██║██╔══██╗██╔══██╗██╔══██╗██║██╔══██╗████╗ ██║
███████║██║ ██║██║ ███╗██║ ██║███████║██████╔╝██║ ██║██║███████║██╔██╗ ██║
██╔══██║██║ ██║██║ ██║██║ ██║██╔══██║██╔══██╗██║ ██║██║██╔══██║██║╚██╗██║
██║ ██║██████╔╝╚██████╔╝╚██████╔╝██║ ██║██║ ██║██████╔╝██║██║ ██║██║ ╚████║
╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝
";
print_info(art, false);
print_info("\nWelcome to AdGuardian Terminal Edition!", false);
print_info("Terminal-based, real-time traffic monitoring and statistics for your AdGuard TUI instance", true);
print_info("For documentation and support, please visit: https://github.com/lissy93/adguardian-term\n", true);
}
fn print_error(message: &str, sub_message: &str, error: Option<&Error>) {
eprintln!(
"{}{}{}",
format!("{}", message).red(),
match error {
Some(err) => format!("\n{}", err).red().dimmed(),
None => "".red().dimmed(),
},
format!("\n{}", sub_message).yellow(),
);
std::process::exit(1);
}
fn get_env(key: &str) -> Result<String, env::VarError> {
env::var(key).map(|v| {
println!(
"{}",
format!(
"{} is set to {}",
key.bold(),
if key.contains("PASSWORD") { "******" } else { &v }
)
.green()
);
v
})
}
fn check_version(version: Option<&str>) {
let min_version = Version::parse("0.107.29").unwrap();
match version {
Some(version_str) => {
let adguard_version = Version::parse(&version_str[1..]).unwrap();
if adguard_version < min_version {
print_error(
"AdGuard TUI version is too old, and is now unsupported",
format!("You're running AdGuard {}. Please upgrade to v{} or later.", version_str, min_version.to_string()).as_str(),
None,
);
}
},
None => {
print_error(
"Unsupported AdGuard TUI version",
format!(
concat!(
"Failed to get the version number of your AdGuard TUI instance.\n",
"This usually means you're running an old, and unsupported version.\n",
"Please upgrade to v{} or later."
), min_version.to_string()
).as_str(),
None,
);
}
}
}
async fn verify_connection(
client: &Client,
ip: String,
port: String,
protocol: String,
username: String,
password: String,
) -> Result<(), Box<dyn std::error::Error>> {
println!("{}", "\nVerifying connection to your AdGuard instance...".blue());
let auth_string = format!("{}:{}", username, password);
let auth_header_value = format!("Basic {}", base64::encode(&auth_string));
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("Authorization", auth_header_value.parse()?);
let url = format!("{}://{}:{}/control/status", protocol, ip, port);
match client
.get(&url)
.headers(headers)
.timeout(Duration::from_secs(2))
.send()
.await {
Ok(res) if res.status().is_success() => {
let body: Value = res.json().await?;
check_version(body["version"].as_str());
let safe_version = body["version"].as_str().unwrap_or("mystery version");
println!("{}", format!("AdGuard ({}) connection successful!\n", safe_version).green());
Ok(())
}
Ok(_) => {
print_error(
&format!("Authentication with AdGuard at {}:{} failed", ip, port),
"Please check your environmental variables and try again.",
None,
);
Ok(())
},
Err(e) => {
print_error(
&format!("Failed to connect to AdGuard at: {}:{}", ip, port),
"Please check your environmental variables and try again.",
Some(&e),
);
Ok(())
}
}
}
pub async fn welcome() -> Result<(), Box<dyn std::error::Error>> {
print_ascii_art();
println!("{}", "Starting initialization checks...".blue());
let client = Client::new();
let flags = [
("--adguard-ip", "ADGUARD_IP"),
("--adguard-port", "ADGUARD_PORT"),
("--adguard-username", "ADGUARD_USERNAME"),
("--adguard-password", "ADGUARD_PASSWORD"),
];
let protocol: String = env::var("ADGUARD_PROTOCOL").unwrap_or_else(|_| "http".into()).parse()?;
env::set_var("ADGUARD_PROTOCOL", protocol);
let mut args = std::env::args().peekable();
while let Some(arg) = args.next() {
for &(flag, var) in &flags {
if arg == flag {
if let Some(value) = args.peek() {
env::set_var(var, value);
args.next();
}
}
}
}
for &key in &["ADGUARD_IP", "ADGUARD_PORT", "ADGUARD_USERNAME", "ADGUARD_PASSWORD"] {
if env::var(key).is_err() {
println!(
"{}",
format!("The {} environmental variable is not yet set", key.bold()).yellow()
);
print!("{}", format!("› Enter a value for {}: ", key).blue().bold());
io::stdout().flush()?;
let mut value = String::new();
io::stdin().read_line(&mut value)?;
env::set_var(key, value.trim());
}
}
let ip = get_env("ADGUARD_IP")?;
let port = get_env("ADGUARD_PORT")?;
let protocol = get_env("ADGUARD_PROTOCOL")?;
let username = get_env("ADGUARD_USERNAME")?;
let password = get_env("ADGUARD_PASSWORD")?;
verify_connection(&client, ip, port, protocol, username, password).await
}