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                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}