1//! What happened on the site, kept: the Worker makes an `archive::Event` of 2//! every request worth keeping and hands it to the archive, which writes it 3//! as a row of `events`. The event itself, and what it reads from a request, 4//! is `packages/archive/src/event.rs`; here is only what needs the Worker: 5//! Cloudflare's account of where the request came from, and the sending. 6 7pub use ::archive::event::{Event, Origin}; 8use worker::{Env, HttpRequest}; 9 10/// The request as an event, before it is known what came of it. 11pub fn of(request: &HttpRequest) -> Event { 12 let headers = request.headers(); 13 let ip = headers.get("cf-connecting-ip").and_then(|value| value.to_str().ok()).unwrap_or_default().to_owned(); 14 let origin = match request.extensions().get::<worker::Cf>() { 15 Some(cf) => { 16 let coordinates = cf.coordinates(); 17 Origin { 18 ip, 19 country: cf.country().unwrap_or_default(), 20 region: cf.region().unwrap_or_default(), 21 region_code: cf.region_code().unwrap_or_default(), 22 continent: cf.continent().unwrap_or_default(), 23 metro: cf.metro_code().unwrap_or_default(), 24 bot: cf.verified_bot_category().unwrap_or_default(), 25 city: cf.city().unwrap_or_default(), 26 postcode: cf.postal_code().unwrap_or_default(), 27 timezone: cf.timezone_name(), 28 latitude: coordinates.map(|(latitude, _)| f64::from(latitude)), 29 longitude: coordinates.map(|(_, longitude)| f64::from(longitude)), 30 asn: cf.asn(), 31 network: cf.as_organization().unwrap_or_default(), 32 colo: cf.colo(), 33 protocol: cf.http_protocol(), 34 tls: cf.tls_version(), 35 } 36 } 37 None => Origin { ip, ..Origin::default() }, 38 }; 39 Event::from(request.method(), request.uri(), headers, crate::browser(headers), origin) 40} 41 42/// Keeps the event. A failure loses that one row and nothing else. 43pub async fn record(env: &Env, event: Event) { 44 crate::archive::event(env, event).await; 45} 46 47/// A name for how an ask ended. 48pub fn ending(ending: Option<&crate::view::Ending>) -> &'static str { 49 use crate::view::Ending; 50 match ending { 51 None => "", 52 Some(Ending::Empty) => "empty", 53 Some(Ending::NotAQuestion { .. }) => "refused", 54 Some(Ending::Answered) => "answered", 55 Some(Ending::Slow) => "slow", 56 Some(Ending::NoQuestion) => "no-question", 57 Some(Ending::Failed { .. }) => "failed", 58 Some(Ending::Offline) => "offline", 59 Some(Ending::Spent { .. }) => "spent", 60 Some(Ending::NotKept { .. }) => "not-kept", 61 } 62}