mas_handlers/activity_tracker/
bound.rs1use std::{convert::Infallible, net::IpAddr};
9
10use axum::extract::FromRequestParts;
11use mas_data_model::{
12 BrowserSession, Clock, CompatSession, Session, personal::session::PersonalSession,
13};
14
15use crate::{ClientIp, activity_tracker::ActivityTracker};
16
17#[derive(Clone)]
19pub struct Bound {
20 tracker: ActivityTracker,
21 ip: Option<IpAddr>,
22}
23
24impl Bound {
25 #[must_use]
27 pub fn new(tracker: ActivityTracker, ip: Option<IpAddr>) -> Self {
28 Self { tracker, ip }
29 }
30
31 #[must_use]
33 pub fn ip(&self) -> Option<IpAddr> {
34 self.ip
35 }
36
37 pub async fn record_oauth2_session(&self, clock: &dyn Clock, session: &Session) {
39 self.tracker
40 .record_oauth2_session(clock, session, self.ip)
41 .await;
42 }
43
44 pub async fn record_personal_session(&self, clock: &dyn Clock, session: &PersonalSession) {
46 self.tracker
47 .record_personal_session(clock, session, self.ip)
48 .await;
49 }
50
51 pub async fn record_compat_session(&self, clock: &dyn Clock, session: &CompatSession) {
53 self.tracker
54 .record_compat_session(clock, session, self.ip)
55 .await;
56 }
57
58 pub async fn record_browser_session(&self, clock: &dyn Clock, session: &BrowserSession) {
60 self.tracker
61 .record_browser_session(clock, session, self.ip)
62 .await;
63 }
64}
65
66impl<S> FromRequestParts<S> for Bound
71where
72 S: Send + Sync,
73 ActivityTracker: FromRequestParts<S, Rejection = Infallible>,
74{
75 type Rejection = Infallible;
76
77 async fn from_request_parts(
78 parts: &mut axum::http::request::Parts,
79 state: &S,
80 ) -> Result<Self, Self::Rejection> {
81 let ip = parts.extensions.get::<ClientIp>().and_then(|ip| ip.0);
82 let tracker = match ActivityTracker::from_request_parts(parts, state).await {
83 Ok(tracker) => tracker,
84 Err(infallible) => match infallible {},
85 };
86 Ok(tracker.bind(ip))
87 }
88}