diff --git a/src/dokku.rs b/src/dokku.rs new file mode 100644 index 0000000..bfe7c16 --- /dev/null +++ b/src/dokku.rs @@ -0,0 +1,58 @@ +use async_std::fs::read_to_string; +use async_std::path::PathBuf; +use serde::Serialize; +use std::collections::HashSet; + +#[derive(Debug, Serialize)] +pub struct DokkuApp { + name: String, + hosts: HashSet, +} + +impl DokkuApp { + pub async fn try_from_path(path: PathBuf) -> Option { + let vhost_path = path.join("VHOST"); + + if !vhost_path.is_file().await { + return None; + } + + let current_vhosts = read_to_string(&vhost_path).await.ok()?; + + if current_vhosts.is_empty() { + return None; + } + + Some(DokkuApp { + name: String::from(path.file_name()?.to_str()?), + hosts: current_vhosts.lines().map(String::from).collect(), + }) + } +} + +pub fn get_dokku_root() -> Option { + // First, check if we have a custom directory + if let Some(dokku_root) = std::env::var_os("DOKKU_ROOT") { + return Some(std::path::PathBuf::from(dokku_root)); + } + + // Next, check for a dokku user and its home directory + match nix::unistd::User::from_name("dokku") { + Ok(Some(user)) => return Some(user.dir), + + // If the user doesn't exist, do nothing + Ok(None) => return None, + + // If there was an error, give up + _ => {} + } + + // Finally, try a hard-coded path + let dokku_home_guess = std::path::PathBuf::from("/home/dokku"); + if dokku_home_guess.is_dir() { + return Some(dokku_home_guess); + } + + // If there's nothing else, give up + None +} diff --git a/src/main.rs b/src/main.rs index 8c5986b..c5fdf8c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,79 +1,20 @@ -use async_std::fs::read_to_string; use async_std::path::PathBuf; use async_std::stream::StreamExt; use axum::extract::State; use axum::{routing::get, Router}; -use serde::Serialize; -use std::collections::HashSet; use std::net::{IpAddr, Ipv6Addr, SocketAddr}; use tokio::task::JoinSet; +mod dokku; +mod utils; + +use dokku::DokkuApp; + #[derive(Clone)] struct AppState { dokku_root: PathBuf, } -#[derive(Debug, Serialize)] -struct DokkuApp { - name: String, - hosts: HashSet, -} - -impl DokkuApp { - async fn try_from_path(path: PathBuf) -> Option { - let vhost_path = path.join("VHOST"); - - if !vhost_path.is_file().await { - return None; - } - - let current_vhosts = read_to_string(&vhost_path).await.ok()?; - - if current_vhosts.is_empty() { - return None; - } - - Some(DokkuApp { - name: String::from(path.file_name()?.to_str()?), - hosts: current_vhosts.lines().map(String::from).collect(), - }) - } -} - -fn get_dokku_root() -> Option { - // First, check if we have a custom directory - if let Some(dokku_root) = std::env::var_os("DOKKU_ROOT") { - return Some(std::path::PathBuf::from(dokku_root)); - } - - // Next, check for a dokku user and its home directory - match nix::unistd::User::from_name("dokku") { - Ok(Some(user)) => return Some(user.dir), - - // If the user doesn't exist, do nothing - Ok(None) => return None, - - // If there was an error, give up - _ => {} - } - - // Finally, try a hard-coded path - let dokku_home_guess = std::path::PathBuf::from("/home/dokku"); - if dokku_home_guess.is_dir() { - return Some(dokku_home_guess); - } - - // If there's nothing else, give up - None -} - -fn osstring_starts_with(data: std::ffi::OsString, prefix: char) -> bool { - match data.into_string() { - Ok(s) => matches!(s.chars().next(), Some(c) if c == prefix), - Err(_) => false, - } -} - async fn hosts(State(state): State) -> axum::Json> { let mut dir_entries = state.dokku_root.read_dir().await.unwrap(); @@ -81,7 +22,7 @@ async fn hosts(State(state): State) -> axum::Json> { while let Some(Ok(dir_entry)) = dir_entries.next().await { // Skip hidden folders / files - if osstring_starts_with(dir_entry.file_name(), '.') { + if utils::osstring_starts_with(dir_entry.file_name(), '.') { continue; } @@ -107,7 +48,7 @@ async fn hosts(State(state): State) -> axum::Json> { #[tokio::main] async fn main() { - let dokku_root = match get_dokku_root() { + let dokku_root = match dokku::get_dokku_root() { Some(path) => path, None => { println!("Failed to find dokku root"); diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..872c5e3 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,9 @@ +use std::ffi::OsString; + +#[inline] +pub fn osstring_starts_with(data: OsString, prefix: char) -> bool { + match data.into_string() { + Ok(s) => matches!(s.chars().next(), Some(c) if c == prefix), + Err(_) => false, + } +}