diff --git a/src/check.rs b/src/check.rs index f2ba76d..0184ef2 100644 --- a/src/check.rs +++ b/src/check.rs @@ -1,9 +1,17 @@ -use std::{collections::{HashMap, HashSet}, sync::Mutex}; +use std::{ + collections::{HashMap, HashSet}, + sync::Mutex, +}; -use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use json::JsonValue; +use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; -use crate::{docker::get_images_from_docker_daemon, image::Image, registry::{check_auth, get_token, get_latest_digests}, utils::unsplit_image}; +use crate::{ + docker::get_images_from_docker_daemon, + image::Image, + registry::{check_auth, get_latest_digests, get_token}, + utils::unsplit_image, +}; #[cfg(feature = "cli")] use crate::docker::get_image_from_docker_daemon; @@ -25,7 +33,10 @@ where } } -pub async fn get_all_updates(socket: Option, config: &JsonValue) -> Vec<(String, Option)> { +pub async fn get_all_updates( + socket: Option, + config: &JsonValue, +) -> Vec<(String, Option)> { let image_map_mutex: Mutex>> = Mutex::new(HashMap::new()); let local_images = get_images_from_docker_daemon(socket).await; local_images.par_iter().for_each(|image| { @@ -44,7 +55,10 @@ pub async fn get_all_updates(socket: Option, config: &JsonValue) -> Vec< .par_iter() .filter(|image| &image.registry == registry) .collect(); - let credentials = config["authentication"][registry].clone().take_string().or(None); + let credentials = config["authentication"][registry] + .clone() + .take_string() + .or(None); let mut latest_images = match check_auth(registry, config) { Some(auth_url) => { let token = get_token(images.clone(), &auth_url, &credentials); @@ -72,7 +86,10 @@ pub async fn get_all_updates(socket: Option, config: &JsonValue) -> Vec< #[cfg(feature = "cli")] pub async fn get_update(image: &str, socket: Option, config: &JsonValue) -> Option { let local_image = get_image_from_docker_daemon(socket, image).await; - let credentials = config["authentication"][&local_image.registry].clone().take_string().or(None); + let credentials = config["authentication"][&local_image.registry] + .clone() + .take_string() + .or(None); let token = match check_auth(&local_image.registry, config) { Some(auth_url) => get_token(vec![&local_image], &auth_url, &credentials), None => String::new(), @@ -85,4 +102,4 @@ pub async fn get_update(image: &str, socket: Option, config: &JsonValue) Some(d) => Some(d != &local_image.digest.unwrap()), None => None, } -} \ No newline at end of file +} diff --git a/src/docker.rs b/src/docker.rs index 2844e50..a4200f3 100644 --- a/src/docker.rs +++ b/src/docker.rs @@ -1,7 +1,4 @@ -use bollard::{ - secret::ImageSummary, - ClientVersion, Docker, -}; +use bollard::{secret::ImageSummary, ClientVersion, Docker}; #[cfg(feature = "cli")] use bollard::secret::ImageInspect; diff --git a/src/formatting.rs b/src/formatting.rs index e5c9149..87f78f9 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -58,7 +58,7 @@ pub fn print_update(name: &str, has_update: &Option) { } pub fn print_raw_update(name: &str, has_update: &Option) { - let result = object! {images: {[name]: *has_update}} ; + let result = object! {images: {[name]: *has_update}}; println!("{}", result); } diff --git a/src/main.rs b/src/main.rs index 17298c9..185572a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,8 @@ +#[cfg(feature = "cli")] +use check::{get_all_updates, get_update}; use clap::{Parser, Subcommand}; #[cfg(feature = "cli")] use formatting::{print_raw_update, print_raw_updates, print_update, print_updates, Spinner}; -#[cfg(feature = "cli")] -use check::{get_all_updates, get_update}; #[cfg(feature = "server")] use server::serve; use std::path::PathBuf; @@ -10,13 +10,13 @@ use utils::load_config; pub mod check; pub mod docker; -pub mod image; -pub mod registry; -pub mod utils; #[cfg(feature = "cli")] pub mod formatting; +pub mod image; +pub mod registry; #[cfg(feature = "server")] pub mod server; +pub mod utils; #[derive(Parser)] #[command(version, about, long_about = None)] diff --git a/src/registry.rs b/src/registry.rs index e18b57e..35f41f1 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -9,33 +9,45 @@ use http_auth::parse_challenges; use crate::{error, image::Image, warn}; pub fn check_auth(registry: &str, config: &JsonValue) -> Option { - let protocol = if config["insecure_registries"].contains(registry) { "http" } else { "https" }; + let protocol = if config["insecure_registries"].contains(registry) { + "http" + } else { + "https" + }; let response = ureq::get(&format!("{}://{}/v2/", protocol, registry)).call(); match response { Ok(_) => None, Err(Error::Status(401, response)) => match response.header("www-authenticate") { Some(challenge) => Some(parse_www_authenticate(challenge)), - None => error!("Unauthorized to access registry {} and no way to authenticate was provided", registry), + None => error!( + "Unauthorized to access registry {} and no way to authenticate was provided", + registry + ), }, Err(Error::Transport(error)) => { match error.kind() { ErrorKind::Dns => { warn!("Failed to lookup the IP of the registry, retrying."); - return check_auth(registry, config) - }, // If something goes really wrong, this can get stuck in a loop + return check_auth(registry, config); + } // If something goes really wrong, this can get stuck in a loop ErrorKind::ConnectionFailed => { warn!("Connection probably timed out, retrying."); - return check_auth(registry, config) - }, // Same here - _ => error!("{}", error) + return check_auth(registry, config); + } // Same here + _ => error!("{}", error), } - }, + } Err(e) => error!("{}", e), } } pub fn get_latest_digest(image: &Image, token: Option<&String>, config: &JsonValue) -> Image { - let protocol = if config["insecure_registries"].contains(json::JsonValue::from(image.registry.clone())) { "http" } else { "https" }; + let protocol = + if config["insecure_registries"].contains(json::JsonValue::from(image.registry.clone())) { + "http" + } else { + "https" + }; let mut request = ureq::head(&format!( "{}://{}/v2/{}/manifests/{}", protocol, &image.registry, &image.repository, &image.tag @@ -93,7 +105,11 @@ pub fn get_latest_digest(image: &Image, token: Option<&String>, config: &JsonVal } } -pub fn get_latest_digests(images: Vec<&Image>, token: Option<&String>, config: &JsonValue) -> Vec { +pub fn get_latest_digests( + images: Vec<&Image>, + token: Option<&String>, + config: &JsonValue, +) -> Vec { let result: Mutex> = Mutex::new(Vec::new()); images.par_iter().for_each(|&image| { let digest = get_latest_digest(image, token, config).digest; @@ -111,13 +127,13 @@ pub fn get_token(images: Vec<&Image>, auth_url: &str, credentials: &Option base_request.set("Authorization", &format!("Basic {}", creds)), - None => base_request + None => base_request, }; - let raw_response = match base_request.call() - { + let raw_response = match base_request.call() { Ok(response) => match response.into_string() { Ok(res) => res, Err(e) => { @@ -128,15 +144,15 @@ pub fn get_token(images: Vec<&Image>, auth_url: &str, credentials: &Option { warn!("Failed to lookup the IP of the registry, retrying."); - return get_token(images, auth_url, credentials) - }, // If something goes really wrong, this can get stuck in a loop + return get_token(images, auth_url, credentials); + } // If something goes really wrong, this can get stuck in a loop ErrorKind::ConnectionFailed => { warn!("Connection probably timed out, retrying."); - return get_token(images, auth_url, credentials) - }, // Same here - _ => error!("Token request failed\n{}!", error) + return get_token(images, auth_url, credentials); + } // Same here + _ => error!("Token request failed\n{}!", error), } - }, + } Err(e) => { error!("Token request failed!\n{}", e) } diff --git a/src/server.rs b/src/server.rs index bcfc77e..465076a 100644 --- a/src/server.rs +++ b/src/server.rs @@ -43,20 +43,44 @@ pub async fn serve(port: &u16, socket: Option, config: JsonValue) -> std async fn _static(data: StateRef<'_, Arc>>, path: PathRef<'_>) -> WebResponse { match path.0 { - "/" => WebResponse::builder().header("Content-Type", "text/html").body(ResponseBody::from(HTML)).unwrap(), - "/assets/index.js" => WebResponse::builder().header("Content-Type", "text/javascript").body(ResponseBody::from(JS.replace("=\"neutral\"", &format!("=\"{}\"", data.lock().await.theme)))).unwrap(), - "/assets/index.css" => WebResponse::builder().header("Content-Type", "text/css").body(ResponseBody::from(CSS)).unwrap(), - "/favicon.ico" => WebResponse::builder().header("Content-Type", "image/vnd.microsoft.icon").body(ResponseBody::from(FAVICON_ICO)).unwrap(), - "/favicon.svg" => WebResponse::builder().header("Content-Type", "image/svg+xml").body(ResponseBody::from(FAVICON_SVG)).unwrap(), - "/apple-touch-icon.png" => WebResponse::builder().header("Content-Type", "image/png").body(ResponseBody::from(APPLE_TOUCH_ICON)).unwrap(), - _ => WebResponse::builder().status(404).body(ResponseBody::from("Not found")).unwrap() + "/" => WebResponse::builder() + .header("Content-Type", "text/html") + .body(ResponseBody::from(HTML)) + .unwrap(), + "/assets/index.js" => WebResponse::builder() + .header("Content-Type", "text/javascript") + .body(ResponseBody::from(JS.replace( + "=\"neutral\"", + &format!("=\"{}\"", data.lock().await.theme), + ))) + .unwrap(), + "/assets/index.css" => WebResponse::builder() + .header("Content-Type", "text/css") + .body(ResponseBody::from(CSS)) + .unwrap(), + "/favicon.ico" => WebResponse::builder() + .header("Content-Type", "image/vnd.microsoft.icon") + .body(ResponseBody::from(FAVICON_ICO)) + .unwrap(), + "/favicon.svg" => WebResponse::builder() + .header("Content-Type", "image/svg+xml") + .body(ResponseBody::from(FAVICON_SVG)) + .unwrap(), + "/apple-touch-icon.png" => WebResponse::builder() + .header("Content-Type", "image/png") + .body(ResponseBody::from(APPLE_TOUCH_ICON)) + .unwrap(), + _ => WebResponse::builder() + .status(404) + .body(ResponseBody::from("Not found")) + .unwrap(), } } async fn json(data: StateRef<'_, Arc>>) -> WebResponse { - WebResponse::new(ResponseBody::from( - json::stringify(data.lock().await.json.clone()) - )) + WebResponse::new(ResponseBody::from(json::stringify( + data.lock().await.json.clone(), + ))) } async fn refresh(data: StateRef<'_, Arc>>) -> WebResponse { @@ -69,7 +93,7 @@ struct ServerData { json: JsonValue, socket: Option, config: JsonValue, - theme: &'static str + theme: &'static str, } impl ServerData { @@ -82,13 +106,15 @@ impl ServerData { }, raw_updates: Vec::new(), config, - theme: "neutral" + theme: "neutral", }; s.refresh().await; s } async fn refresh(&mut self) { - let updates = sort_update_vec(&get_all_updates(self.socket.clone(), &self.config["authentication"]).await); + let updates = sort_update_vec( + &get_all_updates(self.socket.clone(), &self.config["authentication"]).await, + ); self.raw_updates = updates; self.json = to_json(&self.raw_updates); let last_updated = Local::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true); diff --git a/src/utils.rs b/src/utils.rs index d1cea88..2fdb01a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -32,9 +32,9 @@ pub fn split_image(image: &str) -> (String, String, String) { match RE.captures(image) { Some(c) => { let registry = match c.name("registry") { - Some(registry) => registry.as_str().to_owned(), - None => String::from("registry-1.docker.io"), - }; + Some(registry) => registry.as_str().to_owned(), + None => String::from("registry-1.docker.io"), + }; return ( registry.clone(), match c.name("repository") { @@ -52,7 +52,7 @@ pub fn split_image(image: &str) -> (String, String, String) { Some(tag) => tag.as_str().to_owned(), None => String::from("latest"), }, - ) + ); } None => error!("Failed to parse image {}", image), }