about summary refs log tree commit diff
path: root/src/main.rs
diff options
context:
space:
mode:
authorgennyble <gen@nyble.dev>2025-02-16 10:17:28 -0600
committergennyble <gen@nyble.dev>2025-02-16 10:17:28 -0600
commit0c5986851dee23e09751ae594d8a43a84a0dab61 (patch)
treeed27d132aa04b86ecabd9cf5bb440b7d87f7bff3 /src/main.rs
parente1b51d7dfc24c443af34410da2fed6aa6db52b62 (diff)
downloadawake-0c5986851dee23e09751ae594d8a43a84a0dab61.tar.gz
awake-0c5986851dee23e09751ae594d8a43a84a0dab61.zip
Network stats
Diffstat (limited to 'src/main.rs')
-rwxr-xr-xsrc/main.rs113
1 files changed, 54 insertions, 59 deletions
diff --git a/src/main.rs b/src/main.rs
index 26e1cf0..cddd2f1 100755
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,16 +1,26 @@
 mod atom;
+mod db;
 mod error;
 mod fs;
+mod gatherer;
+mod griph;
 mod ifc;
 mod markup;
 mod settings;
 mod templated;
 mod timeparse;
 mod util;
-mod db;
 
-use std::{fs::File, io::{BufRead, BufReader, Write}, os::unix::fs::MetadataExt, str::FromStr, sync::Arc, time::Duration};
+use std::{
+	fs::File,
+	io::{BufRead, BufReader, Write},
+	os::unix::fs::MetadataExt,
+	str::FromStr,
+	sync::Arc,
+	time::Duration,
+};
 
