use std::{ ffi::{self, CStr, CString}, sync::Mutex, }; use rusqlite::{Connection, params}; use time::{Duration, OffsetDateTime}; #[repr(C)] struct ModuleRequest<'req> { headers_len: ffi::c_ulong, headers: &'req [[*const ffi::c_char; 2]], body_len: ffi::c_ulong, body: *const u8, } #[repr(C)] struct ModuleResponse { status: ffi::c_ushort, headers_len: ffi::c_ulong, headers: &'static [[*const ffi::c_char; 2]], body_len: ffi::c_ulong, body: *const u8, } const HEADERS: &'static [[*const ffi::c_char; 2]] = &[[c"Content-Type".as_ptr(), c"text/html".as_ptr()]]; #[unsafe(no_mangle)] extern "C" fn cgi_handle(req: *const ModuleRequest) -> *const ModuleResponse { let mut ret = String::new(); // unwrap is bad here let reqref = unsafe { req.as_ref() }.unwrap(); let mut stats_path = None; for idx in 0..reqref.headers_len { let kvarr = reqref.headers[idx as usize]; let k = unsafe { CStr::from_ptr(kvarr[0]) }.to_string_lossy(); let v = unsafe { CStr::from_ptr(kvarr[1]) }.to_string_lossy(); match k.as_ref() { "CORGI_STATS_DB" => stats_path = Some(v.into_owned()), _ => (), } } let db = if let Some(path) = stats_path { Connection::open(path).unwrap() } else { return make_error(500, c"could not open stats database"); }; let now = OffsetDateTime::now_utc(); let fifteen_ago = now - Duration::minutes(15); let query = "SELECT count(requests.id) AS request_count, agents.agent FROM requests \ INNER JOIN agents ON requests.agent_id = agents.id \ WHERE requests.timestamp > ?1 \ GROUP BY requests.agent_id;"; let mut prepared = db.prepare(query).unwrap(); let mut agents: Vec<(usize, String)> = prepared .query_map(params![fifteen_ago], |row| Ok((row.get(0)?, row.get(1)?))) .unwrap() .map(|r| r.unwrap()) .collect(); agents.sort_by(|a, b| a.0.cmp(&b.0).reverse()); ret.push_str("
In the last fifteen minutes:");
ret.push_str("total | req/m | agent\n");
for (count, agent) in &agents {
ret.push_str(&format!(
"{count:<5} | {:<5.1} | {agent}\n",
*count as f32 / 15.0
));
}
ret.push_str("