fix: refactor logging events to use owned asset instances and simplify log event structures

This commit is contained in:
2026-01-11 09:36:40 +01:00
parent 7d02443e67
commit 62f3c49e8a
4 changed files with 42 additions and 54 deletions

View File

@@ -6,7 +6,7 @@ use serde_json::json;
use crate::{ use crate::{
data_mgt::AssetTracker, data_mgt::AssetTracker,
logs::{LogEventBody, LogEventLine, LogEventType, log_event}, logs::{ LogEvent, LogEventType, log_event},
}; };
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
@@ -45,7 +45,7 @@ async fn api_upload(
Some(uploader_ip.clone()), Some(uploader_ip.clone()),
); );
log_event(LogEventType::AssetUploaded(&asset)); log_event(LogEventType::AssetUploaded(asset.clone()));
let id = asset.id(); let id = asset.id();
assets.add_asset(asset).await; assets.add_asset(asset).await;
let response_body = json!({ "link": format!("/bhs/{}", id) }); let response_body = json!({ "link": format!("/bhs/{}", id) });
@@ -58,7 +58,7 @@ async fn api_get_asset(
path: web::Path<String>, path: web::Path<String>,
assets: web::Data<AssetTracker>, assets: web::Data<AssetTracker>,
) -> Result<HttpResponse, actix_web::Error> { ) -> Result<HttpResponse, actix_web::Error> {
log_event(LogEventType::HttpRequest(&req.into())); log_event(LogEventType::HttpRequest(req.into()));
match assets.get_asset(&path.into_inner()).await { match assets.get_asset(&path.into_inner()).await {
None => Ok(HttpResponse::NotFound().body("Asset not found")), None => Ok(HttpResponse::NotFound().body("Asset not found")),
@@ -104,26 +104,26 @@ async fn api_stats(assets: web::Data<AssetTracker>) -> Result<HttpResponse, acti
let log_path = format!("{}access.log", LOG_DIR); let log_path = format!("{}access.log", LOG_DIR);
if let Ok(content) = fs::read_to_string(&log_path) { if let Ok(content) = fs::read_to_string(&log_path) {
for line in content.lines() { for line in content.lines() {
if let Ok(entry) = serde_json::from_str::<LogEventLine>(line) { if let Ok(entry) = serde_json::from_str::<LogEvent>(line) {
match entry.event { match entry.event {
LogEventBody::HttpRequest(_req) => { LogEventType::HttpRequest(_req) => {
request_count += 1; request_count += 1;
} }
LogEventBody::AssetUploaded(asset) => { LogEventType::AssetUploaded(asset) => {
total_uploads += 1; total_uploads += 1;
recent_activity.push(ActivityItem { recent_activity.push(ActivityItem {
action: "upload".to_string(), action: "upload".to_string(),
mime: asset.mime, mime: asset.mime(),
share_duration: asset.share_duration, share_duration: asset.share_duration(),
timestamp: entry.time, timestamp: entry.time,
}); });
} }
LogEventBody::AssetDeleted(asset) => { LogEventType::AssetDeleted(asset) => {
total_deleted += 1; total_deleted += 1;
recent_activity.push(ActivityItem { recent_activity.push(ActivityItem {
action: "delete".to_string(), action: "delete".to_string(),
mime: asset.mime, mime: asset.mime(),
share_duration: asset.share_duration, share_duration: asset.share_duration(),
timestamp: entry.time, timestamp: entry.time,
}); });
} }

View File

@@ -5,11 +5,12 @@ use anyhow::Result;
use chrono::{Duration, Utc}; use chrono::{Duration, Utc};
use futures::lock::Mutex; use futures::lock::Mutex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::DATA_STORAGE; use crate::DATA_STORAGE;
use crate::logs::{LogEventType, log_event}; use crate::logs::{LogEventType, log_event};
#[derive(Debug, Serialize, Deserialize, Default, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Asset { pub struct Asset {
id: String, id: String,
share_duration: u32, share_duration: u32,
@@ -45,8 +46,8 @@ impl Asset {
self.id.clone() self.id.clone()
} }
pub fn mime(&self) -> &str { pub fn mime(&self) -> String {
&self.mime self.mime.clone()
} }
pub fn content(&self) -> Vec<u8> { pub fn content(&self) -> Vec<u8> {
@@ -65,6 +66,10 @@ impl Asset {
self.expires_at self.expires_at
} }
pub fn mime_type(&self) -> &str {
&self.mime
}
pub fn size_bytes(&self) -> usize { pub fn size_bytes(&self) -> usize {
self.content.len() self.content.len()
} }
@@ -85,8 +90,16 @@ impl Asset {
std::fs::write(&path, self.to_bytes()?)?; std::fs::write(&path, self.to_bytes()?)?;
Ok(id) Ok(id)
} }
} }
impl From<Asset> for Value {
fn from(asset: Asset) -> Self {
serde_json::to_value(asset).unwrap()
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct AssetTracker { pub struct AssetTracker {
assets: Arc<Mutex<Vec<Asset>>>, assets: Arc<Mutex<Vec<Asset>>>,
@@ -110,8 +123,8 @@ impl AssetTracker {
let mut assets = self.assets.lock().await; let mut assets = self.assets.lock().await;
let removed_assets = assets.extract_if(.., |asset| asset.is_expired()); let removed_assets = assets.extract_if(.., |asset| asset.is_expired());
for asset in removed_assets { for asset in removed_assets {
log_event(LogEventType::AssetDeleted(&asset));
println!("[{}] Removing asset: {}", chrono::Local::now().to_rfc3339(), asset.id()); println!("[{}] Removing asset: {}", chrono::Local::now().to_rfc3339(), asset.id());
log_event(LogEventType::AssetDeleted(asset));
} }
} }

View File

@@ -6,31 +6,6 @@ use serde::{Deserialize, Serialize};
use crate::{LOG_DIR, data_mgt::Asset}; use crate::{LOG_DIR, data_mgt::Asset};
#[derive(Deserialize)]
pub struct LogEventLine {
pub time: String,
pub event: LogEventBody,
}
#[derive(Deserialize)]
pub enum LogEventBody {
AssetUploaded(LogAsset),
AssetDeleted(LogAsset),
HttpRequest(LogHttpRequest),
}
#[derive(Deserialize)]
pub struct LogAsset {
pub id: String,
pub share_duration: u32,
pub created_at: i64,
pub expires_at: i64,
pub mime: String,
pub uploader_ip: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct LogHttpRequest { pub struct LogHttpRequest {
pub method: String, pub method: String,
@@ -72,21 +47,21 @@ impl From<HttpRequest> for LogHttpRequest {
} }
} }
#[derive(Debug, Serialize)] #[derive(Debug, Serialize, Deserialize)]
pub enum LogEventType<'a> { pub enum LogEventType {
AssetUploaded(&'a Asset), AssetUploaded(Asset),
AssetDeleted(&'a Asset), AssetDeleted(Asset),
HttpRequest(&'a LogHttpRequest), HttpRequest(LogHttpRequest),
} }
#[derive(Debug, Serialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct LogEvent<'a> { pub struct LogEvent {
pub time: String, pub time: String,
pub event: LogEventType<'a>, pub event: LogEventType,
} }
impl<'a> From<LogEventType<'a>> for LogEvent<'a> { impl From<LogEventType> for LogEvent {
fn from(event: LogEventType<'a>) -> Self { fn from(event: LogEventType) -> Self {
let time = chrono::Utc::now().to_rfc3339(); let time = chrono::Utc::now().to_rfc3339();
LogEvent { time, event } LogEvent { time, event }
} }

View File

@@ -51,21 +51,21 @@ use crate::{
#[get("/")] #[get("/")]
async fn index(req: HttpRequest) -> actix_web::Result<NamedFile> { async fn index(req: HttpRequest) -> actix_web::Result<NamedFile> {
let path: PathBuf = PathBuf::from(HTML_DIR.to_string() + "index.html"); let path: PathBuf = PathBuf::from(HTML_DIR.to_string() + "index.html");
log_event(LogEventType::HttpRequest(&req.into())); log_event(LogEventType::HttpRequest(req.into()));
Ok(NamedFile::open(path)?) Ok(NamedFile::open(path)?)
} }
#[get("/stats")] #[get("/stats")]
async fn stats(req: HttpRequest) -> actix_web::Result<NamedFile> { async fn stats(req: HttpRequest) -> actix_web::Result<NamedFile> {
let path: PathBuf = PathBuf::from(HTML_DIR.to_string() + "stats.html"); let path: PathBuf = PathBuf::from(HTML_DIR.to_string() + "stats.html");
log_event(LogEventType::HttpRequest(&req.into())); log_event(LogEventType::HttpRequest(req.into()));
Ok(NamedFile::open(path)?) Ok(NamedFile::open(path)?)
} }
#[get("/bhs/{id}")] #[get("/bhs/{id}")]
async fn view_asset(req: HttpRequest) -> actix_web::Result<NamedFile> { async fn view_asset(req: HttpRequest) -> actix_web::Result<NamedFile> {
let path: PathBuf = PathBuf::from(HTML_DIR.to_string() + "view.html"); let path: PathBuf = PathBuf::from(HTML_DIR.to_string() + "view.html");
log_event(LogEventType::HttpRequest(&req.into())); log_event(LogEventType::HttpRequest(req.into()));
Ok(NamedFile::open(path)?) Ok(NamedFile::open(path)?)
} }
@@ -82,7 +82,7 @@ async fn catch_all(req: HttpRequest, _payload: Option<web::Json<Value>>) -> acti
} }
}; };
log_event(LogEventType::HttpRequest(&req.into())); log_event(LogEventType::HttpRequest(req.into()));
response response
} }