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