+use ::time::OffsetDateTime;
 use axum::{
 	body::Body,
 	extract::{Path, State},
@@ -25,6 +35,7 @@ use confindent::{Confindent, Node};
 use db::Database;
 pub use error::RuntimeError;
 use fs::Filesystem;
+use gatherer::Gatherer;
 use settings::Settings;
 use templated::Frontmatter;
 use tokio_util::io::ReaderStream;
@@ -38,7 +49,8 @@ use crate::{
 
 #[derive(Clone)]
 pub struct AwakeState {
-	pub database: Arc<Database>
+	pub database: Arc<Database>,
+	pub cache_path: Utf8PathBuf,
 }
 
 #[tokio::main]
@@ -64,6 +76,7 @@ async fn main() {
 	let templates = conf.child_value("Templates").unwrap();
 	let hostname = conf.child_owned("Hostname").unwrap();
 	let dbpath = conf.child_owned("Database").unwrap();
+	let cache = conf.child_owned("Cache").unwrap();
 
 	let database = Database::new(dbpath.into());
 	database.create_tables();
@@ -71,18 +84,24 @@ async fn main() {
 	let fs = Filesystem::new(&webroot);
 
 	let state = AwakeState {
-		database: Arc::new(database)
+		database: Arc::new(database),
+		cache_path: cache.into(),
 	};
 
-	let mi_state = state.clone();
-	let meminfo_thread = std::thread::spawn(move || {
-		loop {
-			let meminfo = Meminfo::current();
-			mi_state.database.insert_host_meminfo(meminfo);
-
-			std::thread::sleep(Duration::from_secs(60));
+	match std::env::args().nth(1).as_deref() {
+		Some("graph") => {
+			gatherer::make_mem_graph(&state);
 		}
-	});
+		_ => (),
+	}
+
+	let mut gatherer = Gatherer::new(state.clone());
+
+	#[cfg(target_os = "linux")]
+	gatherer.start();
+
+	#[cfg(not(target_os = "linux"))]
+	tracing::warn!("compilation target was not linux; not collecing stat data");
 
 	let settings = Settings {
 		template_dir: Utf8PathBuf::from(webroot.join(templates))
@@ -93,9 +112,11 @@ async fn main() {
 
 	let app = Router::new()
 		.route("/", get(index_handler))
+		.route("/api/stats/:name", get(stats))
 		.route("/*path", get(handler))
 		.layer(Extension(fs))
-		.layer(Extension(settings)).with_state(state);
+		.layer(Extension(settings))
+		.with_state(state);
 
 	let listener = tokio::net::TcpListener::bind("0.0.0.0:2560").await.unwrap();
 	axum::serve(listener, app).await.unwrap()
@@ -190,6 +211,7 @@ fn redirect<S: Into<String>>(redirection: S) -> Response {
 const STREAM_AFTER: u64 = 20 * 1024 * 1024;
 
 async fn send_file(filepath: Utf8PathBuf) -> Result<Response, RuntimeError> {
+	tracing::debug!(target: "send_file", filepath = ?filepath);
 	let ext = filepath.extension().unwrap_or_default();
 	let stem = filepath.file_stem().unwrap_or_default();
 
@@ -209,6 +231,7 @@ async fn send_file(filepath: Utf8PathBuf) -> Result<Response, RuntimeError> {
 		"png" => "image/png",
 		_ => "",
 	};
+	tracing::debug!(target: "send_file", mime = ?mime);
 
 	let mut response = Response::builder();
 	if !mime.is_empty() {
@@ -223,6 +246,8 @@ async fn send_file(filepath: Utf8PathBuf) -> Result<Response, RuntimeError> {
 		let stream = ReaderStream::new(file);
 		Ok(response.body(Body::from_stream(stream)).unwrap())
 	} else {
+		tracing::debug!("small file, sending entirely at once");
+
 		let content = Filesystem::read(filepath).await?;
 		Ok(response.body(Body::from(content)).unwrap())
 	}
@@ -380,63 +405,33 @@ async fn send_template(
 
 fn template_content(state: AwakeState, frontmatter: &Frontmatter, marked: String) -> String {
 	let Ok(mut doc) = Document::from_str(&marked, Options::default()) else {
-		return marked
+		return marked;
 	};
 
 	if frontmatter.get("system-stats").is_some() {
 		let mem = state.database.get_last_host_meminfo();
 
-		doc.set("stats.mem.total", mem.total_kb / 1024);
-		doc.set("stats.mem.usage", mem.usage() / 1024);
+		doc.set("stats.mem.total", mem.total_kb / 1000);
+		doc.set("stats.mem.usage", mem.usage() / 1000);
 	}
 
 	doc.compile()
 }
 
-struct Meminfo {
-	pub total: usize,
-	pub free: usize,
-	pub avaialable: usize
-}
-
-impl Meminfo {
-	pub fn current() -> Self {
-		let procinfo = File::open("/proc/meminfo").unwrap();
-		let bread = BufReader::new(procinfo);
+async fn stats(State(state): State<AwakeState>, Path(name): Path<String>) -> Response {
+	const NAMES: &[&'static str] = &["current_hostmeminfo.gif", "current_hostnetinfo.gif"];
 
-		let mut meminfo = Meminfo {
-			total: 0,
-			free: 0,
-			avaialable: 0
-		};
-
-		for line in bread.lines() {
-			let line = line.unwrap();
-
-			if let Some((raw_key, raw_value_kb)) = line.split_once(':') {
-				let value = if let Some(raw_value) = raw_value_kb.trim().strip_suffix(" kB") {
-					if let Ok(parsed) = raw_value.parse() {
-						parsed
-					} else {
-						continue;
-					}
-				} else {
-					continue;
-				};
-
-				match raw_key.trim() {
-					"MemTotal" => meminfo.total = value,
-					"MemFree" => meminfo.free = value,
-					"MemAvailable" => meminfo.avaialable = value,
-					_ => ()
-				}
-			}
+	if NAMES.contains(&name.as_str()) {
+		let path = state.cache_path.join(name);
+		match send_file(path).await {
+			Ok(resp) => resp,
+			Err(e) => Response::builder().body(Body::from(e.to_string())).unwrap(),
 		}
+	} else {
+		tracing::debug!("invalid stat requested: {name}");
 
-		meminfo
-	}
-
-	pub fn usage(&self) -> usize {
-		self.total - self.avaialable
+		Response::builder()
+			.body(Body::from("that stat is not real"))
+			.unwrap()
 	}
-}
\ No newline at end of file
+}