use std::{fs::OpenOptions, io::Write}; use actix_web::HttpRequest; use serde::Serialize; use crate::{LOG_DIR, data_mgt::Asset}; #[derive(Debug, Serialize)] pub struct LogHttpRequest { pub method: String, pub path: String, pub query_string: String, pub scheme: String, pub ip: String, pub real_ip: String, pub user_agent: String, } impl From for LogHttpRequest { fn from(req: HttpRequest) -> Self { let method = req.method().as_str().to_string(); let uri = req.uri(); let path = uri.path().to_string(); let query_string = uri.query().unwrap_or("-").to_string(); let connection_info = req.connection_info(); let scheme = connection_info.scheme().to_string(); let ip = connection_info.peer_addr().unwrap_or("-").to_string(); let real_ip = connection_info.realip_remote_addr().unwrap_or("-").to_string(); let user_agent = req .headers() .get("user-agent") .and_then(|v| v.to_str().ok()) .unwrap_or("-") .to_string(); LogHttpRequest { method, path, query_string, scheme, ip, real_ip, user_agent, } } } #[derive(Debug, Serialize)] pub enum LogEventType<'a> { AssetUploaded(&'a Asset), AssetDeleted(&'a Asset), HttpRequest(&'a LogHttpRequest), } #[derive(Debug, Serialize)] pub struct LogEvent<'a> { pub time: String, pub event: LogEventType<'a>, } impl<'a> From> for LogEvent<'a> { fn from(event: LogEventType<'a>) -> Self { let time = chrono::Utc::now().to_rfc3339(); LogEvent { time, event } } } pub fn log_event(event: LogEventType) { let log_path = LOG_DIR.to_string() + "access.log"; let Ok(mut file) = OpenOptions::new().create(true).append(true).open(log_path) else { eprintln!("failed to open log file for asset event"); return; }; let log_event: LogEvent = event.into(); let line = serde_json::to_string(&log_event).unwrap_or_else(|e| e.to_string()); let _ = writeln!(file, "{}", line); }