From 2026a8f4579b1db0f6e5e7b11ac33c13969adb6c Mon Sep 17 00:00:00 2001 From: Tyler Murphy Date: Wed, 1 Feb 2023 20:34:22 -0500 Subject: static serve refactor --- src/public/console.rs | 264 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 src/public/console.rs (limited to 'src/public/console.rs') diff --git a/src/public/console.rs b/src/public/console.rs new file mode 100644 index 0000000..16bf4a3 --- /dev/null +++ b/src/public/console.rs @@ -0,0 +1,264 @@ +use axum::{ + http::{Method, Uri}, + response::Response, +}; +use lazy_static::lazy_static; +use serde::Serialize; +use serde_json::{ser::Formatter, Value}; +use std::{collections::VecDeque, io, net::IpAddr}; +use tokio::sync::Mutex; + +use crate::types::http::ResponseCode; + +struct LogMessage { + ip: IpAddr, + method: Method, + uri: Uri, + path: String, + body: String, +} + +impl ToString for LogMessage { + fn to_string(&self) -> String { + let mut ip = self.ip.to_string(); + if ip.contains("::ffff:") { + ip = ip.as_str()[7..].to_string(); + } + let color = match self.method { + Method::GET => "#3fe04f", + Method::POST => "#853fe0", + Method::PATCH => "#e0773f", + Method::PUT => "#e0cb3f", + Method::HEAD => "#3f75e0", + Method::DELETE => "#e04c3f", + Method::CONNECT => "#3fe0ad", + Method::TRACE => "#e03fc5", + Method::OPTIONS => "#423fe0", + _ => "white", + }; + format!("
{} {} {}{} {}
", + ip, color, self.method, self.path, sanatize(&self.uri.to_string()), self.body) + } +} + +lazy_static! { + static ref LOG: Mutex> = Mutex::new(VecDeque::with_capacity(200)); +} + +pub async fn log(ip: IpAddr, method: Method, uri: Uri, path: Option, body: Option) { + let path = path.unwrap_or_default(); + let body = body.unwrap_or_default(); + + if path == "/api/admin" { + return; + } + + tracing::info!("{} {} {}{} {}", &ip, &method, &path, &uri, &body); + + let message = LogMessage { + ip, + method, + uri, + path, + body: beautify(&body), + }; + + let mut lock = LOG.lock().await; + if lock.len() > 200 { + lock.pop_back(); + } + lock.push_front(message); +} + +struct HtmlFormatter; +impl Formatter for HtmlFormatter { + fn write_null(&mut self, writer: &mut W) -> io::Result<()> + where + W: ?Sized + io::Write, + { + writer.write_all(b"null") + } + + fn write_bool(&mut self, writer: &mut W, value: bool) -> io::Result<()> + where + W: ?Sized + io::Write, + { + let s = if value { + b"true" as &[u8] + } else { + b"false" as &[u8] + }; + writer.write_all(s) + } + + fn write_i8(&mut self, writer: &mut W, value: i8) -> io::Result<()> + where + W: ?Sized + io::Write, + { + let buff = format!("{value}"); + writer.write_all(buff.as_bytes()) + } + + fn write_i16(&mut self, writer: &mut W, value: i16) -> io::Result<()> + where + W: ?Sized + io::Write, + { + let buff = format!("{value}"); + writer.write_all(buff.as_bytes()) + } + + fn write_i32(&mut self, writer: &mut W, value: i32) -> io::Result<()> + where + W: ?Sized + io::Write, + { + let buff = format!("{value}"); + writer.write_all(buff.as_bytes()) + } + + fn write_i64(&mut self, writer: &mut W, value: i64) -> io::Result<()> + where + W: ?Sized + io::Write, + { + let buff = format!("{value}"); + writer.write_all(buff.as_bytes()) + } + + fn write_u8(&mut self, writer: &mut W, value: u8) -> io::Result<()> + where + W: ?Sized + io::Write, + { + let buff = format!("{value}"); + writer.write_all(buff.as_bytes()) + } + + fn write_u16(&mut self, writer: &mut W, value: u16) -> io::Result<()> + where + W: ?Sized + io::Write, + { + let buff = format!("{value}"); + writer.write_all(buff.as_bytes()) + } + + fn write_u32(&mut self, writer: &mut W, value: u32) -> io::Result<()> + where + W: ?Sized + io::Write, + { + let buff = format!("{value}"); + writer.write_all(buff.as_bytes()) + } + + fn write_u64(&mut self, writer: &mut W, value: u64) -> io::Result<()> + where + W: ?Sized + io::Write, + { + let buff = format!("{value}"); + writer.write_all(buff.as_bytes()) + } + + fn write_f32(&mut self, writer: &mut W, value: f32) -> io::Result<()> + where + W: ?Sized + io::Write, + { + let buff = format!("{value}"); + writer.write_all(buff.as_bytes()) + } + + fn write_f64(&mut self, writer: &mut W, value: f64) -> io::Result<()> + where + W: ?Sized + io::Write, + { + let buff = format!("{value}"); + writer.write_all(buff.as_bytes()) + } + + fn begin_string(&mut self, writer: &mut W) -> io::Result<()> + where + W: ?Sized + io::Write, + { + writer.write_all(b"\"") + } + + fn end_string(&mut self, writer: &mut W) -> io::Result<()> + where + W: ?Sized + io::Write, + { + writer.write_all(b"\"") + } + + fn begin_object_key(&mut self, writer: &mut W, first: bool) -> io::Result<()> + where + W: ?Sized + io::Write, + { + if first { + writer.write_all(b"") + } else { + writer.write_all(b",") + } + } + + fn end_object_key(&mut self, writer: &mut W) -> io::Result<()> + where + W: ?Sized + io::Write, + { + writer.write_all(b"") + } +} + +pub fn sanatize(input: &str) -> String { + input + .replace('&', "&") + .replace('<', "<") + .replace('>', ">") +} + +pub fn beautify(body: &str) -> String { + let body = sanatize(body); + + if body.is_empty() { + return String::new(); + } + let Ok(mut json) = serde_json::from_str::(&body) else { + return body + }; + if json["password"].is_string() { + json["password"] = Value::String("********".to_owned()); + } + let mut writer: Vec = Vec::with_capacity(128); + let mut serializer = serde_json::Serializer::with_formatter(&mut writer, HtmlFormatter); + if json.serialize(&mut serializer).is_err() { + return body; + } + String::from_utf8_lossy(&writer).to_string() +} + +pub async fn generate() -> Response { + let lock = LOG.lock().await; + + let mut html = r#" + + + + + + + + + XSSBook - Console + + + +
+ "# + .to_string(); + + for message in lock.iter() { + html.push_str(&message.to_string()); + } + + html.push_str(""); + + ResponseCode::Success.html(&html) +} -- cgit v1.2.3-freya