use rusqlite::{Connection, params}; use smalldog::{ModuleRequest, ModuleResponse, Request, Response}; use time::{Duration, OffsetDateTime}; #[unsafe(no_mangle)] extern "C" fn cgi_handle(req: *const ModuleRequest) -> *const ModuleResponse { let mut response = Response::new(); let mut body = String::new(); let request = Request::from_mod_request(req); let db = if let Some(path) = request.header("CORGI_STATS_DB") { Connection::open(path).unwrap() } else { return make_error(500, "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()); body.push_str("

In the last fifteen minutes:

");
	body.push_str("total | req/m | agent\n");
	for (count, agent) in &agents {
		body.push_str(&format!(
			"{count:<5} | {:<5.1} | {agent}\n",
			*count as f32 / 15.0
		));
	}
	body.push_str("

"); response.body(body.into_bytes()); response.into_mod_response(200) } fn make_error>(code: u16, msg: S) -> *const ModuleResponse { let mut response = Response::new(); response.header(c"Content-Length", c"text/html"); response.body(msg.as_ref().as_bytes().to_vec()); response.into_mod_response(code) } #[unsafe(no_mangle)] extern "C" fn cgi_cleanup(response: *const ModuleResponse) { Response::cleanup(response); }