about summary refs log tree commit diff
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rwxr-xr-xsrc/main.rs111
1 files changed, 104 insertions, 7 deletions
diff --git a/src/main.rs b/src/main.rs
index fa1ac5c..26e1cf0 100755
--- a/src/main.rs
+++ b/src/main.rs
@@ -7,12 +7,13 @@ mod settings;
 mod templated;
 mod timeparse;
 mod util;
+mod db;
 
-use std::{io::Write, os::unix::fs::MetadataExt, str::FromStr};
+use std::{fs::File, io::{BufRead, BufReader, Write}, os::unix::fs::MetadataExt, str::FromStr, sync::Arc, time::Duration};
 
 use axum::{
 	body::Body,
-	extract::Path,
+	extract::{Path, State},
 	http::{header, StatusCode},
 	response::Response,
 	routing::get,
@@ -21,9 +22,11 @@ use axum::{
 use bempline::{variables, Document, Options};
 use camino::Utf8PathBuf;
 use confindent::{Confindent, Node};
+use db::Database;
 pub use error::RuntimeError;
 use fs::Filesystem;
 use settings::Settings;
+use templated::Frontmatter;
 use tokio_util::io::ReaderStream;
 use tracing_subscriber::{fmt::time, prelude::*, EnvFilter};
 use util::{Referer, RemoteIp, SessionId};
@@ -33,6 +36,11 @@ use crate::{
 	templated::Templated,
 };
 
+#[derive(Clone)]
+pub struct AwakeState {
+	pub database: Arc<Database>
+}
+
 #[tokio::main]
 async fn main() {
 	match std::env::args().nth(1).as_deref() {
@@ -55,9 +63,27 @@ async fn main() {
 	let webroot: Utf8PathBuf = conf.child_parse("Webroot").unwrap();
 	let templates = conf.child_value("Templates").unwrap();
 	let hostname = conf.child_owned("Hostname").unwrap();
+	let dbpath = conf.child_owned("Database").unwrap();
+
+	let database = Database::new(dbpath.into());
+	database.create_tables();
 
 	let fs = Filesystem::new(&webroot);
 
+	let state = AwakeState {
+		database: Arc::new(database)
+	};
+
+	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));
+		}
+	});
+
 	let settings = Settings {
 		template_dir: Utf8PathBuf::from(webroot.join(templates))
 			.canonicalize_utf8()
@@ -69,29 +95,31 @@ async fn main() {
 		.route("/", get(index_handler))
 		.route("/*path", get(handler))
 		.layer(Extension(fs))
-		.layer(Extension(settings));
+		.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()
 }
 
 async fn index_handler(
+	state: State<AwakeState>,
 	fse: Extension<Filesystem>,
 	se: Extension<Settings>,
 	sid: SessionId,
 	rfr: Option<Referer>,
 ) -> Response {
-	handler(fse, se, sid, rfr, Path(String::from("/"))).await
+	handler(state, fse, se, sid, rfr, Path(String::from("/"))).await
 }
 
 async fn handler(
+	State(state): State<AwakeState>,
 	Extension(fs): Extension<Filesystem>,
 	Extension(settings): Extension<Settings>,
 	sid: SessionId,
 	rfr: Option<Referer>,
 	Path(path): Path<String>,
 ) -> Response {
-	match falible_handler(fs, settings, sid, rfr, path).await {
+	match falible_handler(state, fs, settings, sid, rfr, path).await {
 		Ok(resp) => resp,
 		Err(re) => Response::builder()
 			.body(Body::from(re.to_string()))
@@ -100,6 +128,7 @@ async fn handler(
 }
 
 async fn falible_handler(
+	state: AwakeState,
 	fs: Filesystem,
 	settings: Settings,
 	sid: SessionId,
@@ -134,7 +163,7 @@ async fn falible_handler(
 		let result = Templated::from_str(&content);
 
 		match result {
-			Ok(templated) => send_template(templated, resolve, webpath, settings, sid).await,
+			Ok(templated) => send_template(templated, resolve, webpath, state, settings, sid).await,
 			Err(e) => {
 				tracing::warn!("error sending template {e}");
 
@@ -203,6 +232,7 @@ async fn send_template(
 	templated: Templated,
 	resolve: PathResolution,
 	webpath: Webpath,
+	state: AwakeState,
 	settings: Settings,
 	sid: SessionId,
 ) -> Result<Response, RuntimeError> {
@@ -335,7 +365,11 @@ async fn send_template(
 	tracing::trace!("finished published block");
 
 	// insert the page content itself
-	let markedup = markup::process(&templated.content);
+	let mut markedup = markup::process(&templated.content);
+	if templated.frontmatter.get("use-template").is_some() {
+		markedup = template_content(state, &templated.frontmatter, markedup);
+	}
+
 	template.set("main", markedup);
 
 	Ok(Response::builder()
@@ -343,3 +377,66 @@ async fn send_template(
 		.body(Body::from(template.compile()))
 		.unwrap())
 }
+
+fn template_content(state: AwakeState, frontmatter: &Frontmatter, marked: String) -> String {
+	let Ok(mut doc) = Document::from_str(&marked, Options::default()) else {
+		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.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);
+
+		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,
+					_ => ()
+				}
+			}
+		}
+
+		meminfo
+	}
+
+	pub fn usage(&self) -> usize {
+		self.total - self.avaialable
+	}
+}
\ No newline at end of file