headless_lms_models/
page_visit_datum_daily_visit_hashing_keys.rs1use chrono::NaiveDate;
2use headless_lms_utils::page_visit_hasher::hash_anonymous_identifier;
3
4use crate::prelude::*;
5
6pub struct GenerateAnonymousIdentifierInput {
7 pub course_id: Uuid,
8 pub user_agent: String,
9 pub ip_address: String,
10}
11pub async fn generate_anonymous_identifier(
12 conn: &mut PgConnection,
13 input: GenerateAnonymousIdentifierInput,
14) -> ModelResult<String> {
15 let key_for_the_day = get_key_for_the_day(conn).await?;
16 let hash = hash_anonymous_identifier(
17 input.course_id,
18 key_for_the_day,
19 input.user_agent,
20 input.ip_address,
21 )
22 .map_err(|e| ModelError::new(ModelErrorType::Generic, e.to_string(), Some(e)))?;
23 Ok(hash)
24}
25
26pub async fn get_key_for_the_day(conn: &mut PgConnection) -> ModelResult<Vec<u8>> {
27 let now = Utc::now();
28 let valid_for_date = now.date_naive();
29 let res = try_get_key_for_the_day_internal(conn, valid_for_date).await?;
30 match res {
31 Some(hashing_key) => Ok(hashing_key),
32 None => {
33 try_insert_key_for_the_day_internal(conn, valid_for_date).await?;
34 let second_try = try_get_key_for_the_day_internal(conn, valid_for_date).await?;
35 match second_try {
36 Some(hashing_key) => Ok(hashing_key),
37 None => Err(ModelError::new(
38 ModelErrorType::Generic,
39 "Failed to get hashing key for the day".to_string(),
40 None,
41 )),
42 }
43 }
44 }
45}
46
47async fn try_get_key_for_the_day_internal(
48 conn: &mut PgConnection,
49 valid_for_date: NaiveDate,
50) -> ModelResult<Option<Vec<u8>>> {
51 let res = sqlx::query!(
52 "
53SELECT hashing_key FROM page_visit_datum_daily_visit_hashing_keys
54WHERE valid_for_date = $1
55 ",
56 valid_for_date
57 )
58 .fetch_optional(conn)
59 .await?;
60 Ok(res.map(|r| r.hashing_key))
61}
62
63async fn try_insert_key_for_the_day_internal(
64 conn: &mut PgConnection,
65 valid_for_date: NaiveDate,
66) -> ModelResult<()> {
67 sqlx::query!(
68 "
69INSERT INTO page_visit_datum_daily_visit_hashing_keys(valid_for_date)
70VALUES ($1)
71ON CONFLICT (valid_for_date) DO NOTHING
72 ",
73 valid_for_date
74 )
75 .execute(&mut *conn)
76 .await?;
77
78 sqlx::query!(
80 "
81DELETE FROM page_visit_datum_daily_visit_hashing_keys WHERE valid_for_date < $1
82 ",
83 valid_for_date
84 )
85 .execute(&mut *conn)
86 .await?;
87 Ok(())
88}