lmjtfy.git / apps / lmjtfy / src / events.rs
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                city: cf.city().unwrap_or_default(),
22                postcode: cf.postal_code().unwrap_or_default(),
23                timezone: cf.timezone_name(),
24                latitude: coordinates.map(|(latitude, _)| f64::from(latitude)),
25                longitude: coordinates.map(|(_, longitude)| f64::from(longitude)),
26                asn: cf.asn(),
27                network: cf.as_organization().unwrap_or_default(),
28                colo: cf.colo(),
29                protocol: cf.http_protocol(),
30                tls: cf.tls_version(),
31            }
32        }
33        None => Origin { ip, ..Origin::default() },
34    };
35    Event::from(request.method(), request.uri(), headers, crate::browser(headers), origin)
36}
37
38/// Keeps the event. A failure loses that one row and nothing else.
39pub async fn record(env: &Env, event: Event) {
40    crate::archive::event(env, event).await;
41}
42
43/// A name for how an ask ended.
44pub fn ending(ending: Option<&crate::view::Ending>) -> &'static str {
45    use crate::view::Ending;
46    match ending {
47        None => "",
48        Some(Ending::Empty) => "empty",
49        Some(Ending::NotAQuestion { .. }) => "refused",
50        Some(Ending::Answered) => "answered",
51        Some(Ending::Slow) => "slow",
52        Some(Ending::NoQuestion) => "no-question",
53        Some(Ending::Failed { .. }) => "failed",
54        Some(Ending::Offline) => "offline",
55        Some(Ending::Spent { .. }) => "spent",
56        Some(Ending::NotKept { .. }) => "not-kept",
57    }
58}