sbol_db_sqlite/repo/
tokens.rs1use async_trait::async_trait;
7use chrono::Utc;
8use sbol_db_core::{DomainError, UserId};
9use sbol_db_storage::TokenStore;
10use sqlx::Row;
11
12use crate::pool::db_err;
13use crate::SqlitePool;
14
15#[derive(Clone)]
16pub struct SqliteTokenStore {
17 pool: SqlitePool,
18}
19
20impl SqliteTokenStore {
21 pub fn new(pool: SqlitePool) -> Self {
22 Self { pool }
23 }
24}
25
26#[async_trait]
27impl TokenStore for SqliteTokenStore {
28 async fn issue(&self, token_hash: &str, user_id: UserId) -> Result<(), DomainError> {
29 sqlx::query("INSERT INTO sbh_api_token (token_hash, user_id, created_at) VALUES (?, ?, ?)")
30 .bind(token_hash)
31 .bind(user_id.as_uuid().to_string())
32 .bind(Utc::now())
33 .execute(&self.pool)
34 .await
35 .map_err(db_err)?;
36 Ok(())
37 }
38
39 async fn resolve(&self, token_hash: &str) -> Result<Option<UserId>, DomainError> {
40 let row = sqlx::query("SELECT user_id FROM sbh_api_token WHERE token_hash = ?")
41 .bind(token_hash)
42 .fetch_optional(&self.pool)
43 .await
44 .map_err(db_err)?;
45 match row {
46 Some(r) => {
47 let user_id: String = r.try_get("user_id").map_err(db_err)?;
48 Ok(Some(UserId(
49 uuid::Uuid::parse_str(&user_id).map_err(db_err)?,
50 )))
51 }
52 None => Ok(None),
53 }
54 }
55
56 async fn revoke(&self, token_hash: &str) -> Result<bool, DomainError> {
57 let res = sqlx::query("DELETE FROM sbh_api_token WHERE token_hash = ?")
58 .bind(token_hash)
59 .execute(&self.pool)
60 .await
61 .map_err(db_err)?;
62 Ok(res.rows_affected() == 1)
63 }
64}