use std::{collections::BTreeMap, os::unix::fs::PermissionsExt, path::Path, time::Duration}; use tokio::time::Instant; const SLASHINFO: &str = "/srv/tomatenmhark-slashinfo/"; const REDIRECT: &str = "/srv/tomatenmhark-redirect/"; pub struct Status( pub BTreeMap, Option)>, pub BTreeMap)>, ); impl Status { pub fn empty() -> Self { Self(Default::default(), Default::default()) } pub fn query_sync(dbg: bool) -> Self { let mut map = BTreeMap::new(); query_status_sync( |k, e, r, v, dur| { let (v, add) = v .split_once('\n') .map(|(v, add)| (v, add.trim())) .unwrap_or((v, "")); map.insert( k.to_owned(), ( e, r, v.trim().to_owned(), if add.is_empty() { None } else { Some(add.to_owned()) }, dur, ), ); }, dbg, ); let mut rest = BTreeMap::new(); if let Ok(rd) = std::fs::read_dir(SLASHINFO) { for f in rd.filter_map(Result::ok) { if let Some(id) = f.file_name().to_str() { if !map.contains_key(id) { let mut p = f.path(); p.push("desc"); if let Ok(desc) = std::fs::read_to_string(&p) { let info = Path::new(SLASHINFO).join(id).join("index.html"); let info = info.starts_with(SLASHINFO) && info.try_exists().ok() == Some(true); let redirect = Path::new(REDIRECT).join(id); let redirect = redirect.starts_with(REDIRECT) && redirect.try_exists().ok() == Some(true); let desc = desc.trim(); if let Some(i) = desc.find('\n') { rest.insert( id.to_owned(), ( info, redirect, desc[0..i].trim().to_owned(), Some(desc[i + 1..].trim().to_owned()), ), ); } else { rest.insert(id.to_owned(), (info, redirect, desc.to_owned(), None)); } } } } } } Self(map, rest) } pub async fn query_async(dbg: bool) -> Self { let mut map = BTreeMap::new(); query_status_async( |k, e, r, v, dur| { let (v, add) = v .split_once('\n') .map(|(v, add)| (v, add.trim())) .unwrap_or((v, "")); map.insert( k.to_owned(), ( e, r, v.trim().to_owned(), if add.is_empty() { None } else { Some(add.to_owned()) }, dur, ), ); }, dbg, ) .await; let mut rest = BTreeMap::new(); if let Ok(mut rd) = tokio::fs::read_dir(SLASHINFO).await { while let Ok(Some(f)) = rd.next_entry().await { if let Some(id) = f.file_name().to_str() { if !map.contains_key(id) { let mut p = f.path(); p.push("desc"); if let Ok(desc) = tokio::fs::read_to_string(&p).await { let info = Path::new(SLASHINFO).join(id).join("index.html"); let info = info.starts_with(SLASHINFO) && tokio::fs::try_exists(info).await.ok() == Some(true); let redirect = Path::new(REDIRECT).join(id); let redirect = redirect.starts_with(REDIRECT) && tokio::fs::try_exists(redirect).await.ok() == Some(true); let desc = desc.trim(); if let Some(i) = desc.find('\n') { rest.insert( id.to_owned(), ( info, redirect, desc[0..i].trim().to_owned(), Some(desc[i + 1..].trim().to_owned()), ), ); } else { rest.insert(id.to_owned(), (info, redirect, desc.to_owned(), None)); } } } } } } Self(map, rest) } } fn query_status_sync(mut func: impl FnMut(&str, bool, bool, &str, Option), dbg: bool) { if let Ok(rd) = std::fs::read_dir("/tmp/") { for f in rd.filter_map(Result::ok) { if let Some(name) = f.file_name().to_str() { if let Some(id) = name .strip_prefix("tomatenmhark-status-") .map(|v| v.trim()) .filter(|v| !v.is_empty()) { if let Ok(status) = std::fs::read_to_string(f.path()) .as_ref() .map(|v| v.trim_end()) { if !status.is_empty() { let info = Path::new(SLASHINFO).join(id).join("index.html"); let info = info.starts_with(SLASHINFO) && info.try_exists().ok() == Some(true); let redirect = Path::new(REDIRECT).join(id); let redirect = redirect.starts_with(REDIRECT) && redirect.try_exists().ok() == Some(true); func(id, info, redirect, status, None); } } } } } } if let Ok(rd) = std::fs::read_dir("/srv/tomatenmhark-dystatus/") { for f in rd.filter_map(Result::ok) { if let Some(name) = f.file_name().to_str() { if f.metadata() .is_ok_and(|meta| meta.is_file() && meta.permissions().mode() & 1 == 1) { let id = name.trim(); if !id.is_empty() { let start = dbg.then(Instant::now); if let Ok(output) = std::process::Command::new(f.path()).output() { let elapsed = start.map(|v| v.elapsed()); let out = String::from_utf8_lossy(&output.stdout); let out = out.trim_end(); if dbg || !out.is_empty() { let info = Path::new(SLASHINFO).join(id).join("index.html"); let info = info.starts_with(SLASHINFO) && info.try_exists().ok() == Some(true); let redirect = Path::new(REDIRECT).join(id); let redirect = redirect.starts_with(REDIRECT) && redirect.try_exists().ok() == Some(true); func(id, info, redirect, out, elapsed); } } } } } } } } async fn query_status_async( mut func: impl FnMut(&str, bool, bool, &str, Option), dbg: bool, ) { if let Ok(mut rd) = tokio::fs::read_dir("/tmp/").await { while let Ok(Some(f)) = rd.next_entry().await { if let Some(name) = f.file_name().to_str() { if let Some(id) = name .strip_prefix("tomatenmhark-status-") .map(|v| v.trim()) .filter(|v| !v.is_empty()) { if let Ok(status) = tokio::fs::read_to_string(f.path()) .await .as_ref() .map(|v| v.trim_end()) { if !status.is_empty() { let info = Path::new(SLASHINFO).join(id).join("index.html"); let info = info.starts_with(SLASHINFO) && tokio::fs::try_exists(info).await.ok() == Some(true); let redirect = Path::new(REDIRECT).join(id); let redirect = redirect.starts_with(REDIRECT) && tokio::fs::try_exists(redirect).await.ok() == Some(true); func(id, info, redirect, status, None); } } } } } } if let Ok(mut rd) = tokio::fs::read_dir("/srv/tomatenmhark-dystatus/").await { while let Ok(Some(f)) = rd.next_entry().await { if let Some(name) = f.file_name().to_str() { if f.metadata() .await .is_ok_and(|meta| meta.is_file() && meta.permissions().mode() & 1 == 1) { let id = name.trim(); if !id.is_empty() { let start = dbg.then(Instant::now); if let Ok(output) = tokio::process::Command::new(f.path()).output().await { let elapsed = start.map(|v| v.elapsed()); let out = String::from_utf8_lossy(&output.stdout); let out = out.trim_end(); if dbg || !out.is_empty() { let info = Path::new(SLASHINFO).join(id).join("index.html"); let info = info.starts_with(SLASHINFO) && tokio::fs::try_exists(info).await.ok() == Some(true); let redirect = Path::new(REDIRECT).join(id); let redirect = redirect.starts_with(REDIRECT) && tokio::fs::try_exists(redirect).await.ok() == Some(true); func(id, info, redirect, out, elapsed); } } } } } } } }