Menu
Public repository / master
AkurAI-Notes / crates/notes-api/src/handlers/client_logs.rs
Latest change 2b8f40b2b033847632488d2ab876e6df05c91fd7 - fmt: rustfmt after request-logging change by agent
use crate::handlers::RouteHandler;
use crate::router_setup::{bad_request, internal_error, json_response};
use akurai_http::Request;
use akurai_json::Value;
use serde_json::Value as JsonValue;
const MONITOR_INGEST_URL: &str = "https://monitor.olibuijr.com/api/ingest";
const HOST_ID: i64 = 1;
const APPLICATION_ID: i64 = 7;
const MAX_BODY_BYTES: usize = 256 * 1024;
pub fn client_logs_route() -> RouteHandler {
Box::new(|req: &Request| {
if req.body.len() > MAX_BODY_BYTES {
return bad_request("request body too large");
}
let mut body: JsonValue = match serde_json::from_slice(&req.body) {
Ok(body) => body,
Err(_) => return bad_request("invalid JSON"),
};
let forwarded = match tag_logs(&mut body) {
Ok(forwarded) => forwarded,
Err(message) => return bad_request(message),
};
let token = match std::env::var("MONITOR_INGEST_TOKEN") {
Ok(token) if !token.trim().is_empty() => token,
_ => return internal_error("monitor ingest is not configured"),
};
let response = match reqwest::blocking::Client::builder()
.timeout(std::time::Duration::from_secs(5))
.build()
.and_then(|client| {
client
.post(MONITOR_INGEST_URL)
.bearer_auth(token)
.json(&body)
.send()
}) {
Ok(response) if response.status().is_success() => response,
Ok(response) => {
eprintln!(
"client log forwarding failed with status {}",
response.status()
);
return internal_error("monitor ingest rejected logs");
}
Err(error) => {
eprintln!("client log forwarding failed: {error}");
return internal_error("monitor ingest unavailable");
}
};
json_response(Value::Object(vec![
("forwarded".into(), Value::Int(forwarded as i64)),
(
"status".into(),
Value::Int(response.status().as_u16() as i64),
),
]))
})
}
fn tag_logs(body: &mut JsonValue) -> Result<usize, &'static str> {
let logs = body
.get_mut("logs")
.and_then(JsonValue::as_array_mut)
.ok_or("logs must be an array")?;
let forwarded = logs.len();
for log in logs {
let log = log.as_object_mut().ok_or("each log must be an object")?;
log.insert("origin".into(), JsonValue::String("client".into()));
log.insert("host_id".into(), JsonValue::from(HOST_ID));
log.insert("application_id".into(), JsonValue::from(APPLICATION_ID));
}
Ok(forwarded)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tags_each_browser_log_with_notes_monitor_identity() {
let mut body = serde_json::json!({
"logs": [{
"source": "untrusted-client",
"line": "warn",
"origin": "server",
"host_id": 999,
"application_id": 999
}]
});
assert_eq!(tag_logs(&mut body), Ok(1));
let log = &body["logs"][0];
assert_eq!(log["origin"], "client");
assert_eq!(log["host_id"], HOST_ID);
assert_eq!(log["application_id"], APPLICATION_ID);
}
}