Built for people who think better out loud.
at main 141 lines 3.3 kB view raw
1use sqlx::Row; 2use sqlx::PgPool; 3use uuid::Uuid; 4 5use crate::domain::CurrentUser; 6 7pub async fn load_current_user_by_session( 8 db_pool: &PgPool, 9 session_id: Uuid, 10) -> Result<Option<CurrentUser>, sqlx::Error> { 11 let record = sqlx::query( 12 r#" 13 SELECT users.did, users.handle 14 FROM sessions 15 JOIN users ON users.did = sessions.user_did 16 WHERE sessions.id = $1 17 AND sessions.expires_at > NOW() 18 "#, 19 ) 20 .bind(session_id) 21 .fetch_optional(db_pool) 22 .await?; 23 24 let Some(record) = record else { 25 return Ok(None); 26 }; 27 28 let did: String = record.try_get("did")?; 29 let handle: Option<String> = record.try_get("handle")?; 30 31 Ok(Some(CurrentUser { did, handle })) 32} 33 34/// Inserts or updates the user's handle. 35pub async fn upsert_user( 36 db_pool: &PgPool, 37 did: &str, 38 handle: Option<&str>, 39) -> Result<(), sqlx::Error> { 40 sqlx::query( 41 r#" 42 INSERT INTO users (did, handle) 43 VALUES ($1, $2) 44 ON CONFLICT (did) 45 DO UPDATE SET handle = EXCLUDED.handle 46 "#, 47 ) 48 .bind(did) 49 .bind(handle) 50 .execute(db_pool) 51 .await?; 52 53 Ok(()) 54} 55 56/// Stores OAuth tokens for a user. 57pub async fn store_tokens( 58 db_pool: &PgPool, 59 user_did: &str, 60 access_token: &str, 61 refresh_token: Option<&str>, 62 token_type: &str, 63 scopes: &str, 64 expires_in: u32, 65 issuer: &str, 66 dpop_private_key: &str, 67) -> Result<(), sqlx::Error> { 68 let expires_at = chrono::Utc::now() + chrono::Duration::seconds(i64::from(expires_in)); 69 sqlx::query( 70 r#" 71 INSERT INTO oauth_tokens ( 72 user_did, 73 access_token, 74 refresh_token, 75 token_type, 76 scopes, 77 expires_at, 78 issuer, 79 dpop_private_key, 80 updated_at 81 ) 82 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW()) 83 ON CONFLICT (user_did) 84 DO UPDATE SET 85 access_token = EXCLUDED.access_token, 86 refresh_token = EXCLUDED.refresh_token, 87 token_type = EXCLUDED.token_type, 88 scopes = EXCLUDED.scopes, 89 expires_at = EXCLUDED.expires_at, 90 issuer = EXCLUDED.issuer, 91 dpop_private_key = EXCLUDED.dpop_private_key, 92 updated_at = NOW() 93 "#, 94 ) 95 .bind(user_did) 96 .bind(access_token) 97 .bind(refresh_token) 98 .bind(token_type) 99 .bind(scopes) 100 .bind(expires_at) 101 .bind(issuer) 102 .bind(dpop_private_key) 103 .execute(db_pool) 104 .await?; 105 106 Ok(()) 107} 108 109/// Creates a new session for a user. 110pub async fn create_session( 111 db_pool: &PgPool, 112 session_id: Uuid, 113 user_did: &str, 114 expires_at: chrono::DateTime<chrono::Utc>, 115) -> Result<(), sqlx::Error> { 116 sqlx::query( 117 r#" 118 INSERT INTO sessions (id, user_did, expires_at) 119 VALUES ($1, $2, $3) 120 "#, 121 ) 122 .bind(session_id) 123 .bind(user_did) 124 .bind(expires_at) 125 .execute(db_pool) 126 .await?; 127 Ok(()) 128} 129 130/// Deletes a session by id. 131pub async fn delete_session(db_pool: &PgPool, session_id: Uuid) -> Result<(), sqlx::Error> { 132 sqlx::query( 133 r#" 134 DELETE FROM sessions WHERE id = $1 135 "#, 136 ) 137 .bind(session_id) 138 .execute(db_pool) 139 .await?; 140 Ok(()) 141}