shared/domain/session/
repository.rs1use std::sync::Arc;
2
3use chrono::{Duration, Utc};
4
5use crate::error::{CoreError, InternalError, Result, TokenErrorType};
6
7use super::model::Session;
8use super::ports::database::DatabaseAdapter;
9use super::ports::query::QueryBuilder;
10
11#[derive(Clone)]
12pub struct SessionRepository {
13 adapter: Arc<dyn DatabaseAdapter<Session>>,
14}
15
16impl SessionRepository {
17 pub fn new(adapter: Arc<dyn DatabaseAdapter<Session>>) -> Self {
18 Self { adapter }
19 }
20}
21
22impl SessionRepository {
23 #[tracing::instrument(name = "db.account.insert", skip(self, session))]
24 pub async fn insert(&self, session: Session) -> Result<()> {
25 match self.adapter.insert(session).await {
26 Ok(_id) => Ok(()),
27 Err(err) => {
28 tracing::error!("Failed to insert account to database - {err}");
29 Err(CoreError::Internal(InternalError::Database(
30 err.to_string(),
31 )))
32 }
33 }
34 }
35
36 #[tracing::instrument(name = "db.account.find", skip(self, token))]
37 pub async fn find(&self, token: &str) -> Result<Session> {
38 let filter = QueryBuilder::default().eq("token", token);
39
40 match self.adapter.find_one(filter).await {
41 Ok(Some(session)) => Ok(session),
42 Ok(None) => Err(CoreError::Unauthenticated(
43 crate::error::AuthError::TokenInvalid {
44 token_type: TokenErrorType::SessionToken,
45 },
46 )),
47 Err(err) => {
48 tracing::error!(error_code = "InternalError::Database", error = %err, "Database query failed");
49 Err(err)
50 }
51 }
52 }
53
54 #[tracing::instrument(name = "db.account.extend_timeout", skip(self, id))]
55 pub async fn extend_timeout(&self, id: &str) -> Result<Session> {
56 let filter = QueryBuilder::default().eq("id", id);
57 let update = QueryBuilder::default()
58 .set("usedAt", Utc::now())
59 .set("expiresAt", Utc::now() + Duration::hours(24));
60
61 match self.adapter.find_one_and_update(filter, update).await {
62 Ok(Some(session)) => Ok(session),
63 Ok(None) => Err(CoreError::NotFound(crate::error::ResourceKind::Token {
64 token_type: TokenErrorType::SessionToken,
65 })),
66 Err(err) => {
67 tracing::error!(error_code = "InternalError::Database", error = %err, "Database query failed");
68 Err(err)
69 }
70 }
71 }
72
73 #[tracing::instrument(name = "db.account.invalidate", skip(self, token))]
74 pub async fn invalidate(&self, token: &str) -> Result<()> {
75 let filter = QueryBuilder::default().eq("token", token);
76
77 self.adapter.delete_one(filter).await
78 }
79
80 #[tracing::instrument(name = "db.account.revoke", skip(self), fields(user.id = user_id))]
81 pub async fn revoke(&self, user_id: &str) -> Result<()> {
82 let filter = QueryBuilder::default().eq("userId", user_id);
83
84 self.adapter.delete_many(filter).await.map_err(|err| {
85 tracing::error!(error_code = "InternalError::Database", error = %err, "Database query failed");
86 CoreError::Internal(InternalError::Hashing)
87 })
88 }
89}