Skip to main content

mas_storage_pg/user/
terms.rs

1// Copyright 2025, 2026 Element Creations Ltd.
2// Copyright 2024, 2025 New Vector Ltd.
3// Copyright 2024 The Matrix.org Foundation C.I.C.
4//
5// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6// Please see LICENSE files in the repository root for full details.
7
8use async_trait::async_trait;
9use mas_data_model::{Clock, UlidExt as _, User};
10use mas_storage::user::UserTermsRepository;
11use rand::Rng;
12use sqlx::PgConnection;
13use ulid::Ulid;
14use url::Url;
15use uuid::Uuid;
16
17use crate::{DatabaseError, tracing::ExecuteExt};
18
19/// An implementation of [`UserTermsRepository`] for a PostgreSQL connection
20pub struct PgUserTermsRepository<'c> {
21    conn: &'c mut PgConnection,
22}
23
24impl<'c> PgUserTermsRepository<'c> {
25    /// Create a new [`PgUserTermsRepository`] from an active PostgreSQL
26    /// connection
27    pub fn new(conn: &'c mut PgConnection) -> Self {
28        Self { conn }
29    }
30}
31
32#[async_trait]
33impl UserTermsRepository for PgUserTermsRepository<'_> {
34    type Error = DatabaseError;
35
36    #[tracing::instrument(
37        name = "db.user_terms.accept_terms",
38        skip_all,
39        fields(
40            db.query.text,
41            %user.id,
42            user_terms.id,
43            %user_terms.url = terms_url.as_str(),
44        ),
45    )]
46    async fn accept_terms(
47        &mut self,
48        rng: &mut (dyn Rng + Send),
49        clock: &dyn Clock,
50        user: &User,
51        terms_url: Url,
52    ) -> Result<(), Self::Error> {
53        let created_at = clock.now();
54        let id = Ulid::from_datetime_with_rng(created_at, rng);
55        tracing::Span::current().record("user_terms.id", tracing::field::display(id));
56
57        sqlx::query!(
58            r#"
59            INSERT INTO user_terms (user_terms_id, user_id, terms_url, created_at)
60            VALUES ($1, $2, $3, $4)
61            ON CONFLICT (user_id, terms_url) DO NOTHING
62            "#,
63            Uuid::from(id),
64            Uuid::from(user.id),
65            terms_url.as_str(),
66            created_at,
67        )
68        .traced()
69        .execute(&mut *self.conn)
70        .await?;
71
72        Ok(())
73    }
74}