Our Personal Data Server from scratch!
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor(api): migrate all endpoints to repos accessor pattern

authored by did:plc:mb5to35neicxt4gemstoro… and committed by tangled.org e454e99b 19b0ea19

+520 -615
+4 -4
crates/tranquil-api/src/actor/preferences.rs
··· 34 34 } 35 35 pub async fn get_preferences(State(state): State<AppState>, auth: Auth<Permissive>) -> Response { 36 36 let has_full_access = auth.permissions().has_full_access(); 37 - let user_id: uuid::Uuid = match state.user_repo.get_id_by_did(&auth.did).await { 37 + let user_id: uuid::Uuid = match state.repos.user.get_id_by_did(&auth.did).await { 38 38 Ok(Some(id)) => id, 39 39 _ => { 40 40 return ApiError::InternalError(Some("User not found".into())).into_response(); 41 41 } 42 42 }; 43 - let prefs = match state.infra_repo.get_account_preferences(user_id).await { 43 + let prefs = match state.repos.infra.get_account_preferences(user_id).await { 44 44 Ok(rows) => rows, 45 45 Err(_) => { 46 46 return ApiError::InternalError(Some("Failed to fetch preferences".into())) ··· 93 93 Json(input): Json<PutPreferencesInput>, 94 94 ) -> Response { 95 95 let has_full_access = auth.permissions().has_full_access(); 96 - let user_id: uuid::Uuid = match state.user_repo.get_id_by_did(&auth.did).await { 96 + let user_id: uuid::Uuid = match state.repos.user.get_id_by_did(&auth.did).await { 97 97 Ok(Some(id)) => id, 98 98 _ => { 99 99 return ApiError::InternalError(Some("User not found".into())).into_response(); ··· 188 188 .collect(); 189 189 190 190 if state 191 - .infra_repo 191 + .repos.infra 192 192 .replace_namespace_preferences(user_id, APP_BSKY_NAMESPACE, prefs_to_save) 193 193 .await 194 194 .is_err()
+2 -2
crates/tranquil-api/src/admin/account/delete.rs
··· 19 19 ) -> Result<Json<EmptyResponse>, ApiError> { 20 20 let did = &input.did; 21 21 let (user_id, handle) = state 22 - .user_repo 22 + .repos.user 23 23 .get_id_and_handle_by_did(did) 24 24 .await 25 25 .log_db_err("in delete_account")? ··· 27 27 .map(|row| (row.id, row.handle))?; 28 28 29 29 state 30 - .user_repo 30 + .repos.user 31 31 .admin_delete_account_complete(user_id, did) 32 32 .await 33 33 .log_db_err("deleting account")?;
+2 -2
crates/tranquil-api/src/admin/account/email.rs
··· 31 31 return Err(ApiError::InvalidRequest("content is required".into())); 32 32 } 33 33 let user = state 34 - .user_repo 34 + .repos.user 35 35 .get_by_did(&input.recipient_did) 36 36 .await 37 37 .log_db_err("in send_email")? ··· 45 45 .clone() 46 46 .unwrap_or_else(|| format!("Message from {}", hostname)); 47 47 let result = state 48 - .infra_repo 48 + .repos.infra 49 49 .enqueue_comms( 50 50 Some(user_id), 51 51 tranquil_db_traits::CommsChannel::Email,
+10 -10
crates/tranquil-api/src/admin/account/info.rs
··· 69 69 Query(params): Query<GetAccountInfoParams>, 70 70 ) -> Result<Json<AccountInfo>, ApiError> { 71 71 let account = state 72 - .infra_repo 72 + .repos.infra 73 73 .get_admin_account_info_by_did(&params.did) 74 74 .await 75 75 .log_db_err("in get_account_info")? ··· 98 98 99 99 async fn get_invited_by(state: &AppState, user_id: uuid::Uuid) -> Option<InviteCodeInfo> { 100 100 let code = state 101 - .infra_repo 101 + .repos.infra 102 102 .get_invite_code_used_by_user(user_id) 103 103 .await 104 104 .ok()??; ··· 111 111 user_id: uuid::Uuid, 112 112 ) -> Option<Vec<InviteCodeInfo>> { 113 113 let invite_codes = state 114 - .infra_repo 114 + .repos.infra 115 115 .get_invites_created_by_user(user_id) 116 116 .await 117 117 .ok()?; ··· 123 123 let code_strings: Vec<String> = invite_codes.iter().map(|ic| ic.code.clone()).collect(); 124 124 125 125 let uses = state 126 - .infra_repo 126 + .repos.infra 127 127 .get_invite_code_uses_batch(&code_strings) 128 128 .await 129 129 .ok()?; ··· 154 154 } 155 155 156 156 async fn get_invite_code_info(state: &AppState, code: &str) -> Option<InviteCodeInfo> { 157 - let info = state.infra_repo.get_invite_code_info(code).await.ok()??; 157 + let info = state.repos.infra.get_invite_code_info(code).await.ok()??; 158 158 159 159 let uses = state 160 - .infra_repo 160 + .repos.infra 161 161 .get_invite_code_uses(code) 162 162 .await 163 163 .ok() ··· 197 197 198 198 let dids_typed: Vec<Did> = dids.iter().filter_map(|d| d.parse().ok()).collect(); 199 199 let accounts = state 200 - .infra_repo 200 + .repos.infra 201 201 .get_admin_account_infos_by_dids(&dids_typed) 202 202 .await 203 203 .log_db_err("fetching account infos")?; ··· 205 205 let user_ids: Vec<uuid::Uuid> = accounts.iter().map(|u| u.id).collect(); 206 206 207 207 let all_invite_codes = state 208 - .infra_repo 208 + .repos.infra 209 209 .get_invite_codes_by_users(&user_ids) 210 210 .await 211 211 .unwrap_or_default(); ··· 217 217 218 218 let all_invite_uses = if !all_codes.is_empty() { 219 219 state 220 - .infra_repo 220 + .repos.infra 221 221 .get_invite_code_uses_batch(&all_codes) 222 222 .await 223 223 .unwrap_or_default() ··· 226 226 }; 227 227 228 228 let invited_by_map: HashMap<uuid::Uuid, String> = state 229 - .infra_repo 229 + .repos.infra 230 230 .get_invite_code_uses_by_users(&user_ids) 231 231 .await 232 232 .unwrap_or_default()
+1 -1
crates/tranquil-api/src/admin/account/search.rs
··· 55 55 let handle_filter = params.handle.as_deref().map(|h| format!("%{}%", h)); 56 56 let cursor_did: Option<Did> = params.cursor.as_ref().and_then(|c| c.parse().ok()); 57 57 let rows = state 58 - .user_repo 58 + .repos.user 59 59 .search_accounts( 60 60 cursor_did.as_ref(), 61 61 email_filter.as_deref(),
+7 -10
crates/tranquil-api/src/admin/account/update.rs
··· 30 30 .map_err(|_| ApiError::InvalidDid("Invalid DID format".into()))?; 31 31 32 32 match state 33 - .user_repo 33 + .repos.user 34 34 .admin_update_email(&account_did, email) 35 35 .await 36 36 { ··· 71 71 } else { 72 72 input_handle.to_string() 73 73 }; 74 - let old_handle = state.user_repo.get_handle_by_did(did).await.ok().flatten(); 74 + let old_handle = state.repos.user.get_handle_by_did(did).await.ok().flatten(); 75 75 let user_id = state 76 - .user_repo 76 + .repos.user 77 77 .get_id_by_did(did) 78 78 .await 79 79 .ok() ··· 81 81 .ok_or(ApiError::AccountNotFound)?; 82 82 let handle_for_check: Handle = handle.parse().map_err(|_| ApiError::InvalidHandle(None))?; 83 83 if let Ok(true) = state 84 - .user_repo 84 + .repos.user 85 85 .check_handle_exists(&handle_for_check, user_id) 86 86 .await 87 87 { 88 88 return Err(ApiError::HandleTaken); 89 89 } 90 90 match state 91 - .user_repo 91 + .repos.user 92 92 .admin_update_handle(did, &handle_for_check) 93 93 .await 94 94 { ··· 146 146 if password.is_empty() { 147 147 return Err(ApiError::InvalidRequest("password is required".into())); 148 148 } 149 - let password_hash = bcrypt::hash(password, bcrypt::DEFAULT_COST).map_err(|e| { 150 - error!("Failed to hash password: {:?}", e); 151 - ApiError::InternalError(None) 152 - })?; 149 + let password_hash = crate::common::hash_or_internal_error(password)?; 153 150 154 151 match state 155 - .user_repo 152 + .repos.user 156 153 .admin_update_password(did, &password_hash) 157 154 .await 158 155 {
+15 -15
crates/tranquil-api/src/admin/config.rs
··· 53 53 ]; 54 54 55 55 let rows = state 56 - .infra_repo 56 + .repos.infra 57 57 .get_server_configs(keys) 58 58 .await 59 59 .log_db_err("fetching server config")?; ··· 86 86 )); 87 87 } 88 88 state 89 - .infra_repo 89 + .repos.infra 90 90 .upsert_server_config("server_name", trimmed) 91 91 .await 92 92 .log_db_err("upserting server_name")?; ··· 95 95 if let Some(ref color) = req.primary_color { 96 96 if color.is_empty() { 97 97 state 98 - .infra_repo 98 + .repos.infra 99 99 .delete_server_config("primary_color") 100 100 .await 101 101 .log_db_err("deleting primary_color")?; 102 102 } else if is_valid_hex_color(color) { 103 103 state 104 - .infra_repo 104 + .repos.infra 105 105 .upsert_server_config("primary_color", color) 106 106 .await 107 107 .log_db_err("upserting primary_color")?; ··· 115 115 if let Some(ref color) = req.primary_color_dark { 116 116 if color.is_empty() { 117 117 state 118 - .infra_repo 118 + .repos.infra 119 119 .delete_server_config("primary_color_dark") 120 120 .await 121 121 .log_db_err("deleting primary_color_dark")?; 122 122 } else if is_valid_hex_color(color) { 123 123 state 124 - .infra_repo 124 + .repos.infra 125 125 .upsert_server_config("primary_color_dark", color) 126 126 .await 127 127 .log_db_err("upserting primary_color_dark")?; ··· 135 135 if let Some(ref color) = req.secondary_color { 136 136 if color.is_empty() { 137 137 state 138 - .infra_repo 138 + .repos.infra 139 139 .delete_server_config("secondary_color") 140 140 .await 141 141 .log_db_err("deleting secondary_color")?; 142 142 } else if is_valid_hex_color(color) { 143 143 state 144 - .infra_repo 144 + .repos.infra 145 145 .upsert_server_config("secondary_color", color) 146 146 .await 147 147 .log_db_err("upserting secondary_color")?; ··· 155 155 if let Some(ref color) = req.secondary_color_dark { 156 156 if color.is_empty() { 157 157 state 158 - .infra_repo 158 + .repos.infra 159 159 .delete_server_config("secondary_color_dark") 160 160 .await 161 161 .log_db_err("deleting secondary_color_dark")?; 162 162 } else if is_valid_hex_color(color) { 163 163 state 164 - .infra_repo 164 + .repos.infra 165 165 .upsert_server_config("secondary_color_dark", color) 166 166 .await 167 167 .log_db_err("upserting secondary_color_dark")?; ··· 174 174 175 175 if let Some(ref logo_cid) = req.logo_cid { 176 176 let old_logo_cid = state 177 - .infra_repo 177 + .repos.infra 178 178 .get_server_config("logo_cid") 179 179 .await 180 180 .ok() ··· 190 190 match CidLink::new(old_cid_str) { 191 191 Ok(old_cid) => { 192 192 if let Ok(Some(storage_key)) = 193 - state.infra_repo.get_blob_storage_key_by_cid(&old_cid).await 193 + state.repos.infra.get_blob_storage_key_by_cid(&old_cid).await 194 194 { 195 195 if let Err(e) = state.blob_store.delete(&storage_key).await { 196 196 error!("Failed to delete old logo blob from storage: {:?}", e); 197 197 } 198 - if let Err(e) = state.infra_repo.delete_blob_by_cid(&old_cid).await { 198 + if let Err(e) = state.repos.infra.delete_blob_by_cid(&old_cid).await { 199 199 error!("Failed to delete old logo blob record: {:?}", e); 200 200 } 201 201 } ··· 211 211 212 212 if logo_cid.is_empty() { 213 213 state 214 - .infra_repo 214 + .repos.infra 215 215 .delete_server_config("logo_cid") 216 216 .await 217 217 .log_db_err("deleting logo_cid")?; 218 218 } else { 219 219 state 220 - .infra_repo 220 + .repos.infra 221 221 .upsert_server_config("logo_cid", logo_cid) 222 222 .await 223 223 .log_db_err("upserting logo_cid")?;
+7 -7
crates/tranquil-api/src/admin/invite.rs
··· 24 24 Json(input): Json<DisableInviteCodesInput>, 25 25 ) -> Result<Json<EmptyResponse>, ApiError> { 26 26 if let Some(codes) = &input.codes 27 - && let Err(e) = state.infra_repo.disable_invite_codes_by_code(codes).await 27 + && let Err(e) = state.repos.infra.disable_invite_codes_by_code(codes).await 28 28 { 29 29 error!("DB error disabling invite codes: {:?}", e); 30 30 } ··· 32 32 let accounts_typed: Vec<tranquil_types::Did> = 33 33 accounts.iter().filter_map(|a| a.parse().ok()).collect(); 34 34 if let Err(e) = state 35 - .infra_repo 35 + .repos.infra 36 36 .disable_invite_codes_by_account(&accounts_typed) 37 37 .await 38 38 { ··· 87 87 }; 88 88 89 89 let codes_rows = state 90 - .infra_repo 90 + .repos.infra 91 91 .list_invite_codes(params.cursor.as_deref(), limit, sort_order) 92 92 .await 93 93 .log_db_err("fetching invite codes")?; ··· 96 96 let code_strings: Vec<String> = codes_rows.iter().map(|r| r.code.clone()).collect(); 97 97 98 98 let creator_dids: std::collections::HashMap<uuid::Uuid, tranquil_types::Did> = state 99 - .infra_repo 99 + .repos.infra 100 100 .get_user_dids_by_ids(&user_ids) 101 101 .await 102 102 .unwrap_or_default() ··· 108 108 } else { 109 109 common::group_invite_uses_by_code( 110 110 state 111 - .infra_repo 111 + .repos.infra 112 112 .get_invite_code_uses_batch(&code_strings) 113 113 .await 114 114 .unwrap_or_default(), ··· 168 168 .map_err(|_| ApiError::InvalidDid("Invalid DID format".into()))?; 169 169 170 170 match state 171 - .user_repo 171 + .repos.user 172 172 .set_invites_disabled(&account_did, true) 173 173 .await 174 174 { ··· 200 200 .map_err(|_| ApiError::InvalidDid("Invalid DID format".into()))?; 201 201 202 202 match state 203 - .user_repo 203 + .repos.user 204 204 .set_invites_disabled(&account_did, false) 205 205 .await 206 206 {
+4 -4
crates/tranquil-api/src/admin/server_stats.rs
··· 17 17 State(state): State<AppState>, 18 18 _auth: Auth<Admin>, 19 19 ) -> Result<Json<ServerStatsOutput>, ApiError> { 20 - let user_count = state.user_repo.count_users().await.unwrap_or(0); 21 - let repo_count = state.repo_repo.count_repos().await.unwrap_or(0); 22 - let record_count = state.repo_repo.count_all_records().await.unwrap_or(0); 23 - let blob_storage_bytes = state.blob_repo.sum_blob_storage().await.unwrap_or(0); 20 + let user_count = state.repos.user.count_users().await.unwrap_or(0); 21 + let repo_count = state.repos.repo.count_repos().await.unwrap_or(0); 22 + let record_count = state.repos.repo.count_all_records().await.unwrap_or(0); 23 + let blob_storage_bytes = state.repos.blob.sum_blob_storage().await.unwrap_or(0); 24 24 25 25 Ok(Json(ServerStatsOutput { 26 26 user_count,
+9 -9
crates/tranquil-api/src/admin/status.rs
··· 45 45 let did: Did = did_str 46 46 .parse() 47 47 .map_err(|_| ApiError::InvalidDid("Invalid DID format".into()))?; 48 - match state.user_repo.get_status_by_did(&did).await { 48 + match state.repos.user.get_status_by_did(&did).await { 49 49 Ok(Some(status)) => { 50 50 let deactivated = status.deactivated_at.map(|_| StatusAttr { 51 51 applied: true, ··· 77 77 let cid: CidLink = uri_str 78 78 .parse() 79 79 .map_err(|_| ApiError::InvalidRequest("Invalid CID format".into()))?; 80 - match state.repo_repo.get_record_by_cid(&cid).await { 80 + match state.repos.repo.get_record_by_cid(&cid).await { 81 81 Ok(Some(record)) => { 82 82 let takedown = record.takedown_ref.as_ref().map(|r| StatusAttr { 83 83 applied: true, ··· 109 109 let did = params.did.as_ref().ok_or_else(|| { 110 110 ApiError::InvalidRequest("Must provide a did to request blob state".into()) 111 111 })?; 112 - match state.blob_repo.get_blob_with_takedown(&blob_cid).await { 112 + match state.repos.blob.get_blob_with_takedown(&blob_cid).await { 113 113 Ok(Some(blob)) => { 114 114 let takedown = blob.takedown_ref.as_ref().map(|r| StatusAttr { 115 115 applied: true, ··· 172 172 None 173 173 }; 174 174 state 175 - .user_repo 175 + .repos.user 176 176 .set_user_takedown(&did, takedown_ref) 177 177 .await 178 178 .map_err(|e| { ··· 182 182 } 183 183 if let Some(deactivated) = &input.deactivated { 184 184 let result = if deactivated.applied { 185 - state.user_repo.deactivate_account(&did, None).await 185 + state.repos.user.deactivate_account(&did, None).await 186 186 } else { 187 - state.user_repo.activate_account(&did).await 187 + state.repos.user.activate_account(&did).await 188 188 }; 189 189 result.map_err(|e| { 190 190 error!( ··· 218 218 warn!("Failed to sequence account event for deactivation: {}", e); 219 219 } 220 220 } 221 - if let Ok(Some(handle)) = state.user_repo.get_handle_by_did(&did).await { 221 + if let Ok(Some(handle)) = state.repos.user.get_handle_by_did(&did).await { 222 222 let _ = state 223 223 .cache 224 224 .delete(&tranquil_pds::cache_keys::handle_key(&handle)) ··· 249 249 None 250 250 }; 251 251 state 252 - .repo_repo 252 + .repos.repo 253 253 .set_record_takedown(&cid, takedown_ref) 254 254 .await 255 255 .map_err(|e| { ··· 282 282 None 283 283 }; 284 284 state 285 - .blob_repo 285 + .repos.blob 286 286 .update_blob_takedown(&cid, takedown_ref) 287 287 .await 288 288 .map_err(|e| {
+3 -3
crates/tranquil-api/src/age_assurance.rs
··· 68 68 let http_uri = "/"; 69 69 70 70 let auth_user = match validate_token_with_dpop( 71 - state.user_repo.as_ref(), 72 - state.oauth_repo.as_ref(), 71 + state.repos.user.as_ref(), 72 + state.repos.oauth.as_ref(), 73 73 &extracted.token, 74 74 extracted.scheme, 75 75 dpop_proof, ··· 89 89 } 90 90 }; 91 91 92 - match state.user_repo.get_by_did(&auth_user.did).await { 92 + match state.repos.user.get_by_did(&auth_user.did).await { 93 93 Ok(Some(user)) => { 94 94 tracing::debug!(created_at = ?user.created_at, "age assurance: got user"); 95 95 Some(user.created_at.to_rfc3339())
+15 -1
crates/tranquil-api/src/common.rs
··· 1 - use bcrypt::DEFAULT_COST; 1 + use bcrypt::{DEFAULT_COST, hash}; 2 2 use chrono::{DateTime, Utc}; 3 3 use std::collections::HashMap; 4 4 use tracing::error; ··· 243 243 error!("Bcrypt hash error: {:?}", e); 244 244 ApiError::InternalError(None) 245 245 }) 246 + } 247 + 248 + pub async fn hash_password_async(password: &str) -> Result<String, ApiError> { 249 + let password = password.to_string(); 250 + tokio::task::spawn_blocking(move || hash(password, DEFAULT_COST)) 251 + .await 252 + .map_err(|e| { 253 + error!("Failed to spawn blocking task: {:?}", e); 254 + ApiError::InternalError(None) 255 + })? 256 + .map_err(|e| { 257 + error!("Failed to hash password: {:?}", e); 258 + ApiError::InternalError(None) 259 + }) 246 260 } 247 261 248 262 pub fn validate_token_hash(
+18 -18
crates/tranquil-api/src/delegation.rs
··· 24 24 auth: Auth<Active>, 25 25 ) -> Result<Json<ControllersOutput<Vec<tranquil_db_traits::ControllerInfo>>>, ApiError> { 26 26 let controllers = state 27 - .delegation_repo 27 + .repos.delegation 28 28 .get_delegations_for_account(&auth.did) 29 29 .await 30 30 .map_err(|e| { ··· 99 99 100 100 if resolved.is_local 101 101 && state 102 - .delegation_repo 102 + .repos.delegation 103 103 .is_delegated_account(&input.controller_did) 104 104 .await 105 105 .unwrap_or(false) ··· 110 110 } 111 111 112 112 match state 113 - .delegation_repo 113 + .repos.delegation 114 114 .create_delegation( 115 115 can_add.did(), 116 116 &input.controller_did, ··· 121 121 { 122 122 Ok(_) => { 123 123 let _ = state 124 - .delegation_repo 124 + .repos.delegation 125 125 .log_delegation_action( 126 126 can_add.did(), 127 127 can_add.did(), ··· 158 158 Json(input): Json<RemoveControllerInput>, 159 159 ) -> Result<Json<SuccessResponse>, ApiError> { 160 160 match state 161 - .delegation_repo 161 + .repos.delegation 162 162 .revoke_delegation(&auth.did, &input.controller_did, &auth.did) 163 163 .await 164 164 { 165 165 Ok(true) => { 166 166 let revoked_app_passwords = state 167 - .session_repo 167 + .repos.session 168 168 .delete_app_passwords_by_controller(&auth.did, &input.controller_did) 169 169 .await 170 170 .unwrap_or(0) ··· 172 172 .unwrap_or(0usize); 173 173 174 174 let revoked_oauth_tokens = state 175 - .oauth_repo 175 + .repos.oauth 176 176 .revoke_tokens_for_controller(&auth.did, &input.controller_did) 177 177 .await 178 178 .unwrap_or(0); 179 179 180 180 let _ = state 181 - .delegation_repo 181 + .repos.delegation 182 182 .log_delegation_action( 183 183 &auth.did, 184 184 &auth.did, ··· 217 217 Json(input): Json<UpdateControllerScopesInput>, 218 218 ) -> Result<Json<SuccessResponse>, ApiError> { 219 219 match state 220 - .delegation_repo 220 + .repos.delegation 221 221 .update_delegation_scopes(&auth.did, &input.controller_did, &input.granted_scopes) 222 222 .await 223 223 { 224 224 Ok(true) => { 225 225 let _ = state 226 - .delegation_repo 226 + .repos.delegation 227 227 .log_delegation_action( 228 228 &auth.did, 229 229 &auth.did, ··· 254 254 auth: Auth<Active>, 255 255 ) -> Result<Json<AccountsOutput<Vec<tranquil_db_traits::DelegatedAccountInfo>>>, ApiError> { 256 256 let accounts = state 257 - .delegation_repo 257 + .repos.delegation 258 258 .get_accounts_controlled_by(&auth.did) 259 259 .await 260 260 .map_err(|e| { ··· 286 286 let offset = params.offset.max(0); 287 287 288 288 let entries = state 289 - .delegation_repo 289 + .repos.delegation 290 290 .get_audit_log_for_account(&auth.did, limit, offset) 291 291 .await 292 292 .map_err(|e| { ··· 295 295 })?; 296 296 297 297 let total = state 298 - .delegation_repo 298 + .repos.delegation 299 299 .count_audit_log_entries(&auth.did) 300 300 .await 301 301 .unwrap_or_default(); ··· 349 349 } 350 350 351 351 let validated_invite_code = if let Some(ref code) = input.invite_code { 352 - match state.infra_repo.validate_invite_code(code).await { 352 + match state.repos.infra.validate_invite_code(code).await { 353 353 Ok(validated) => Some(validated), 354 354 Err(_) => return Err(ApiError::InvalidInviteCode), 355 355 } ··· 387 387 }; 388 388 389 389 let user_id = match state 390 - .user_repo 390 + .repos.user 391 391 .create_delegated_account(&create_input) 392 392 .await 393 393 { ··· 406 406 407 407 if let Some(validated) = validated_invite_code 408 408 && let Err(e) = state 409 - .infra_repo 409 + .repos.infra 410 410 .record_invite_code_use(&validated, user_id) 411 411 .await 412 412 { ··· 423 423 .await; 424 424 425 425 let _ = state 426 - .delegation_repo 426 + .repos.delegation 427 427 .log_delegation_action( 428 428 &did, 429 429 &auth.did, ··· 461 461 } else { 462 462 let local_handle: Option<Handle> = identifier.parse().ok(); 463 463 let local_user = match local_handle { 464 - Some(ref h) => state.user_repo.get_by_handle(h).await.ok().flatten(), 464 + Some(ref h) => state.repos.user.get_by_handle(h).await.ok().flatten(), 465 465 None => None, 466 466 }; 467 467 match local_user {
+3 -3
crates/tranquil-api/src/discord_webhook.rs
··· 169 169 ); 170 170 171 171 match state 172 - .user_repo 172 + .repos.user 173 173 .store_discord_user_id(&discord_username, &discord_user_id, handle.as_deref()) 174 174 .await 175 175 { ··· 180 180 "Verified Discord user and stored user ID" 181 181 ); 182 182 if let Err(e) = comms_repo::enqueue_channel_verified( 183 - state.user_repo.as_ref(), 184 - state.infra_repo.as_ref(), 183 + state.repos.user.as_ref(), 184 + state.repos.infra.as_ref(), 185 185 user_id, 186 186 tranquil_db_traits::CommsChannel::Discord, 187 187 &discord_user_id,
+11 -21
crates/tranquil-api/src/identity/account.rs
··· 6 6 http::{HeaderMap, StatusCode}, 7 7 response::{IntoResponse, Response}, 8 8 }; 9 - use bcrypt::{DEFAULT_COST, hash}; 10 9 use serde::{Deserialize, Serialize}; 11 10 use serde_json::json; 12 11 use tracing::{debug, error, info}; ··· 68 67 new_email: email.clone(), 69 68 }; 70 69 match state 71 - .user_repo 70 + .repos.user 72 71 .reactivate_migration_account(&reactivate_input) 73 72 .await 74 73 { 75 74 Ok(reactivated) => { 76 75 info!(did = %did, old_handle = %reactivated.old_handle, new_handle = %handle, "Preparing existing account for inbound migration"); 77 76 let secret_key_bytes = match state 78 - .user_repo 77 + .repos.user 79 78 .get_user_key_by_id(reactivated.user_id) 80 79 .await 81 80 { ··· 130 129 controller_did: None, 131 130 app_password_name: None, 132 131 }; 133 - if let Err(e) = state.session_repo.create_session(&session_data).await { 132 + if let Err(e) = state.repos.session.create_session(&session_data).await { 134 133 error!("Error creating session: {:?}", e); 135 134 return Some(ApiError::InternalError(None).into_response()); 136 135 } ··· 395 394 Err(_) => return ApiError::InvalidHandle(None).into_response(), 396 395 }; 397 396 let handle_available = match state 398 - .user_repo 397 + .repos.user 399 398 .check_handle_available_for_new_account(&handle_typed) 400 399 .await 401 400 { ··· 410 409 } 411 410 412 411 let is_bootstrap = state.bootstrap_invite_code.is_some() 413 - && state.user_repo.count_users().await.unwrap_or(1) == 0; 412 + && state.repos.user.count_users().await.unwrap_or(1) == 0; 414 413 415 414 if is_bootstrap { 416 415 match input.invite_code.as_deref() { ··· 431 430 if let Some(code) = &input.invite_code 432 431 && !code.trim().is_empty() 433 432 { 434 - let valid = match state.user_repo.check_and_consume_invite_code(code).await { 433 + let valid = match state.repos.user.check_and_consume_invite_code(code).await { 435 434 Ok(v) => v, 436 435 Err(e) => { 437 436 error!("Error checking invite code: {:?}", e); ··· 448 447 return ApiError::InvalidRequest(e.to_string()).into_response(); 449 448 } 450 449 451 - let password_clone = input.password.clone(); 452 - let password_hash = 453 - match tokio::task::spawn_blocking(move || hash(&password_clone, DEFAULT_COST)).await { 454 - Ok(Ok(h)) => h, 455 - Ok(Err(e)) => { 456 - error!("Error hashing password: {:?}", e); 457 - return ApiError::InternalError(None).into_response(); 458 - } 459 - Err(e) => { 460 - error!("Failed to spawn blocking task: {:?}", e); 461 - return ApiError::InternalError(None).into_response(); 462 - } 463 - }; 450 + let password_hash = match crate::common::hash_password_async(&input.password).await { 451 + Ok(h) => h, 452 + Err(e) => return e.into_response(), 453 + }; 464 454 465 455 let deactivated_at: Option<chrono::DateTime<chrono::Utc>> = if is_migration || is_did_web_byod { 466 456 Some(chrono::Utc::now()) ··· 527 517 birthdate_pref, 528 518 }; 529 519 530 - let create_result = match state.user_repo.create_password_account(&create_input).await { 520 + let create_result = match state.repos.user.create_password_account(&create_input).await { 531 521 Ok(r) => r, 532 522 Err(tranquil_db_traits::CreateAccountError::HandleTaken) => { 533 523 return ApiError::HandleNotAvailable(None).into_response();
+12 -12
crates/tranquil-api/src/identity/did.rs
··· 54 54 return ApiError::InvalidHandle(Some("Invalid handle format".into())).into_response(); 55 55 } 56 56 }; 57 - let user = state.user_repo.get_by_handle(&handle).await; 57 + let user = state.repos.user.get_by_handle(&handle).await; 58 58 match user { 59 59 Ok(Some(row)) => { 60 60 let _ = state ··· 165 165 Err(_) => return ApiError::InvalidRequest("Invalid DID format".into()).into_response(), 166 166 }; 167 167 let user = match state 168 - .user_repo 168 + .repos.user 169 169 .get_user_for_did_doc_build(&expected_did_typed) 170 170 .await 171 171 { ··· 182 182 let did = expected_did; 183 183 184 184 let overrides = state 185 - .user_repo 185 + .repos.user 186 186 .get_did_web_overrides(user_id) 187 187 .await 188 188 .ok() ··· 218 218 } 219 219 }; 220 220 let user = match state 221 - .user_repo 221 + .repos.user 222 222 .get_did_web_info_by_handle(&current_handle_typed) 223 223 .await 224 224 { ··· 246 246 } 247 247 248 248 let overrides = state 249 - .user_repo 249 + .repos.user 250 250 .get_did_web_overrides(user_id) 251 251 .await 252 252 .ok() ··· 295 295 .collect()); 296 296 } 297 297 298 - let key_info = match state.user_repo.get_user_key_by_id(user_id).await { 298 + let key_info = match state.repos.user.get_user_key_by_id(user_id).await { 299 299 Ok(Some(k)) => k, 300 300 _ => return Err(ApiError::InternalError(None).into_response()), 301 301 }; ··· 468 468 auth: Auth<NotTakendown>, 469 469 ) -> Result<Json<GetRecommendedDidCredentialsOutput>, ApiError> { 470 470 let handle = state 471 - .user_repo 471 + .repos.user 472 472 .get_handle_by_did(&auth.did) 473 473 .await 474 474 .log_db_err("fetching handle for DID credentials")? ··· 539 539 ) 540 540 .await?; 541 541 let user_row = state 542 - .user_repo 542 + .repos.user 543 543 .get_id_and_handle_by_did(&did) 544 544 .await 545 545 .log_db_err("fetching user for handle update")? ··· 661 661 .parse() 662 662 .map_err(|_| ApiError::InvalidHandle(Some("Invalid handle format".into())))?; 663 663 let handle_exists = state 664 - .user_repo 664 + .repos.user 665 665 .check_handle_exists(&handle_typed, user_id) 666 666 .await 667 667 .log_db_err("checking handle existence")?; ··· 669 669 return Err(ApiError::HandleTaken); 670 670 } 671 671 state 672 - .user_repo 672 + .repos.user 673 673 .update_handle(user_id, &handle_typed) 674 674 .await 675 675 .map_err(|e| { ··· 706 706 if !did.as_str().starts_with("did:plc:") { 707 707 return Ok(()); 708 708 } 709 - let user_row = match state.user_repo.get_user_with_key_by_did(did).await? { 709 + let user_row = match state.repos.user.get_user_with_key_by_did(did).await? { 710 710 Some(r) => r, 711 711 None => return Ok(()), 712 712 }; ··· 733 733 Ok(h) => h, 734 734 Err(_) => return (StatusCode::BAD_REQUEST, "Invalid handle format").into_response(), 735 735 }; 736 - let user = state.user_repo.get_by_handle(&handle).await; 736 + let user = state.repos.user.get_by_handle(&handle).await; 737 737 match user { 738 738 Ok(Some(row)) => row.did.to_string().into_response(), 739 739 Ok(None) => (StatusCode::NOT_FOUND, "Handle not found").into_response(),
+5 -5
crates/tranquil-api/src/identity/plc/request.rs
··· 20 20 tranquil_pds::oauth::scopes::IdentityAttr::Wildcard, 21 21 )?; 22 22 let user_id = state 23 - .user_repo 23 + .repos.user 24 24 .get_id_by_did(&auth.did) 25 25 .await 26 26 .log_db_err("fetching user id")? 27 27 .ok_or(ApiError::AccountNotFound)?; 28 28 29 - let _ = state.infra_repo.delete_plc_tokens_for_user(user_id).await; 29 + let _ = state.repos.infra.delete_plc_tokens_for_user(user_id).await; 30 30 let plc_token = generate_plc_token(); 31 31 let expires_at = Utc::now() + Duration::minutes(10); 32 32 state 33 - .infra_repo 33 + .repos.infra 34 34 .insert_plc_token(user_id, &plc_token, expires_at) 35 35 .await 36 36 .log_db_err("creating PLC token")?; 37 37 38 38 let hostname = &tranquil_config::get().server.hostname; 39 39 if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_plc_operation( 40 - state.user_repo.as_ref(), 41 - state.infra_repo.as_ref(), 40 + state.repos.user.as_ref(), 41 + state.repos.infra.as_ref(), 42 42 user_id, 43 43 &plc_token, 44 44 hostname,
+5 -5
crates/tranquil-api/src/identity/plc/sign.rs
··· 55 55 })?; 56 56 57 57 let user_id = state 58 - .user_repo 58 + .repos.user 59 59 .get_id_by_did(did) 60 60 .await 61 61 .log_db_err("fetching user id")? 62 62 .ok_or(ApiError::AccountNotFound)?; 63 63 64 64 let token_expiry = state 65 - .infra_repo 65 + .repos.infra 66 66 .get_plc_token_expiry(user_id, token) 67 67 .await 68 68 .log_db_err("fetching PLC token expiry")? 69 69 .ok_or_else(|| ApiError::InvalidToken(Some("Invalid or expired token".into())))?; 70 70 71 71 if Utc::now() > token_expiry { 72 - let _ = state.infra_repo.delete_plc_token(user_id, token).await; 72 + let _ = state.repos.infra.delete_plc_token(user_id, token).await; 73 73 return Err(ApiError::ExpiredToken(Some("Token has expired".into()))); 74 74 } 75 75 let key_row = state 76 - .user_repo 76 + .repos.user 77 77 .get_user_key_by_id(user_id) 78 78 .await 79 79 .log_db_err("fetching user key")? ··· 136 136 ApiError::InternalError(None) 137 137 })?; 138 138 139 - let _ = state.infra_repo.delete_plc_token(user_id, token).await; 139 + let _ = state.repos.infra.delete_plc_token(user_id, token).await; 140 140 info!("Signed PLC operation for user {}", did); 141 141 Ok(Json(SignPlcOperationOutput { 142 142 operation: signed_op,
+4 -4
crates/tranquil-api/src/identity/plc/submit.rs
··· 38 38 let hostname = &tranquil_config::get().server.hostname; 39 39 let public_url = format!("https://{}", hostname); 40 40 let user = state 41 - .user_repo 41 + .repos.user 42 42 .get_id_and_handle_by_did(did) 43 43 .await 44 44 .log_db_err("fetching user")? 45 45 .ok_or(ApiError::AccountNotFound)?; 46 46 47 47 let key_row = state 48 - .user_repo 48 + .repos.user 49 49 .get_user_key_by_id(user.id) 50 50 .await 51 51 .log_db_err("fetching user key")? ··· 128 128 .map_err(ApiError::from)?; 129 129 130 130 match state 131 - .repo_repo 131 + .repos.repo 132 132 .insert_identity_event(did, Some(&user.handle)) 133 133 .await 134 134 { 135 135 Ok(seq) => { 136 - if let Err(e) = state.repo_repo.notify_update(seq).await { 136 + if let Err(e) = state.repos.repo.notify_update(seq).await { 137 137 warn!("Failed to notify identity event: {:?}", e); 138 138 } 139 139 }
+6 -6
crates/tranquil-api/src/identity/provision.rs
··· 136 136 match signing_key_did { 137 137 Some(key_did) => { 138 138 let key = state 139 - .infra_repo 139 + .repos.infra 140 140 .get_reserved_signing_key(key_did) 141 141 .await 142 142 .map_err(|e| { ··· 295 295 app_password_name: None, 296 296 }; 297 297 state 298 - .session_repo 298 + .repos.session 299 299 .create_session(&session_data) 300 300 .await 301 301 .map_err(|e| { ··· 320 320 let formatted = tranquil_pds::auth::verification_token::format_token_for_display(&token); 321 321 let hostname = &tranquil_config::get().server.hostname; 322 322 if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_signup_verification( 323 - state.user_repo.as_ref(), 324 - state.infra_repo.as_ref(), 323 + state.repos.user.as_ref(), 324 + state.repos.infra.as_ref(), 325 325 user_id, 326 326 channel, 327 327 recipient, ··· 346 346 let formatted = tranquil_pds::auth::verification_token::format_token_for_display(&token); 347 347 let hostname = &tranquil_config::get().server.hostname; 348 348 if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_migration_verification( 349 - state.user_repo.as_ref(), 350 - state.infra_repo.as_ref(), 349 + state.repos.user.as_ref(), 350 + state.repos.infra.as_ref(), 351 351 user_id, 352 352 channel, 353 353 recipient,
+2 -2
crates/tranquil-api/src/moderation/mod.rs
··· 107 107 108 108 let key_bytes = match &auth_user.key_bytes { 109 109 Some(kb) => kb.clone(), 110 - None => match state.user_repo.get_with_key_by_did(&auth_user.did).await { 110 + None => match state.repos.user.get_with_key_by_did(&auth_user.did).await { 111 111 Ok(Some(user_with_key)) => { 112 112 match tranquil_pds::config::decrypt_key( 113 113 &user_with_key.key_bytes, ··· 226 226 let subject_json = json!(input.subject); 227 227 228 228 if let Err(e) = state 229 - .infra_repo 229 + .repos.infra 230 230 .insert_report( 231 231 report_id, 232 232 input.reason_type.as_str(),
+16 -16
crates/tranquil-api/src/notification_prefs.rs
··· 26 26 auth: Auth<Active>, 27 27 ) -> Result<Json<NotificationPrefsOutput>, ApiError> { 28 28 let prefs = state 29 - .user_repo 29 + .repos.user 30 30 .get_notification_prefs(&auth.did) 31 31 .await 32 32 .log_db_err("get notification prefs")? ··· 65 65 auth: Auth<Active>, 66 66 ) -> Result<Json<GetNotificationHistoryOutput>, ApiError> { 67 67 let user_id = state 68 - .user_repo 68 + .repos.user 69 69 .get_id_by_did(&auth.did) 70 70 .await 71 71 .log_db_err("get user id by did")? 72 72 .ok_or(ApiError::AccountNotFound)?; 73 73 74 74 let rows = state 75 - .infra_repo 75 + .repos.infra 76 76 .get_notification_history(user_id, 50) 77 77 .await 78 78 .log_db_err("get notification history")?; ··· 146 146 let hostname = &tranquil_config::get().server.hostname; 147 147 let handle_str = handle.unwrap_or("user"); 148 148 tranquil_pds::comms::comms_repo::enqueue_email_update( 149 - state.infra_repo.as_ref(), 149 + state.repos.infra.as_ref(), 150 150 user_id, 151 151 identifier, 152 152 handle_str, ··· 165 165 hostname, encoded_token, encoded_identifier 166 166 ); 167 167 let prefs = state 168 - .user_repo 168 + .repos.user 169 169 .get_comms_prefs(user_id) 170 170 .await 171 171 .ok() ··· 185 185 ); 186 186 let recipient = match channel { 187 187 CommsChannel::Telegram => state 188 - .user_repo 188 + .repos.user 189 189 .get_telegram_chat_id(user_id) 190 190 .await 191 191 .ok() ··· 195 195 _ => identifier.to_string(), 196 196 }; 197 197 state 198 - .infra_repo 198 + .repos.infra 199 199 .enqueue_comms( 200 200 Some(user_id), 201 201 channel, ··· 238 238 } 239 239 match channel { 240 240 CommsChannel::Discord => state 241 - .user_repo 241 + .repos.user 242 242 .clear_discord(user_id) 243 243 .await 244 244 .log_db_err("clear discord")?, 245 245 CommsChannel::Telegram => state 246 - .user_repo 246 + .repos.user 247 247 .clear_telegram(user_id) 248 248 .await 249 249 .log_db_err("clear telegram")?, 250 250 CommsChannel::Signal => state 251 - .user_repo 251 + .repos.user 252 252 .clear_signal(user_id) 253 253 .await 254 254 .log_db_err("clear signal")?, ··· 281 281 282 282 match channel { 283 283 CommsChannel::Discord => state 284 - .user_repo 284 + .repos.user 285 285 .set_unverified_discord(user_id, &clean) 286 286 .await 287 287 .log_db_err("set unverified discord")?, 288 288 CommsChannel::Telegram => state 289 - .user_repo 289 + .repos.user 290 290 .set_unverified_telegram(user_id, &clean) 291 291 .await 292 292 .log_db_err("set unverified telegram")?, 293 293 CommsChannel::Signal => state 294 - .user_repo 294 + .repos.user 295 295 .set_unverified_signal(user_id, &clean) 296 296 .await 297 297 .log_db_err("set unverified signal")?, ··· 313 313 Json(input): Json<UpdateNotificationPrefsInput>, 314 314 ) -> Result<Json<UpdateNotificationPrefsOutput>, ApiError> { 315 315 let user_row = state 316 - .user_repo 316 + .repos.user 317 317 .get_id_handle_email_by_did(&auth.did) 318 318 .await 319 319 .log_db_err("get user by did")? ··· 324 324 let current_email = user_row.email; 325 325 326 326 let current_prefs = state 327 - .user_repo 327 + .repos.user 328 328 .get_notification_prefs(&auth.did) 329 329 .await 330 330 .log_db_err("get notification prefs for update")? ··· 347 347 348 348 if input.preferred_channel.is_some() { 349 349 state 350 - .user_repo 350 + .repos.user 351 351 .update_preferred_comms_channel(&auth.did, effective_channel) 352 352 .await 353 353 .log_db_err("update preferred channel")?;
+7 -7
crates/tranquil-api/src/repo/blob.rs
··· 66 66 }; 67 67 68 68 if state 69 - .user_repo 69 + .repos.user 70 70 .is_account_migrated(&did) 71 71 .await 72 72 .unwrap_or(false) ··· 78 78 get_header_str(&headers, http::header::CONTENT_TYPE).unwrap_or("application/octet-stream"); 79 79 80 80 let user_id = state 81 - .user_repo 81 + .repos.user 82 82 .get_id_by_did(&did) 83 83 .await 84 84 .log_db_err("fetching user id for blob upload")? ··· 143 143 ); 144 144 145 145 match state 146 - .blob_repo 146 + .repos.blob 147 147 .insert_blob( 148 148 &cid_link, 149 149 &mime_type, ··· 163 163 164 164 if let Err(e) = state.blob_store.copy(&temp_key, &storage_key).await { 165 165 let _ = state.blob_store.delete(&temp_key).await; 166 - if let Err(db_err) = state.blob_repo.delete_blob_by_cid(&cid_link).await { 166 + if let Err(db_err) = state.repos.blob.delete_blob_by_cid(&cid_link).await { 167 167 error!( 168 168 "Failed to clean up orphaned blob record after copy failure: {:?}", 169 169 db_err ··· 177 177 178 178 if let Some(ref controller) = controller_did 179 179 && let Err(e) = state 180 - .delegation_repo 180 + .repos.delegation 181 181 .log_delegation_action( 182 182 &did, 183 183 controller, ··· 236 236 ) -> Result<Json<ListMissingBlobsOutput>, ApiError> { 237 237 let did = &auth.did; 238 238 let user = state 239 - .user_repo 239 + .repos.user 240 240 .get_by_did(did) 241 241 .await 242 242 .log_db_err("fetching user")? ··· 245 245 let limit = params.limit.unwrap_or(500).clamp(1, 1000); 246 246 let cursor = params.cursor.as_deref(); 247 247 let missing = state 248 - .blob_repo 248 + .repos.blob 249 249 .list_missing_blobs(user.id, cursor, limit + 1) 250 250 .await 251 251 .log_db_err("fetching missing blobs")?;
+10 -10
crates/tranquil-api/src/repo/import.rs
··· 34 34 } 35 35 let did = &auth.did; 36 36 let user = state 37 - .user_repo 37 + .repos.user 38 38 .get_by_did(did) 39 39 .await 40 40 .log_db_err("fetching user")? ··· 44 44 } 45 45 let user_id = user.id; 46 46 let expected_root_cid = state 47 - .repo_repo 47 + .repos.repo 48 48 .get_repo_root_cid_by_user_id(user_id) 49 49 .await 50 50 .map_err(|e| { ··· 194 194 let max_blocks = tranquil_config::get().import.max_blocks as usize; 195 195 let _write_lock = state.repo_write_locks.lock(user_id).await; 196 196 match apply_import( 197 - &state.repo_repo, 197 + &state.repos.repo, 198 198 user_id, 199 199 root, 200 200 blocks.clone(), ··· 232 232 blob_refs.into_iter().unzip(); 233 233 234 234 match state 235 - .blob_repo 235 + .repos.blob 236 236 .insert_record_blobs(user_id, &record_uris, &blob_cids) 237 237 .await 238 238 { ··· 248 248 } 249 249 } 250 250 let key_row = state 251 - .user_repo 251 + .repos.user 252 252 .get_user_with_key_by_did(did) 253 253 .await 254 254 .map_err(|e| { ··· 289 289 })?; 290 290 let new_root_cid_link = CidLink::from(&new_root_cid); 291 291 state 292 - .repo_repo 292 + .repos.repo 293 293 .update_repo_root(user_id, &new_root_cid_link, &new_rev_str) 294 294 .await 295 295 .map_err(|e| { ··· 299 299 let mut all_block_cids: Vec<Vec<u8>> = blocks.keys().map(|c| c.to_bytes()).collect(); 300 300 all_block_cids.push(new_root_cid.to_bytes()); 301 301 state 302 - .repo_repo 302 + .repos.repo 303 303 .insert_user_blocks(user_id, &all_block_cids, &new_rev_str) 304 304 .await 305 305 .map_err(|e| { ··· 322 322 "birthDate": "1998-05-06T00:00:00.000Z" 323 323 }); 324 324 if let Err(e) = state 325 - .infra_repo 325 + .repos.infra 326 326 .insert_account_preference_if_not_exists( 327 327 user_id, 328 328 "app.bsky.actor.defs#personalDetailsPref", ··· 391 391 rev: None, 392 392 }; 393 393 394 - let seq = state.repo_repo.insert_commit_event(&data).await?; 395 - state.repo_repo.notify_update(seq).await?; 394 + let seq = state.repos.repo.insert_commit_event(&data).await?; 395 + state.repos.repo.notify_update(seq).await?; 396 396 Ok(()) 397 397 }
+2 -2
crates/tranquil-api/src/repo/meta.rs
··· 18 18 State(state): State<AppState>, 19 19 Query(input): Query<DescribeRepoInput>, 20 20 ) -> Response { 21 - let resolved = match common::resolve_repo(state.user_repo.as_ref(), &input.repo).await { 21 + let resolved = match common::resolve_repo(state.repos.user.as_ref(), &input.repo).await { 22 22 Ok(r) => r, 23 23 Err(e) => return e.into_response(), 24 24 }; 25 25 let collections = state 26 - .repo_repo 26 + .repos.repo 27 27 .list_collections(resolved.user_id) 28 28 .await 29 29 .unwrap_or_default();
+2 -2
crates/tranquil-api/src/repo/record/batch.rs
··· 11 11 Active, Auth, WriteOpKind, require_not_migrated, require_verified_or_delegated, 12 12 verify_batch_write_scopes, 13 13 }; 14 - use tranquil_pds::repo::tracking::TrackingBlockStore; 14 + use tranquil_pds::repo::TrackingBlockStore; 15 15 use tranquil_pds::repo_ops::{ 16 16 FinalizeParams, RecordOp, begin_repo_write, extract_blob_cids, finalize_repo_write, 17 17 }; ··· 304 304 require_verified_or_delegated(&state, batch_proof.user()).await?; 305 305 306 306 let user_id: uuid::Uuid = state 307 - .user_repo 307 + .repos.user 308 308 .get_id_by_did(&did) 309 309 .await 310 310 .log_db_err("fetching user for batch write")?
+3 -3
crates/tranquil-api/src/repo/record/delete.rs
··· 9 9 use tracing::error; 10 10 use tranquil_pds::api::error::ApiError; 11 11 use tranquil_pds::auth::{Active, Auth, VerifyScope}; 12 - use tranquil_pds::repo::tracking::TrackingBlockStore; 12 + use tranquil_pds::repo::TrackingBlockStore; 13 13 use tranquil_pds::repo_ops::{ 14 14 CommitError, FinalizeParams, RecordOp, begin_repo_write, finalize_repo_write, 15 15 }; ··· 101 101 102 102 let deleted_uri = AtUri::from_parts(&did, &input.collection, &input.rkey); 103 103 if let Err(e) = state 104 - .backlink_repo 104 + .repos.backlink 105 105 .remove_backlinks_by_uri(&deleted_uri) 106 106 .await 107 107 { ··· 130 130 let _write_lock = state.repo_write_locks.lock(user_id).await; 131 131 132 132 let root_cid_str = state 133 - .repo_repo 133 + .repos.repo 134 134 .get_repo_root_cid_by_user_id(user_id) 135 135 .await 136 136 .map_err(|e| CommitError::DatabaseError(e.to_string()))?
+4 -4
crates/tranquil-api/src/repo/record/read.rs
··· 60 60 _headers: HeaderMap, 61 61 Query(input): Query<GetRecordInput>, 62 62 ) -> Response { 63 - let user_id = match common::resolve_repo_user_id(state.user_repo.as_ref(), &input.repo).await { 63 + let user_id = match common::resolve_repo_user_id(state.repos.user.as_ref(), &input.repo).await { 64 64 Ok(id) => id, 65 65 Err(e) => return e.into_response(), 66 66 }; 67 67 let record_row = state 68 - .repo_repo 68 + .repos.repo 69 69 .get_record_cid(user_id, &input.collection, &input.rkey) 70 70 .await; 71 71 let record_cid_link = match record_row { ··· 128 128 State(state): State<AppState>, 129 129 Query(input): Query<ListRecordsInput>, 130 130 ) -> Response { 131 - let user_id = match common::resolve_repo_user_id(state.user_repo.as_ref(), &input.repo).await { 131 + let user_id = match common::resolve_repo_user_id(state.repos.user.as_ref(), &input.repo).await { 132 132 Ok(id) => id, 133 133 Err(e) => return e.into_response(), 134 134 }; ··· 139 139 .as_ref() 140 140 .and_then(|c| c.parse::<tranquil_pds::types::Rkey>().ok()); 141 141 let rows = match state 142 - .repo_repo 142 + .repos.repo 143 143 .list_records( 144 144 user_id, 145 145 &input.collection,
+4 -4
crates/tranquil-api/src/repo/record/write.rs
··· 46 46 let _account_verified = require_verified_or_delegated(state, user).await?; 47 47 48 48 let user_id = state 49 - .user_repo 49 + .repos.user 50 50 .get_id_by_did(principal_did.as_did()) 51 51 .await 52 52 .log_db_err("fetching user for repo write")? ··· 128 128 129 129 if !backlinks.is_empty() { 130 130 let conflicts = state 131 - .backlink_repo 131 + .repos.backlink 132 132 .get_backlink_conflicts(user_id, &input.collection, &backlinks) 133 133 .await 134 134 .log_db_err("checking backlink conflicts")?; ··· 229 229 .await?; 230 230 231 231 { 232 - let backlink_repo = state.backlink_repo.clone(); 232 + let backlink_repo = state.repos.backlink.clone(); 233 233 futures::future::join_all(conflict_uris_to_cleanup.iter().map(|uri| { 234 234 let backlink_repo = backlink_repo.clone(); 235 235 async move { ··· 244 244 let created_uri = AtUri::from_parts(&did, &input.collection, &rkey); 245 245 let backlinks = extract_backlinks(&created_uri, &input.record); 246 246 if !backlinks.is_empty() 247 - && let Err(e) = state.backlink_repo.add_backlinks(user_id, &backlinks).await 247 + && let Err(e) = state.repos.backlink.add_backlinks(user_id, &backlinks).await 248 248 { 249 249 error!("Failed to add backlinks for {}: {}", created_uri, e); 250 250 }
+24 -24
crates/tranquil-api/src/server/account_status.rs
··· 41 41 ) -> Result<Json<CheckAccountStatusOutput>, ApiError> { 42 42 let did = &auth.did; 43 43 let user_id = state 44 - .user_repo 44 + .repos.user 45 45 .get_id_by_did(did) 46 46 .await 47 47 .log_db_err("fetching user ID for account status")? 48 48 .ok_or(ApiError::InternalError(None))?; 49 49 let is_active = state 50 - .user_repo 50 + .repos.user 51 51 .is_account_active_by_did(did) 52 52 .await 53 53 .ok() 54 54 .flatten() 55 55 .unwrap_or(false); 56 - let repo_info = state.repo_repo.get_repo(user_id).await.ok().flatten(); 56 + let repo_info = state.repos.repo.get_repo(user_id).await.ok().flatten(); 57 57 let (repo_commit, repo_rev_from_db) = repo_info 58 58 .map(|r| (r.repo_root_cid.to_string(), r.repo_rev)) 59 59 .unwrap_or_else(|| (String::new(), None)); 60 60 let block_count: i64 = state 61 - .repo_repo 61 + .repos.repo 62 62 .count_user_blocks(user_id) 63 63 .await 64 64 .unwrap_or(0); ··· 80 80 } else { 81 81 String::new() 82 82 }; 83 - let record_count: i64 = state.repo_repo.count_records(user_id).await.unwrap_or(0); 83 + let record_count: i64 = state.repos.repo.count_records(user_id).await.unwrap_or(0); 84 84 let imported_blobs: i64 = state 85 - .blob_repo 85 + .repos.blob 86 86 .count_blobs_by_user(user_id) 87 87 .await 88 88 .unwrap_or(0); 89 89 let expected_blobs: i64 = state 90 - .blob_repo 90 + .repos.blob 91 91 .count_distinct_record_blobs(user_id) 92 92 .await 93 93 .unwrap_or(0); 94 94 let valid_did = 95 - is_valid_did_for_service(state.user_repo.as_ref(), state.cache.clone(), did).await; 95 + is_valid_did_for_service(state.repos.user.as_ref(), state.cache.clone(), did).await; 96 96 Ok(Json(CheckAccountStatusOutput { 97 97 activated: is_active, 98 98 valid_did, ··· 319 319 ); 320 320 let did_validation_start = std::time::Instant::now(); 321 321 if let Err(e) = assert_valid_did_document_for_service( 322 - state.user_repo.as_ref(), 322 + state.repos.user.as_ref(), 323 323 state.cache.clone(), 324 324 &did, 325 325 true, ··· 339 339 did_validation_start.elapsed() 340 340 ); 341 341 342 - let handle = state.user_repo.get_handle_by_did(&did).await.ok().flatten(); 342 + let handle = state.repos.user.get_handle_by_did(&did).await.ok().flatten(); 343 343 info!( 344 344 "[MIGRATION] activateAccount: Activating account did={} handle={:?}", 345 345 did, handle 346 346 ); 347 - let result = state.user_repo.activate_account(&did).await; 347 + let result = state.repos.user.activate_account(&did).await; 348 348 match result { 349 349 Ok(_) => { 350 350 info!( ··· 406 406 info!("[MIGRATION] activateAccount: Identity event sequenced successfully"); 407 407 } 408 408 let repo_root = state 409 - .repo_repo 409 + .repos.repo 410 410 .get_repo_root_by_did(&did) 411 411 .await 412 412 .ok() ··· 480 480 481 481 let did = auth.did.clone(); 482 482 483 - let handle = state.user_repo.get_handle_by_did(&did).await.ok().flatten(); 483 + let handle = state.repos.user.get_handle_by_did(&did).await.ok().flatten(); 484 484 485 - let result = state.user_repo.deactivate_account(&did, delete_after).await; 485 + let result = state.repos.user.deactivate_account(&did, delete_after).await; 486 486 487 487 match result { 488 488 Ok(true) => { ··· 518 518 let session_mfa = require_legacy_session_mfa(&state, &auth).await?; 519 519 520 520 let user_id = state 521 - .user_repo 521 + .repos.user 522 522 .get_id_by_did(session_mfa.did()) 523 523 .await 524 524 .ok() ··· 527 527 let confirmation_token = Uuid::new_v4().to_string(); 528 528 let expires_at = Utc::now() + Duration::minutes(15); 529 529 state 530 - .infra_repo 530 + .repos.infra 531 531 .create_deletion_request(&confirmation_token, session_mfa.did(), expires_at) 532 532 .await 533 533 .log_db_err("creating deletion token")?; 534 534 let hostname = &tranquil_config::get().server.hostname; 535 535 if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_account_deletion( 536 - state.user_repo.as_ref(), 537 - state.infra_repo.as_ref(), 536 + state.repos.user.as_ref(), 537 + state.repos.infra.as_ref(), 538 538 user_id, 539 539 &confirmation_token, 540 540 hostname, ··· 572 572 return Err(ApiError::InvalidToken(Some("token is required".into()))); 573 573 } 574 574 let user = state 575 - .user_repo 575 + .repos.user 576 576 .get_user_for_deletion(did) 577 577 .await 578 578 .map_err(|e| { ··· 582 582 .ok_or(ApiError::InvalidRequest("account not found".into()))?; 583 583 let (user_id, password_hash, handle) = (user.id, user.password_hash, user.handle); 584 584 if crate::common::verify_credential( 585 - state.session_repo.as_ref(), 585 + state.repos.session.as_ref(), 586 586 user_id, 587 587 password, 588 588 password_hash.as_deref(), ··· 595 595 ))); 596 596 } 597 597 let deletion_request = state 598 - .infra_repo 598 + .repos.infra 599 599 .get_deletion_request(token) 600 600 .await 601 601 .map_err(|e| { ··· 611 611 ))); 612 612 } 613 613 if Utc::now() > deletion_request.expires_at { 614 - let _ = state.infra_repo.delete_deletion_request(token).await; 614 + let _ = state.repos.infra.delete_deletion_request(token).await; 615 615 return Err(ApiError::ExpiredToken(None)); 616 616 } 617 617 state 618 - .user_repo 618 + .repos.user 619 619 .delete_account_complete(user_id, did) 620 620 .await 621 621 .map_err(|e| { ··· 630 630 .await; 631 631 match account_seq { 632 632 Ok(seq) => { 633 - if let Err(e) = state.repo_repo.delete_sequences_except(did, seq).await { 633 + if let Err(e) = state.repos.repo.delete_sequences_except(did, seq).await { 634 634 warn!( 635 635 "Failed to cleanup sequences for deleted account {}: {}", 636 636 did, e
+12 -24
crates/tranquil-api/src/server/app_password.rs
··· 1 1 use axum::{Json, extract::State}; 2 2 use serde::{Deserialize, Serialize}; 3 3 use serde_json::json; 4 - use tracing::error; 5 4 use tranquil_db_traits::AppPasswordCreate; 6 5 use tranquil_pds::api::EmptyResponse; 7 6 use tranquil_pds::api::error::{ApiError, DbResultExt}; ··· 32 31 auth: Auth<Permissive>, 33 32 ) -> Result<Json<ListAppPasswordsOutput>, ApiError> { 34 33 let user = state 35 - .user_repo 34 + .repos.user 36 35 .get_by_did(&auth.did) 37 36 .await 38 37 .log_db_err("getting user")? 39 38 .ok_or(ApiError::AccountNotFound)?; 40 39 41 40 let rows = state 42 - .session_repo 41 + .repos.session 43 42 .list_app_passwords(user.id) 44 43 .await 45 44 .log_db_err("listing app passwords")?; ··· 84 83 Json(input): Json<CreateAppPasswordInput>, 85 84 ) -> Result<Json<CreateAppPasswordOutput>, ApiError> { 86 85 let user = state 87 - .user_repo 86 + .repos.user 88 87 .get_by_did(&auth.did) 89 88 .await 90 89 .log_db_err("getting user")? ··· 96 95 } 97 96 98 97 if state 99 - .session_repo 98 + .repos.session 100 99 .get_app_password_by_name(user.id, name) 101 100 .await 102 101 .log_db_err("checking app password")? ··· 107 106 108 107 let (final_scopes, controller_did) = if let Some(ref controller) = auth.controller_did { 109 108 let grant = state 110 - .delegation_repo 109 + .repos.delegation 111 110 .get_delegation(&auth.did, controller) 112 111 .await 113 112 .ok() ··· 133 132 134 133 let password = generate_app_password(); 135 134 136 - let password_clone = password.clone(); 137 - let password_hash = 138 - tokio::task::spawn_blocking(move || bcrypt::hash(&password_clone, bcrypt::DEFAULT_COST)) 139 - .await 140 - .map_err(|e| { 141 - error!("Failed to spawn blocking task: {:?}", e); 142 - ApiError::InternalError(None) 143 - })? 144 - .map_err(|e| { 145 - error!("Failed to hash password: {:?}", e); 146 - ApiError::InternalError(None) 147 - })?; 135 + let password_hash = crate::common::hash_password_async(&password).await?; 148 136 149 137 let privilege = tranquil_db_traits::AppPasswordPrivilege::from_privileged_flag( 150 138 input.privileged.unwrap_or(false), ··· 161 149 }; 162 150 163 151 state 164 - .session_repo 152 + .repos.session 165 153 .create_app_password(&create_data) 166 154 .await 167 155 .log_db_err("creating app password")?; 168 156 169 157 if let Some(ref controller) = controller_did { 170 158 let _ = state 171 - .delegation_repo 159 + .repos.delegation 172 160 .log_delegation_action( 173 161 &auth.did, 174 162 controller, ··· 204 192 Json(input): Json<RevokeAppPasswordInput>, 205 193 ) -> Result<Json<EmptyResponse>, ApiError> { 206 194 let user = state 207 - .user_repo 195 + .repos.user 208 196 .get_by_did(&auth.did) 209 197 .await 210 198 .log_db_err("getting user")? ··· 216 204 } 217 205 218 206 let sessions_to_invalidate = state 219 - .session_repo 207 + .repos.session 220 208 .get_session_jtis_by_app_password(&auth.did, name) 221 209 .await 222 210 .unwrap_or_default(); 223 211 224 212 state 225 - .session_repo 213 + .repos.session 226 214 .delete_sessions_by_app_password(&auth.did, name) 227 215 .await 228 216 .log_db_err("revoking sessions for app password")?; ··· 237 225 .await; 238 226 239 227 state 240 - .session_repo 228 + .repos.session 241 229 .delete_app_password(user.id, name) 242 230 .await 243 231 .log_db_err("revoking app password")?;
+32 -48
crates/tranquil-api/src/server/email.rs
··· 22 22 23 23 const EMAIL_UPDATE_TTL: Duration = Duration::from_secs(30 * 60); 24 24 25 - fn email_update_cache_key(did: &str) -> String { 26 - tranquil_pds::cache_keys::email_update_key(did) 27 - } 28 - 29 25 fn hash_token(token: &str) -> String { 30 26 let mut hasher = Sha256::new(); 31 27 hasher.update(token.as_bytes()); ··· 39 35 authorized: bool, 40 36 } 41 37 38 + async fn get_pending_email_update( 39 + cache: &dyn tranquil_pds::cache::Cache, 40 + did: &str, 41 + ) -> Option<PendingEmailUpdate> { 42 + cache 43 + .get(&tranquil_pds::cache_keys::email_update_key(did)) 44 + .await 45 + .and_then(|json| serde_json::from_str(&json).ok()) 46 + } 47 + 42 48 #[derive(Deserialize)] 43 49 #[serde(rename_all = "camelCase")] 44 50 pub struct RequestEmailUpdateInput { ··· 55 61 auth.check_account_scope(AccountAttr::Email, AccountAction::Manage)?; 56 62 57 63 let user = state 58 - .user_repo 64 + .repos.user 59 65 .get_email_info_by_did(&auth.did) 60 66 .await 61 67 .log_db_err("getting email info")? ··· 92 98 authorized: false, 93 99 }; 94 100 if let Ok(json) = serde_json::to_string(&pending) { 95 - let cache_key = email_update_cache_key(&auth.did); 101 + let cache_key = tranquil_pds::cache_keys::email_update_key(&auth.did); 96 102 if let Err(e) = state.cache.set(&cache_key, &json, EMAIL_UPDATE_TTL).await { 97 103 warn!("Failed to cache pending email update: {:?}", e); 98 104 } ··· 102 108 103 109 let hostname = &tranquil_config::get().server.hostname; 104 110 if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_short_token_email( 105 - state.user_repo.as_ref(), 106 - state.infra_repo.as_ref(), 111 + state.repos.user.as_ref(), 112 + state.repos.infra.as_ref(), 107 113 user.id, 108 114 &token, 109 115 hostname, ··· 135 141 136 142 let did = &auth.did; 137 143 let user = state 138 - .user_repo 144 + .repos.user 139 145 .get_email_info_by_did(did) 140 146 .await 141 147 .log_db_err("getting email info")? ··· 179 185 } 180 186 181 187 state 182 - .user_repo 188 + .repos.user 183 189 .set_email_verified(user.id, true) 184 190 .await 185 191 .log_db_err("confirming email")?; ··· 206 212 207 213 let did = &auth.did; 208 214 let user = state 209 - .user_repo 215 + .repos.user 210 216 .get_email_info_by_did(did) 211 217 .await 212 218 .log_db_err("getting email info")? ··· 253 259 } 254 260 255 261 state 256 - .infra_repo 262 + .repos.infra 257 263 .upsert_account_preference(user_id, "email_auth_factor", json!(email_auth_factor)) 258 264 .await 259 265 .map_err(|e| { ··· 267 273 if email_verified { 268 274 let mut authorized_via_link = false; 269 275 270 - let cache_key = email_update_cache_key(did); 276 + let cache_key = tranquil_pds::cache_keys::email_update_key(did); 271 277 if let Some(pending_json) = state.cache.get(&cache_key).await 272 278 && let Ok(pending) = serde_json::from_str::<PendingEmailUpdate>(&pending_json) 273 279 && pending.authorized ··· 336 342 } 337 343 338 344 state 339 - .user_repo 345 + .repos.user 340 346 .update_email(user_id, &new_email) 341 347 .await 342 348 .log_db_err("updating email")?; ··· 350 356 tranquil_pds::auth::verification_token::format_token_for_display(&verification_token); 351 357 let hostname = &tranquil_config::get().server.hostname; 352 358 if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_signup_verification( 353 - state.user_repo.as_ref(), 354 - state.infra_repo.as_ref(), 359 + state.repos.user.as_ref(), 360 + state.repos.infra.as_ref(), 355 361 user_id, 356 362 tranquil_db_traits::CommsChannel::Email, 357 363 &new_email, ··· 364 370 } 365 371 366 372 if let Err(e) = state 367 - .infra_repo 373 + .repos.infra 368 374 .upsert_account_preference( 369 375 user_id, 370 376 "email_auth_factor", ··· 390 396 Json(input): Json<CheckEmailVerifiedInput>, 391 397 ) -> Result<Json<VerifiedResponse>, ApiError> { 392 398 let verified = state 393 - .user_repo 399 + .repos.user 394 400 .check_email_verified_by_identifier(&input.identifier) 395 401 .await 396 402 .map_err(|e| { ··· 414 420 Json(input): Json<CheckChannelVerifiedInput>, 415 421 ) -> Result<Json<VerifiedResponse>, ApiError> { 416 422 let verified = state 417 - .user_repo 423 + .repos.user 418 424 .check_channel_verified_by_did(&input.did, input.channel) 419 425 .await 420 426 .map_err(|e| { ··· 470 476 let did = token_data.did; 471 477 info!("authorize_email_update: token valid for did={}", did); 472 478 473 - let cache_key = email_update_cache_key(&did); 474 - let pending_json = match state.cache.get(&cache_key).await { 475 - Some(json) => json, 479 + let cache_key = tranquil_pds::cache_keys::email_update_key(&did); 480 + let mut pending = match get_pending_email_update(state.cache.as_ref(), &did).await { 481 + Some(p) => p, 476 482 None => { 477 - warn!( 478 - "authorize_email_update: no pending email update in cache for did={}", 479 - did 480 - ); 483 + warn!("authorize_email_update: no pending email update in cache for did={}", did); 481 484 return ApiError::InvalidRequest("No pending email update found".into()) 482 485 .into_response(); 483 486 } 484 487 }; 485 488 486 - let mut pending: PendingEmailUpdate = match serde_json::from_str(&pending_json) { 487 - Ok(p) => p, 488 - Err(_) => { 489 - return ApiError::InternalError(None).into_response(); 490 - } 491 - }; 492 - 493 489 let token_hash = hash_token(&query.token); 494 490 if pending 495 491 .token_hash ··· 528 524 ) -> Result<Json<EmailUpdateStatusOutput>, ApiError> { 529 525 auth.check_account_scope(AccountAttr::Email, AccountAction::Read)?; 530 526 531 - let cache_key = email_update_cache_key(&auth.did); 532 - let pending_json = match state.cache.get(&cache_key).await { 533 - Some(json) => json, 527 + let pending = match get_pending_email_update(state.cache.as_ref(), &auth.did).await { 528 + Some(p) => p, 534 529 None => { 535 530 return Ok(Json(EmailUpdateStatusOutput { 536 531 pending: false, ··· 540 535 } 541 536 }; 542 537 543 - let pending: PendingEmailUpdate = match serde_json::from_str(&pending_json) { 544 - Ok(p) => p, 545 - Err(_) => { 546 - return Ok(Json(EmailUpdateStatusOutput { 547 - pending: false, 548 - authorized: false, 549 - new_email: None, 550 - })); 551 - } 552 - }; 553 - 554 538 Ok(Json(EmailUpdateStatusOutput { 555 539 pending: true, 556 540 authorized: pending.authorized, ··· 574 558 } 575 559 576 560 let count = state 577 - .user_repo 561 + .repos.user 578 562 .count_accounts_by_email(&email) 579 563 .await 580 564 .map_err(|e| {
+6 -24
crates/tranquil-api/src/server/invite.rs
··· 1 1 use axum::{Json, extract::State}; 2 - use rand::Rng; 3 2 use serde::{Deserialize, Serialize}; 4 3 use tracing::error; 5 4 use tranquil_pds::api::ApiError; ··· 7 6 use tranquil_pds::auth::{Admin, Auth, NotTakendown}; 8 7 use tranquil_pds::state::AppState; 9 8 use tranquil_pds::types::Did; 10 - 11 - const BASE32_ALPHABET: &[u8] = b"abcdefghijklmnopqrstuvwxyz234567"; 12 - 13 - pub(crate) fn gen_random_token() -> String { 14 - let mut rng = rand::thread_rng(); 15 - let gen_segment = |rng: &mut rand::rngs::ThreadRng, len: usize| -> String { 16 - (0..len) 17 - .map(|_| BASE32_ALPHABET[rng.gen_range(0..32)] as char) 18 - .collect() 19 - }; 20 - format!("{}-{}", gen_segment(&mut rng, 5), gen_segment(&mut rng, 5)) 21 - } 22 - 23 - pub fn gen_invite_code() -> String { 24 - let hostname = &tranquil_config::get().server.hostname; 25 - let hostname_prefix = hostname.replace('.', "-"); 26 - format!("{}-{}", hostname_prefix, gen_random_token()) 27 - } 9 + use tranquil_pds::util::gen_invite_code; 28 10 29 11 #[derive(Deserialize)] 30 12 #[serde(rename_all = "camelCase")] ··· 58 40 let code = gen_invite_code(); 59 41 60 42 match state 61 - .infra_repo 43 + .repos.infra 62 44 .create_invite_code(&code, input.use_count, Some(&for_account)) 63 45 .await 64 46 { ··· 115 97 }; 116 98 117 99 let admin_user_id = state 118 - .user_repo 100 + .repos.user 119 101 .get_any_admin_user_id() 120 102 .await 121 103 .log_db_err("looking up admin user")? ··· 125 107 })?; 126 108 127 109 let result = futures::future::try_join_all(for_accounts.into_iter().map(|account| { 128 - let infra_repo = state.infra_repo.clone(); 110 + let infra_repo = state.repos.infra.clone(); 129 111 let use_count = input.use_count; 130 112 async move { 131 113 let codes: Vec<String> = (0..code_count).map(|_| gen_invite_code()).collect(); ··· 192 174 let include_used = params.include_used.unwrap_or(true); 193 175 194 176 let codes_info = state 195 - .infra_repo 177 + .repos.infra 196 178 .get_invite_codes_for_account(&auth.did) 197 179 .await 198 180 .log_db_err("fetching invite codes")?; ··· 203 185 .collect(); 204 186 205 187 let codes = futures::future::join_all(filtered_codes.into_iter().map(|info| { 206 - let infra_repo = state.infra_repo.clone(); 188 + let infra_repo = state.repos.infra.clone(); 207 189 async move { 208 190 let uses = infra_repo 209 191 .get_invite_code_uses(&info.code)
+2 -2
crates/tranquil-api/src/server/logo.rs
··· 9 9 use tranquil_pds::state::AppState; 10 10 11 11 pub async fn get_logo(State(state): State<AppState>) -> Response { 12 - let logo_cid = match state.infra_repo.get_server_config("logo_cid").await { 12 + let logo_cid = match state.repos.infra.get_server_config("logo_cid").await { 13 13 Ok(cid) => cid, 14 14 Err(e) => { 15 15 error!("DB error fetching logo_cid: {:?}", e); ··· 26 26 Err(_) => return StatusCode::NOT_FOUND.into_response(), 27 27 }; 28 28 29 - let metadata = match state.blob_repo.get_blob_metadata(&cid).await { 29 + let metadata = match state.repos.blob.get_blob_metadata(&cid).await { 30 30 Ok(Some(m)) => m, 31 31 Ok(None) => return StatusCode::NOT_FOUND.into_response(), 32 32 Err(e) => {
+1 -1
crates/tranquil-api/src/server/meta.rs
··· 98 98 } 99 99 100 100 pub async fn health(State(state): State<AppState>) -> impl IntoResponse { 101 - match state.infra_repo.health_check().await { 101 + match state.repos.infra.health_check().await { 102 102 Ok(true) => ( 103 103 StatusCode::OK, 104 104 Json(HealthOutput {
+6 -6
crates/tranquil-api/src/server/migration.rs
··· 42 42 } 43 43 44 44 let user = state 45 - .user_repo 45 + .repos.user 46 46 .get_user_for_did_doc(&auth.did) 47 47 .await 48 48 .log_db_err("getting user")? ··· 97 97 let also_known_as: Option<Vec<String>> = input.also_known_as.clone(); 98 98 99 99 state 100 - .user_repo 100 + .repos.user 101 101 .upsert_did_web_overrides(user.id, verification_methods_json, also_known_as) 102 102 .await 103 103 .log_db_err("upserting did_web_overrides")?; ··· 105 105 if let Some(ref endpoint) = input.service_endpoint { 106 106 let endpoint_clean = endpoint.trim().trim_end_matches('/'); 107 107 state 108 - .user_repo 108 + .repos.user 109 109 .update_migrated_to_pds(&auth.did, endpoint_clean) 110 110 .await 111 111 .log_db_err("updating service endpoint")?; ··· 139 139 async fn build_did_document(state: &AppState, did: &tranquil_pds::types::Did) -> serde_json::Value { 140 140 let hostname = &tranquil_config::get().server.hostname; 141 141 142 - let user = match state.user_repo.get_user_for_did_doc_build(did).await { 142 + let user = match state.repos.user.get_user_for_did_doc_build(did).await { 143 143 Ok(Some(row)) => row, 144 144 _ => { 145 145 return json!({ ··· 149 149 }; 150 150 151 151 let overrides = state 152 - .user_repo 152 + .repos.user 153 153 .get_did_web_overrides(user.id) 154 154 .await 155 155 .ok() ··· 193 193 } 194 194 195 195 let key_info = state 196 - .user_repo 196 + .repos.user 197 197 .get_user_key_by_id(user.id) 198 198 .await 199 199 .ok()
+20 -20
crates/tranquil-api/src/server/passkey_account.rs
··· 120 120 } 121 121 122 122 let is_bootstrap = state.bootstrap_invite_code.is_some() 123 - && state.user_repo.count_users().await.unwrap_or(1) == 0; 123 + && state.repos.user.count_users().await.unwrap_or(1) == 0; 124 124 125 125 let _validated_invite_code = if is_bootstrap { 126 126 match input.invite_code.as_deref() { ··· 128 128 _ => return Err(ApiError::InvalidInviteCode), 129 129 } 130 130 } else if let Some(ref code) = input.invite_code { 131 - match state.infra_repo.validate_invite_code(code).await { 131 + match state.repos.infra.validate_invite_code(code).await { 132 132 Ok(validated) => Some(validated), 133 133 Err(_) => return Err(ApiError::InvalidInviteCode), 134 134 } ··· 351 351 birthdate_pref, 352 352 }; 353 353 354 - let create_result = match state.user_repo.create_passkey_account(&create_input).await { 354 + let create_result = match state.repos.user.create_passkey_account(&create_input).await { 355 355 Ok(r) => r, 356 356 Err(tranquil_db_traits::CreateAccountError::HandleTaken) => { 357 357 return Err(ApiError::HandleNotAvailable(None)); ··· 405 405 controller_did: None, 406 406 app_password_name: None, 407 407 }; 408 - if let Err(e) = state.session_repo.create_session(&session_data).await { 408 + if let Err(e) = state.repos.session.create_session(&session_data).await { 409 409 warn!(did = %did, "Failed to insert migration session: {:?}", e); 410 410 } 411 411 info!(did = %did, "Generated migration access token for BYOD passkey account"); ··· 451 451 State(state): State<AppState>, 452 452 Json(input): Json<CompletePasskeySetupInput>, 453 453 ) -> Result<Json<CompletePasskeySetupOutput>, ApiError> { 454 - let user = match state.user_repo.get_user_for_passkey_setup(&input.did).await { 454 + let user = match state.repos.user.get_user_for_passkey_setup(&input.did).await { 455 455 Ok(Some(u)) => u, 456 456 Ok(None) => { 457 457 return Err(ApiError::AccountNotFound); ··· 484 484 let webauthn = &state.webauthn_config; 485 485 486 486 let reg_state = match state 487 - .user_repo 487 + .repos.user 488 488 .load_webauthn_challenge(&input.did, WebauthnChallengeType::Registration) 489 489 .await 490 490 { ··· 530 530 } 531 531 }; 532 532 if let Err(e) = state 533 - .user_repo 533 + .repos.user 534 534 .save_passkey( 535 535 &input.did, 536 536 &credential_id, ··· 553 553 app_password_name: app_password_name.clone(), 554 554 app_password_hash: password_hash, 555 555 }; 556 - if let Err(e) = state.user_repo.complete_passkey_setup(&setup_input).await { 556 + if let Err(e) = state.repos.user.complete_passkey_setup(&setup_input).await { 557 557 error!("Error completing passkey setup: {:?}", e); 558 558 return Err(ApiError::InternalError(None)); 559 559 } 560 560 561 561 let _ = state 562 - .user_repo 562 + .repos.user 563 563 .delete_webauthn_challenge(&input.did, WebauthnChallengeType::Registration) 564 564 .await; 565 565 ··· 577 577 State(state): State<AppState>, 578 578 Json(input): Json<StartPasskeyRegistrationInput>, 579 579 ) -> Result<Json<OptionsResponse<serde_json::Value>>, ApiError> { 580 - let user = match state.user_repo.get_user_for_passkey_setup(&input.did).await { 580 + let user = match state.repos.user.get_user_for_passkey_setup(&input.did).await { 581 581 Ok(Some(u)) => u, 582 582 Ok(None) => { 583 583 return Err(ApiError::AccountNotFound); ··· 610 610 let webauthn = &state.webauthn_config; 611 611 612 612 let existing_passkeys = state 613 - .user_repo 613 + .repos.user 614 614 .get_passkeys_for_user(&input.did) 615 615 .await 616 616 .unwrap_or_default(); ··· 643 643 } 644 644 }; 645 645 if let Err(e) = state 646 - .user_repo 646 + .repos.user 647 647 .save_webauthn_challenge(&input.did, WebauthnChallengeType::Registration, &state_json) 648 648 .await 649 649 { ··· 682 682 NormalizedLoginIdentifier::normalize(&input.email, hostname_for_handles); 683 683 684 684 let user = match state 685 - .user_repo 685 + .repos.user 686 686 .get_user_for_passkey_recovery(identifier, normalized_handle.as_str()) 687 687 .await 688 688 { ··· 697 697 let expires_at = Utc::now() + Duration::hours(1); 698 698 699 699 if let Err(e) = state 700 - .user_repo 700 + .repos.user 701 701 .set_recovery_token(&user.did, &recovery_token_hash, expires_at) 702 702 .await 703 703 { ··· 714 714 ); 715 715 716 716 let _ = tranquil_pds::comms::comms_repo::enqueue_passkey_recovery( 717 - state.user_repo.as_ref(), 718 - state.infra_repo.as_ref(), 717 + state.repos.user.as_ref(), 718 + state.repos.infra.as_ref(), 719 719 user.id, 720 720 &recovery_url, 721 721 hostname, ··· 742 742 return Err(ApiError::InvalidRequest(e.to_string())); 743 743 } 744 744 745 - let user = match state.user_repo.get_user_for_recovery(&input.did).await { 745 + let user = match state.repos.user.get_user_for_recovery(&input.did).await { 746 746 Ok(Some(u)) => u, 747 747 _ => { 748 748 return Err(ApiError::InvalidRecoveryLink); ··· 771 771 password_hash, 772 772 }; 773 773 let result = match state 774 - .user_repo 774 + .repos.user 775 775 .recover_passkey_account(&recover_input) 776 776 .await 777 777 { ··· 785 785 if result.passkeys_deleted > 0 { 786 786 info!(did = %input.did, count = result.passkeys_deleted, "Deleted lost passkeys during account recovery"); 787 787 } 788 - if let Ok(Some(prefs)) = state.user_repo.get_comms_prefs(user.id).await { 788 + if let Ok(Some(prefs)) = state.repos.user.get_comms_prefs(user.id).await { 789 789 let actual_channel = 790 790 tranquil_pds::comms::resolve_delivery_channel(&prefs, user.preferred_comms_channel); 791 791 if let Err(e) = state 792 - .user_repo 792 + .repos.user 793 793 .set_channel_verified(&input.did, actual_channel) 794 794 .await 795 795 {
+10 -10
crates/tranquil-api/src/server/passkeys.rs
··· 28 28 let webauthn = &state.webauthn_config; 29 29 30 30 let handle = state 31 - .user_repo 31 + .repos.user 32 32 .get_handle_by_did(&auth.did) 33 33 .await 34 34 .log_db_err("fetching user")? 35 35 .ok_or(ApiError::AccountNotFound)?; 36 36 37 37 let existing_passkeys = state 38 - .user_repo 38 + .repos.user 39 39 .get_passkeys_for_user(&auth.did) 40 40 .await 41 41 .log_db_err("fetching existing passkeys")?; ··· 60 60 })?; 61 61 62 62 state 63 - .user_repo 63 + .repos.user 64 64 .save_webauthn_challenge(&auth.did, WebauthnChallengeType::Registration, &state_json) 65 65 .await 66 66 .log_db_err("saving registration state")?; ··· 94 94 let webauthn = &state.webauthn_config; 95 95 96 96 let reg_state_json = state 97 - .user_repo 97 + .repos.user 98 98 .load_webauthn_challenge(&auth.did, WebauthnChallengeType::Registration) 99 99 .await 100 100 .log_db_err("loading registration state")? ··· 125 125 })?; 126 126 127 127 let passkey_id = state 128 - .user_repo 128 + .repos.user 129 129 .save_passkey( 130 130 &auth.did, 131 131 passkey.cred_id(), ··· 136 136 .log_db_err("saving passkey")?; 137 137 138 138 if let Err(e) = state 139 - .user_repo 139 + .repos.user 140 140 .delete_webauthn_challenge(&auth.did, WebauthnChallengeType::Registration) 141 141 .await 142 142 { ··· 177 177 auth: Auth<Active>, 178 178 ) -> Result<Json<ListPasskeysOutput>, ApiError> { 179 179 let passkeys = state 180 - .user_repo 180 + .repos.user 181 181 .get_passkeys_for_user(&auth.did) 182 182 .await 183 183 .log_db_err("fetching passkeys")?; ··· 215 215 216 216 let id: uuid::Uuid = input.id.parse().map_err(|_| ApiError::InvalidId)?; 217 217 218 - match state.user_repo.delete_passkey(id, reauth_mfa.did()).await { 218 + match state.repos.user.delete_passkey(id, reauth_mfa.did()).await { 219 219 Ok(true) => { 220 220 info!(did = %session_mfa.did(), passkey_id = %id, "Passkey deleted"); 221 221 Ok(Json(EmptyResponse {})) ··· 243 243 let id: uuid::Uuid = input.id.parse().map_err(|_| ApiError::InvalidId)?; 244 244 245 245 match state 246 - .user_repo 246 + .repos.user 247 247 .update_passkey_name(id, &auth.did, &input.friendly_name) 248 248 .await 249 249 { ··· 260 260 } 261 261 262 262 pub async fn has_passkeys_for_user(state: &AppState, did: &tranquil_pds::types::Did) -> bool { 263 - state.user_repo.has_passkeys(did).await.unwrap_or(false) 263 + state.repos.user.has_passkeys(did).await.unwrap_or(false) 264 264 }
+21 -54
crates/tranquil-api/src/server/password.rs
··· 1 1 use axum::{Json, extract::State}; 2 - use bcrypt::{DEFAULT_COST, hash}; 3 2 use chrono::{Duration, Utc}; 4 3 use serde::Deserialize; 5 4 use tracing::{error, info, warn}; ··· 42 41 let normalized_handle = NormalizedLoginIdentifier::normalize(identifier, hostname_for_handles); 43 42 44 43 let multiple_accounts_warning = if is_email_lookup { 45 - match state.user_repo.count_accounts_by_email(normalized).await { 44 + match state.repos.user.count_accounts_by_email(normalized).await { 46 45 Ok(count) if count > 1 => Some(count), 47 46 _ => None, 48 47 } ··· 51 50 }; 52 51 53 52 let user_id = match state 54 - .user_repo 53 + .repos.user 55 54 .get_id_by_email_or_handle(normalized, normalized_handle.as_str()) 56 55 .await 57 56 { ··· 73 72 let code = generate_reset_code(); 74 73 let expires_at = Utc::now() + Duration::minutes(10); 75 74 if let Err(e) = state 76 - .user_repo 75 + .repos.user 77 76 .set_password_reset_code(user_id, &code, expires_at) 78 77 .await 79 78 { ··· 82 81 } 83 82 let hostname = &tranquil_config::get().server.hostname; 84 83 if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_password_reset( 85 - state.user_repo.as_ref(), 86 - state.infra_repo.as_ref(), 84 + state.repos.user.as_ref(), 85 + state.repos.infra.as_ref(), 87 86 user_id, 88 87 &code, 89 88 hostname, ··· 132 131 if let Err(e) = validate_password(password) { 133 132 return Err(ApiError::InvalidRequest(e.to_string())); 134 133 } 135 - let user = match state.user_repo.get_user_by_reset_code(token).await { 134 + let user = match state.repos.user.get_user_by_reset_code(token).await { 136 135 Ok(Some(u)) => u, 137 136 Ok(None) => { 138 137 return Err(ApiError::InvalidToken(None)); ··· 147 146 return Err(ApiError::InvalidToken(None)); 148 147 }; 149 148 if Utc::now() > exp { 150 - if let Err(e) = state.user_repo.clear_password_reset_code(user_id).await { 149 + if let Err(e) = state.repos.user.clear_password_reset_code(user_id).await { 151 150 error!("Failed to clear expired reset code: {:?}", e); 152 151 } 153 152 return Err(ApiError::ExpiredToken(None)); 154 153 } 155 - let password_clone = password.to_string(); 156 - let password_hash = 157 - match tokio::task::spawn_blocking(move || hash(password_clone, DEFAULT_COST)).await { 158 - Ok(Ok(h)) => h, 159 - Ok(Err(e)) => { 160 - error!("Failed to hash password: {:?}", e); 161 - return Err(ApiError::InternalError(None)); 162 - } 163 - Err(e) => { 164 - error!("Failed to spawn blocking task: {:?}", e); 165 - return Err(ApiError::InternalError(None)); 166 - } 167 - }; 154 + let password_hash = crate::common::hash_password_async(&password).await?; 168 155 let result = match state 169 - .user_repo 156 + .repos.user 170 157 .reset_password_with_sessions(user_id, &password_hash) 171 158 .await 172 159 { ··· 189 176 } 190 177 })) 191 178 .await; 192 - if let Ok(Some(prefs)) = state.user_repo.get_comms_prefs(user_id).await { 179 + if let Ok(Some(prefs)) = state.repos.user.get_comms_prefs(user_id).await { 193 180 let actual_channel = 194 181 tranquil_pds::comms::resolve_delivery_channel(&prefs, user.preferred_comms_channel); 195 182 if let Err(e) = state 196 - .user_repo 183 + .repos.user 197 184 .set_channel_verified(&user.did, actual_channel) 198 185 .await 199 186 { ··· 238 225 let password_mfa = verify_password_mfa(&state, &auth, &input.current_password).await?; 239 226 240 227 let user = state 241 - .user_repo 228 + .repos.user 242 229 .get_id_and_password_hash_by_did(password_mfa.did()) 243 230 .await 244 231 .log_db_err("in change_password")? 245 232 .ok_or(ApiError::AccountNotFound)?; 246 233 247 - let new_password_clone = input.new_password.to_string(); 248 - let new_hash = tokio::task::spawn_blocking(move || hash(new_password_clone, DEFAULT_COST)) 249 - .await 250 - .map_err(|e| { 251 - error!("Failed to spawn blocking task: {:?}", e); 252 - ApiError::InternalError(None) 253 - })? 254 - .map_err(|e| { 255 - error!("Failed to hash password: {:?}", e); 256 - ApiError::InternalError(None) 257 - })?; 234 + let new_hash = crate::common::hash_password_async(&input.new_password).await?; 258 235 259 236 state 260 - .user_repo 237 + .repos.user 261 238 .update_password_hash(user.id, &new_hash) 262 239 .await 263 240 .log_db_err("updating password")?; ··· 271 248 auth: Auth<Active>, 272 249 ) -> Result<Json<HasPasswordResponse>, ApiError> { 273 250 let has = state 274 - .user_repo 251 + .repos.user 275 252 .has_password_by_did(&auth.did) 276 253 .await 277 254 .log_db_err("checking password status")? ··· 288 265 let reauth_mfa = require_reauth_window(&state, &auth).await?; 289 266 290 267 let has_passkeys = state 291 - .user_repo 268 + .repos.user 292 269 .has_passkeys(reauth_mfa.did()) 293 270 .await 294 271 .unwrap_or(false); ··· 299 276 } 300 277 301 278 let user = state 302 - .user_repo 279 + .repos.user 303 280 .get_password_info_by_did(reauth_mfa.did()) 304 281 .await 305 282 .log_db_err("getting password info")? ··· 312 289 } 313 290 314 291 state 315 - .user_repo 292 + .repos.user 316 293 .remove_user_password(user.id) 317 294 .await 318 295 .log_db_err("removing password")?; ··· 345 322 let did = reauth_mfa.as_ref().map(|m| m.did()).unwrap_or(&auth.did); 346 323 347 324 let user = state 348 - .user_repo 325 + .repos.user 349 326 .get_password_info_by_did(did) 350 327 .await 351 328 .log_db_err("getting password info")? ··· 357 334 )); 358 335 } 359 336 360 - let new_password_clone = new_password.to_string(); 361 - let new_hash = tokio::task::spawn_blocking(move || hash(new_password_clone, DEFAULT_COST)) 362 - .await 363 - .map_err(|e| { 364 - error!("Failed to spawn blocking task: {:?}", e); 365 - ApiError::InternalError(None) 366 - })? 367 - .map_err(|e| { 368 - error!("Failed to hash password: {:?}", e); 369 - ApiError::InternalError(None) 370 - })?; 337 + let new_hash = crate::common::hash_password_async(&new_password).await?; 371 338 372 339 state 373 - .user_repo 340 + .repos.user 374 341 .set_new_user_password(user.id, &new_hash) 375 342 .await 376 343 .log_db_err("setting password")?;
+13 -13
crates/tranquil-api/src/server/reauth.rs
··· 33 33 auth: Auth<Active>, 34 34 ) -> Result<Json<ReauthStatusOutput>, ApiError> { 35 35 let last_reauth_at = state 36 - .session_repo 36 + .repos.session 37 37 .get_last_reauth_at(&auth.did) 38 38 .await 39 39 .log_db_err("getting last reauth")?; 40 40 41 41 let reauth_required = is_reauth_required(last_reauth_at); 42 - let available_methods = get_available_reauth_methods(&*state.user_repo, &auth.did).await; 42 + let available_methods = get_available_reauth_methods(&*state.repos.user, &auth.did).await; 43 43 44 44 Ok(Json(ReauthStatusOutput { 45 45 last_reauth_at, ··· 66 66 Json(input): Json<PasswordReauthInput>, 67 67 ) -> Result<Json<ReauthOutput>, ApiError> { 68 68 let password_hash = state 69 - .user_repo 69 + .repos.user 70 70 .get_password_hash_by_did(&auth.did) 71 71 .await 72 72 .log_db_err("fetching password hash")? ··· 76 76 77 77 if !password_valid { 78 78 let app_password_hashes = state 79 - .session_repo 79 + .repos.session 80 80 .get_app_password_hashes_by_did(&auth.did) 81 81 .await 82 82 .unwrap_or_default(); ··· 91 91 } 92 92 } 93 93 94 - let reauthed_at = update_last_reauth_cached(&*state.session_repo, &state.cache, &auth.did) 94 + let reauthed_at = update_last_reauth_cached(&*state.repos.session, &state.cache, &auth.did) 95 95 .await 96 96 .log_db_err("updating reauth")?; 97 97 ··· 127 127 ))); 128 128 } 129 129 130 - let reauthed_at = update_last_reauth_cached(&*state.session_repo, &state.cache, &auth.did) 130 + let reauthed_at = update_last_reauth_cached(&*state.repos.session, &state.cache, &auth.did) 131 131 .await 132 132 .log_db_err("updating reauth")?; 133 133 ··· 146 146 auth: Auth<Active>, 147 147 ) -> Result<Json<PasskeyReauthStartOutput>, ApiError> { 148 148 let stored_passkeys = state 149 - .user_repo 149 + .repos.user 150 150 .get_passkeys_for_user(&auth.did) 151 151 .await 152 152 .log_db_err("getting passkeys")?; ··· 179 179 })?; 180 180 181 181 state 182 - .user_repo 182 + .repos.user 183 183 .save_webauthn_challenge( 184 184 &auth.did, 185 185 WebauthnChallengeType::Authentication, ··· 204 204 Json(input): Json<PasskeyReauthFinishInput>, 205 205 ) -> Result<Json<ReauthOutput>, ApiError> { 206 206 let auth_state_json = state 207 - .user_repo 207 + .repos.user 208 208 .load_webauthn_challenge(&auth.did, WebauthnChallengeType::Authentication) 209 209 .await 210 210 .log_db_err("loading authentication state")? ··· 232 232 233 233 let cred_id_bytes = auth_result.cred_id().as_ref(); 234 234 match state 235 - .user_repo 235 + .repos.user 236 236 .update_passkey_counter( 237 237 cred_id_bytes, 238 238 i32::try_from(auth_result.counter()).unwrap_or(i32::MAX), ··· 242 242 Ok(false) => { 243 243 warn!(did = %&auth.did, "Passkey counter anomaly detected - possible cloned key"); 244 244 let _ = state 245 - .user_repo 245 + .repos.user 246 246 .delete_webauthn_challenge(&auth.did, WebauthnChallengeType::Authentication) 247 247 .await; 248 248 return Err(ApiError::PasskeyCounterAnomaly); ··· 254 254 } 255 255 256 256 let _ = state 257 - .user_repo 257 + .repos.user 258 258 .delete_webauthn_challenge(&auth.did, WebauthnChallengeType::Authentication) 259 259 .await; 260 260 261 - let reauthed_at = update_last_reauth_cached(&*state.session_repo, &state.cache, &auth.did) 261 + let reauthed_at = update_last_reauth_cached(&*state.repos.session, &state.cache, &auth.did) 262 262 .await 263 263 .log_db_err("updating reauth")?; 264 264
+1 -1
crates/tranquil-api/src/server/service_auth.rs
··· 72 72 Some(kb) => kb.clone(), 73 73 None => { 74 74 warn!(did = %&auth.did, "getServiceAuth: no key_bytes in auth, fetching from DB"); 75 - match state.user_repo.get_user_info_by_did(&auth.did).await { 75 + match state.repos.user.get_user_info_by_did(&auth.did).await { 76 76 Ok(Some(info)) => match info.key_bytes { 77 77 Some(key_bytes_enc) => { 78 78 match tranquil_pds::config::decrypt_key(
+32 -32
crates/tranquil-api/src/server/session.rs
··· 69 69 input.identifier, normalized_identifier 70 70 ); 71 71 let row = match state 72 - .user_repo 72 + .repos.user 73 73 .get_login_full_by_identifier(normalized_identifier.as_str()) 74 74 .await 75 75 { ··· 98 98 } 99 99 }; 100 100 let credential = crate::common::verify_credential( 101 - state.session_repo.as_ref(), 101 + state.repos.session.as_ref(), 102 102 row.id, 103 103 &input.password, 104 104 row.password_hash.as_deref(), ··· 130 130 } 131 131 let is_verified = row.channel_verification.has_any_verified(); 132 132 let is_delegated = state 133 - .delegation_repo 133 + .repos.delegation 134 134 .is_delegated_account(&row.did) 135 135 .await 136 136 .unwrap_or(false); ··· 181 181 Ok(tranquil_pds::auth::legacy_2fa::Legacy2faOutcome::ChallengeSent(code)) => { 182 182 let hostname = &tranquil_config::get().server.hostname; 183 183 if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_2fa_code( 184 - state.user_repo.as_ref(), 185 - state.infra_repo.as_ref(), 184 + state.repos.user.as_ref(), 185 + state.repos.infra.as_ref(), 186 186 row.id, 187 187 code.as_str(), 188 188 hostname, ··· 269 269 app_password_name: app_password_name.clone(), 270 270 }; 271 271 let (insert_result, did_doc) = tokio::join!( 272 - state.session_repo.create_session(&session_data), 272 + state.repos.session.create_session(&session_data), 273 273 did_resolver.resolve_did_document(&did_for_doc) 274 274 ); 275 275 if let Err(e) = insert_result { ··· 284 284 ); 285 285 let hostname = &tranquil_config::get().server.hostname; 286 286 if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_legacy_login( 287 - state.user_repo.as_ref(), 288 - state.infra_repo.as_ref(), 287 + state.repos.user.as_ref(), 288 + state.repos.infra.as_ref(), 289 289 row.id, 290 290 hostname, 291 291 client_ip, ··· 359 359 let did_for_doc = auth.did.clone(); 360 360 let did_resolver = state.did_resolver.clone(); 361 361 let (db_result, did_doc) = tokio::join!( 362 - state.user_repo.get_session_info_by_did(&auth.did), 362 + state.repos.user.get_session_info_by_did(&auth.did), 363 363 did_resolver.resolve_did_document(&did_for_doc) 364 364 ); 365 365 match db_result { ··· 422 422 ) -> Result<Json<EmptyResponse>, ApiError> { 423 423 let jti = tranquil_pds::auth::extract_jti_from_headers(&headers) 424 424 .ok_or(ApiError::AuthenticationRequired)?; 425 - match state.session_repo.delete_session_by_access_jti(&jti).await { 425 + match state.repos.session.delete_session_by_access_jti(&jti).await { 426 426 Ok(rows) if rows > 0 => { 427 427 let session_cache_key = tranquil_pds::cache_keys::session_key(&auth.did, &jti); 428 428 let _ = state.cache.delete(&session_cache_key).await; ··· 476 476 } 477 477 }; 478 478 if let Ok(Some(_)) = state 479 - .session_repo 479 + .repos.session 480 480 .check_refresh_token_used(&refresh_jti) 481 481 .await 482 482 { ··· 486 486 ))); 487 487 } 488 488 let session_row = match state 489 - .session_repo 489 + .repos.session 490 490 .get_session_for_refresh(&refresh_jti) 491 491 .await 492 492 { ··· 548 548 new_refresh_expires_at: new_refresh_meta.expires_at, 549 549 }; 550 550 match state 551 - .session_repo 551 + .repos.session 552 552 .refresh_session_atomic(&refresh_data) 553 553 .await 554 554 { ··· 576 576 let did_for_doc = session_row.did.clone(); 577 577 let did_resolver = state.did_resolver.clone(); 578 578 let (db_result, did_doc) = tokio::join!( 579 - state.user_repo.get_session_info_by_did(&session_row.did), 579 + state.repos.user.get_session_info_by_did(&session_row.did), 580 580 did_resolver.resolve_did_document(&did_for_doc) 581 581 ); 582 582 match db_result { ··· 639 639 Json(input): Json<ConfirmSignupInput>, 640 640 ) -> Result<Json<ConfirmSignupOutput>, ApiError> { 641 641 info!("confirm_signup called for DID: {}", input.did); 642 - let row = match state.user_repo.get_confirm_signup_by_did(&input.did).await { 642 + let row = match state.repos.user.get_confirm_signup_by_did(&input.did).await { 643 643 Ok(Some(row)) => row, 644 644 Ok(None) => { 645 645 warn!("User not found for confirm_signup: {}", input.did); ··· 702 702 }; 703 703 704 704 if let Err(e) = state 705 - .user_repo 705 + .repos.user 706 706 .set_channel_verified(&input.did, row.channel) 707 707 .await 708 708 { ··· 726 726 727 727 let hostname = &tranquil_config::get().server.hostname; 728 728 if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_welcome( 729 - state.user_repo.as_ref(), 730 - state.infra_repo.as_ref(), 729 + state.repos.user.as_ref(), 730 + state.repos.infra.as_ref(), 731 731 row.id, 732 732 hostname, 733 733 ) ··· 757 757 pub async fn auto_resend_verification(state: &AppState, did: &Did) -> Option<AutoResendResult> { 758 758 let debounce_key = tranquil_pds::cache_keys::auto_verify_sent_key(did.as_str()); 759 759 let debounced = state.cache.get(&debounce_key).await.is_some(); 760 - let row = match state.user_repo.get_resend_verification_by_did(did).await { 760 + let row = match state.repos.user.get_resend_verification_by_did(did).await { 761 761 Ok(Some(row)) => row, 762 762 Ok(None) => return None, 763 763 Err(e) => { ··· 821 821 ) -> Result<Json<SuccessResponse>, ApiError> { 822 822 info!("resend_verification called for DID: {}", input.did); 823 823 let row = match state 824 - .user_repo 824 + .repos.user 825 825 .get_resend_verification_by_did(&input.did) 826 826 .await 827 827 { ··· 895 895 let current_jti = tranquil_pds::auth::extract_jti_from_headers(&headers); 896 896 897 897 let jwt_rows = state 898 - .session_repo 898 + .repos.session 899 899 .list_sessions_by_did(&auth.did) 900 900 .await 901 901 .log_db_err("fetching JWT sessions")?; 902 902 903 903 let oauth_rows = state 904 - .oauth_repo 904 + .repos.oauth 905 905 .list_sessions_by_did(&auth.did) 906 906 .await 907 907 .log_db_err("fetching OAuth sessions")?; ··· 962 962 .map(SessionId::new) 963 963 .map_err(|_| ApiError::InvalidRequest("Invalid session ID".into()))?; 964 964 let access_jti = state 965 - .session_repo 965 + .repos.session 966 966 .get_session_access_jti_by_id(session_id, &auth.did) 967 967 .await 968 968 .log_db_err("in revoke_session")? 969 969 .ok_or(ApiError::SessionNotFound)?; 970 970 state 971 - .session_repo 971 + .repos.session 972 972 .delete_session_by_id(session_id) 973 973 .await 974 974 .log_db_err("deleting session")?; ··· 983 983 .map(TokenFamilyId::new) 984 984 .map_err(|_| ApiError::InvalidRequest("Invalid session ID".into()))?; 985 985 let deleted = state 986 - .oauth_repo 986 + .repos.oauth 987 987 .delete_session_by_id(session_id, &auth.did) 988 988 .await 989 989 .log_db_err("deleting OAuth session")?; ··· 1007 1007 1008 1008 if auth.is_oauth() { 1009 1009 state 1010 - .session_repo 1010 + .repos.session 1011 1011 .delete_sessions_by_did(&auth.did) 1012 1012 .await 1013 1013 .log_db_err("revoking JWT sessions")?; 1014 1014 let jti_typed = TokenId::from(jti.clone()); 1015 1015 state 1016 - .oauth_repo 1016 + .repos.oauth 1017 1017 .delete_sessions_by_did_except(&auth.did, &jti_typed) 1018 1018 .await 1019 1019 .log_db_err("revoking OAuth sessions")?; 1020 1020 } else { 1021 1021 state 1022 - .session_repo 1022 + .repos.session 1023 1023 .delete_sessions_by_did_except_jti(&auth.did, &jti) 1024 1024 .await 1025 1025 .log_db_err("revoking JWT sessions")?; 1026 1026 state 1027 - .oauth_repo 1027 + .repos.oauth 1028 1028 .delete_sessions_by_did(&auth.did) 1029 1029 .await 1030 1030 .log_db_err("revoking OAuth sessions")?; ··· 1046 1046 auth: Auth<Active>, 1047 1047 ) -> Result<Json<LegacyLoginPreferenceOutput>, ApiError> { 1048 1048 let pref = state 1049 - .user_repo 1049 + .repos.user 1050 1050 .get_legacy_login_pref(&auth.did) 1051 1051 .await 1052 1052 .log_db_err("getting legacy login pref")? ··· 1079 1079 let reauth_mfa = require_reauth_window(&state, &auth).await?; 1080 1080 1081 1081 let updated = state 1082 - .user_repo 1082 + .repos.user 1083 1083 .update_legacy_login(reauth_mfa.did(), input.allow_legacy_login) 1084 1084 .await 1085 1085 .log_db_err("updating legacy login")?; ··· 1117 1117 } 1118 1118 1119 1119 let updated = state 1120 - .user_repo 1120 + .repos.user 1121 1121 .update_locale(&auth.did, &input.preferred_locale) 1122 1122 .await 1123 1123 .log_db_err("updating locale")?;
+1 -1
crates/tranquil-api/src/server/signing_key.rs
··· 44 44 let expires_at = Utc::now() + Duration::hours(24); 45 45 let private_bytes: &[u8] = &private_key_bytes; 46 46 match state 47 - .infra_repo 47 + .repos.infra 48 48 .reserve_signing_key( 49 49 input.did.as_ref(), 50 50 &public_key_did_key,
+14 -14
crates/tranquil-api/src/server/totp.rs
··· 29 29 ) -> Result<Json<CreateTotpSecretOutput>, ApiError> { 30 30 use tranquil_db_traits::TotpRecordState; 31 31 32 - match state.user_repo.get_totp_record_state(&auth.did).await { 32 + match state.repos.user.get_totp_record_state(&auth.did).await { 33 33 Ok(Some(TotpRecordState::Verified(_))) => return Err(ApiError::TotpAlreadyEnabled), 34 34 Ok(Some(TotpRecordState::Unverified(_))) | Ok(None) => {} 35 35 Err(e) => { ··· 41 41 let secret = generate_totp_secret(); 42 42 43 43 let handle = state 44 - .user_repo 44 + .repos.user 45 45 .get_handle_by_did(&auth.did) 46 46 .await 47 47 .log_db_err("fetching handle")? ··· 61 61 })?; 62 62 63 63 state 64 - .user_repo 64 + .repos.user 65 65 .upsert_totp_secret(&auth.did, &encrypted_secret, ENCRYPTION_VERSION) 66 66 .await 67 67 .log_db_err("storing TOTP secret")?; ··· 102 102 ) 103 103 .await?; 104 104 105 - let unverified_record = match state.user_repo.get_totp_record_state(&auth.did).await { 105 + let unverified_record = match state.repos.user.get_totp_record_state(&auth.did).await { 106 106 Ok(Some(TotpRecordState::Unverified(record))) => record, 107 107 Ok(Some(TotpRecordState::Verified(_))) => return Err(ApiError::TotpAlreadyEnabled), 108 108 Ok(None) => return Err(ApiError::TotpNotEnabled), ··· 139 139 })?; 140 140 141 141 state 142 - .user_repo 142 + .repos.user 143 143 .enable_totp_with_backup_codes(&auth.did, &backup_hashes) 144 144 .await 145 145 .log_db_err("enabling TOTP")?; ··· 173 173 let totp_mfa = verify_totp_mfa(&state, &auth, &input.code).await?; 174 174 175 175 state 176 - .user_repo 176 + .repos.user 177 177 .delete_totp_and_backup_codes(totp_mfa.did()) 178 178 .await 179 179 .log_db_err("deleting TOTP")?; ··· 199 199 ) -> Result<Json<GetTotpStatusOutput>, ApiError> { 200 200 use tranquil_db_traits::TotpRecordState; 201 201 202 - let enabled = match state.user_repo.get_totp_record_state(&auth.did).await { 202 + let enabled = match state.repos.user.get_totp_record_state(&auth.did).await { 203 203 Ok(Some(TotpRecordState::Verified(_))) => true, 204 204 Ok(Some(TotpRecordState::Unverified(_))) | Ok(None) => false, 205 205 Err(e) => { ··· 209 209 }; 210 210 211 211 let backup_count = state 212 - .user_repo 212 + .repos.user 213 213 .count_unused_backup_codes(&auth.did) 214 214 .await 215 215 .log_db_err("counting backup codes")?; ··· 259 259 })?; 260 260 261 261 state 262 - .user_repo 262 + .repos.user 263 263 .replace_backup_codes(totp_mfa.did(), &backup_hashes) 264 264 .await 265 265 .log_db_err("replacing backup codes")?; ··· 276 276 ) -> bool { 277 277 let code = code.trim().to_uppercase(); 278 278 279 - let backup_codes = match state.user_repo.get_unused_backup_codes(did).await { 279 + let backup_codes = match state.repos.user.get_unused_backup_codes(did).await { 280 280 Ok(codes) => codes, 281 281 Err(e) => { 282 282 warn!("Failed to fetch backup codes: {:?}", e); ··· 290 290 291 291 match matched { 292 292 Some(row) => { 293 - let _ = state.user_repo.mark_backup_code_used(row.id).await; 293 + let _ = state.repos.user.mark_backup_code_used(row.id).await; 294 294 true 295 295 } 296 296 None => false, ··· 310 310 return verify_backup_code_for_user(state, did, code).await; 311 311 } 312 312 313 - let verified_record = match state.user_repo.get_totp_record_state(did).await { 313 + let verified_record = match state.repos.user.get_totp_record_state(did).await { 314 314 Ok(Some(TotpRecordState::Verified(record))) => record, 315 315 _ => return false, 316 316 }; ··· 324 324 }; 325 325 326 326 if verify_totp_code(&secret, code) { 327 - let _ = state.user_repo.update_totp_last_used(did).await; 327 + let _ = state.repos.user.update_totp_last_used(did).await; 328 328 return true; 329 329 } 330 330 ··· 332 332 } 333 333 334 334 pub async fn has_totp_enabled(state: &AppState, did: &tranquil_pds::types::Did) -> bool { 335 - state.user_repo.has_totp_enabled(did).await.unwrap_or(false) 335 + state.repos.user.has_totp_enabled(did).await.unwrap_or(false) 336 336 }
+5 -5
crates/tranquil-api/src/server/trusted_devices.rs
··· 72 72 auth: Auth<Active>, 73 73 ) -> Result<Json<ListTrustedDevicesOutput>, ApiError> { 74 74 let rows = state 75 - .oauth_repo 75 + .repos.oauth 76 76 .list_trusted_devices(&auth.did) 77 77 .await 78 78 .log_db_err("listing trusted devices")?; ··· 108 108 Json(input): Json<RevokeTrustedDeviceInput>, 109 109 ) -> Result<Json<SuccessResponse>, ApiError> { 110 110 match state 111 - .oauth_repo 111 + .repos.oauth 112 112 .device_belongs_to_user(&input.device_id, &auth.did) 113 113 .await 114 114 { ··· 123 123 } 124 124 125 125 state 126 - .oauth_repo 126 + .repos.oauth 127 127 .revoke_device_trust(&input.device_id) 128 128 .await 129 129 .log_db_err("revoking device trust")?; ··· 145 145 Json(input): Json<UpdateTrustedDeviceInput>, 146 146 ) -> Result<Json<SuccessResponse>, ApiError> { 147 147 match state 148 - .oauth_repo 148 + .repos.oauth 149 149 .device_belongs_to_user(&input.device_id, &auth.did) 150 150 .await 151 151 { ··· 160 160 } 161 161 162 162 state 163 - .oauth_repo 163 + .repos.oauth 164 164 .update_device_friendly_name(&input.device_id, input.friendly_name.as_deref()) 165 165 .await 166 166 .log_db_err("updating device friendly name")?;
+1 -1
crates/tranquil-api/src/server/verify_email.rs
··· 59 59 .unwrap_or(tranquil_db_traits::CommsChannel::Email); 60 60 let identifier = input.identifier.trim().to_lowercase(); 61 61 62 - let user = match state.user_repo.get_by_email(&identifier).await { 62 + let user = match state.repos.user.get_by_email(&identifier).await { 63 63 Ok(Some(u)) => u, 64 64 Ok(None) => { 65 65 return Ok(Json(ResendMigrationVerificationOutput { sent: true }));
+30 -42
crates/tranquil-api/src/server/verify_token.rs
··· 79 79 identifier: &str, 80 80 ) -> Result<Json<VerifyTokenOutput>, ApiError> { 81 81 let user = state 82 - .user_repo 82 + .repos.user 83 83 .get_verification_info(did) 84 84 .await 85 85 .log_db_err("during migration verification")? ··· 92 92 } 93 93 if !user.channel_verification.email { 94 94 state 95 - .user_repo 95 + .repos.user 96 96 .set_email_verified_flag(user.id) 97 97 .await 98 98 .log_db_err("updating email_verified status")?; 99 99 } 100 100 } 101 - _ => common::set_channel_verified_flag(state.user_repo.as_ref(), user.id, channel).await?, 101 + _ => common::set_channel_verified_flag(state.repos.user.as_ref(), user.id, channel).await?, 102 102 }; 103 103 104 104 info!(did = %did, channel = ?channel, "Migration verification completed successfully"); ··· 118 118 identifier: &str, 119 119 ) -> Result<Json<VerifyTokenOutput>, ApiError> { 120 120 let user_id = state 121 - .user_repo 121 + .repos.user 122 122 .get_id_by_did(did) 123 123 .await 124 124 .log_db_err("fetching user id")? ··· 127 127 match channel { 128 128 CommsChannel::Email => { 129 129 let success = state 130 - .user_repo 130 + .repos.user 131 131 .verify_email_channel(user_id, identifier) 132 132 .await 133 133 .log_db_err("updating email channel")?; ··· 137 137 } 138 138 CommsChannel::Discord => { 139 139 state 140 - .user_repo 140 + .repos.user 141 141 .verify_discord_channel(user_id, identifier) 142 142 .await 143 143 .log_db_err("updating discord channel")?; 144 144 } 145 145 CommsChannel::Telegram => { 146 146 state 147 - .user_repo 147 + .repos.user 148 148 .verify_telegram_channel(user_id, identifier) 149 149 .await 150 150 .log_db_err("updating telegram channel")?; 151 151 } 152 152 CommsChannel::Signal => { 153 153 state 154 - .user_repo 154 + .repos.user 155 155 .verify_signal_channel(user_id, identifier) 156 156 .await 157 157 .log_db_err("updating signal channel")?; ··· 160 160 161 161 info!(did = %did, channel = ?channel, "Channel verified successfully"); 162 162 163 - let recipient = resolve_verified_recipient(state, user_id, channel, identifier).await; 164 - if let Err(e) = comms_repo::enqueue_channel_verified( 165 - state.user_repo.as_ref(), 166 - state.infra_repo.as_ref(), 167 - user_id, 168 - channel, 169 - &recipient, 170 - &tranquil_config::get().server.hostname, 171 - ) 172 - .await 173 - { 174 - warn!(error = %e, "Failed to enqueue channel verified notification"); 175 - } 163 + notify_channel_verified(state, user_id, channel, identifier).await; 176 164 177 165 Ok(Json(VerifyTokenOutput { 178 166 success: true, ··· 182 170 })) 183 171 } 184 172 185 - async fn resolve_verified_recipient( 173 + async fn notify_channel_verified( 186 174 state: &AppState, 187 175 user_id: uuid::Uuid, 188 - channel: tranquil_db_traits::CommsChannel, 176 + channel: CommsChannel, 189 177 identifier: &str, 190 - ) -> String { 191 - match channel { 192 - tranquil_db_traits::CommsChannel::Telegram => state 193 - .user_repo 178 + ) { 179 + let recipient = match channel { 180 + CommsChannel::Telegram => state 181 + .repos.user 194 182 .get_telegram_chat_id(user_id) 195 183 .await 196 184 .ok() ··· 198 186 .map(|id| id.to_string()) 199 187 .unwrap_or_else(|| identifier.to_string()), 200 188 _ => identifier.to_string(), 189 + }; 190 + if let Err(e) = comms_repo::enqueue_channel_verified( 191 + state.repos.user.as_ref(), 192 + state.repos.infra.as_ref(), 193 + user_id, 194 + channel, 195 + &recipient, 196 + &tranquil_config::get().server.hostname, 197 + ) 198 + .await 199 + { 200 + warn!(error = %e, "Failed to enqueue channel verified notification"); 201 201 } 202 202 } 203 203 ··· 208 208 identifier: &str, 209 209 ) -> Result<Json<VerifyTokenOutput>, ApiError> { 210 210 let user = state 211 - .user_repo 211 + .repos.user 212 212 .get_verification_info(did) 213 213 .await 214 214 .log_db_err("during signup verification")? ··· 225 225 })); 226 226 } 227 227 228 - common::set_channel_verified_flag(state.user_repo.as_ref(), user.id, channel).await?; 228 + common::set_channel_verified_flag(state.repos.user.as_ref(), user.id, channel).await?; 229 229 230 230 info!(did = %did, channel = ?channel, "Signup verified successfully"); 231 231 232 - let recipient = resolve_verified_recipient(state, user.id, channel, identifier).await; 233 - if let Err(e) = comms_repo::enqueue_channel_verified( 234 - state.user_repo.as_ref(), 235 - state.infra_repo.as_ref(), 236 - user.id, 237 - channel, 238 - &recipient, 239 - &tranquil_config::get().server.hostname, 240 - ) 241 - .await 242 - { 243 - warn!(error = %e, "Failed to enqueue channel verified notification"); 244 - } 232 + notify_channel_verified(state, user.id, channel, identifier).await; 245 233 246 234 Ok(Json(VerifyTokenOutput { 247 235 success: true,
+3 -3
crates/tranquil-api/src/telegram_webhook.rs
··· 71 71 "Received /start from Telegram user" 72 72 ); 73 73 match state 74 - .user_repo 74 + .repos.user 75 75 .store_telegram_chat_id(&username, from.id, handle.as_deref()) 76 76 .await 77 77 { ··· 82 82 "Verified Telegram user and stored chat_id" 83 83 ); 84 84 if let Err(e) = comms_repo::enqueue_channel_verified( 85 - state.user_repo.as_ref(), 86 - state.infra_repo.as_ref(), 85 + state.repos.user.as_ref(), 86 + state.repos.infra.as_ref(), 87 87 user_id, 88 88 tranquil_db_traits::CommsChannel::Telegram, 89 89 &from.id.to_string(),
+7 -7
crates/tranquil-oauth-server/src/endpoints/delegation.rs
··· 25 25 async fn get_auth_request(state: &AppState, request_uri: &str) -> Result<RequestData, Response> { 26 26 let request_id = RequestId::from(request_uri.to_string()); 27 27 match state 28 - .oauth_repo 28 + .repos.oauth 29 29 .get_authorization_request(&request_id) 30 30 .await 31 31 { ··· 43 43 controller_did: &Did, 44 44 ) -> Result<tranquil_db_traits::DelegationGrant, Response> { 45 45 match state 46 - .delegation_repo 46 + .repos.delegation 47 47 .get_delegation(delegated_did, controller_did) 48 48 .await 49 49 { ··· 65 65 user_agent: Option<&str>, 66 66 ) -> Response { 67 67 let _ = state 68 - .delegation_repo 68 + .repos.delegation 69 69 .log_delegation_action( 70 70 delegated_did, 71 71 controller_did, ··· 87 87 ) -> Result<(), Response> { 88 88 let request_id = RequestId::from(request_uri.to_string()); 89 89 state 90 - .oauth_repo 90 + .repos.oauth 91 91 .set_request_did(&request_id, delegated_did) 92 92 .await 93 93 .map_err(|_| DelegationAuthResponse::err("Failed to update authorization request"))?; 94 94 state 95 - .oauth_repo 95 + .repos.oauth 96 96 .set_controller_did(&request_id, controller_did) 97 97 .await 98 98 .map_err(|_| DelegationAuthResponse::err("Failed to update authorization request"))?; ··· 211 211 212 212 let is_cross_pds = form.auth_method.as_deref() == Some("cross_pds"); 213 213 let controller_local = state 214 - .user_repo 214 + .repos.user 215 215 .get_auth_info_by_did(&controller_did) 216 216 .await 217 217 .ok() ··· 562 562 } 563 563 564 564 let _ = state 565 - .delegation_repo 565 + .repos.delegation 566 566 .log_delegation_action( 567 567 delegated_did, 568 568 controller_did,
+2 -2
crates/tranquil-oauth-server/src/endpoints/par.rs
··· 115 115 }; 116 116 let request_id_typed = RequestIdType::from(request_id.0.clone()); 117 117 state 118 - .oauth_repo 118 + .repos.oauth 119 119 .create_authorization_request(&request_id_typed, &request_data) 120 120 .await 121 121 .map_err(tranquil_pds::oauth::db_err_to_oauth)?; 122 122 tokio::spawn({ 123 - let oauth_repo = state.oauth_repo.clone(); 123 + let oauth_repo = state.repos.oauth.clone(); 124 124 async move { 125 125 if let Err(e) = oauth_repo.delete_expired_authorization_requests().await { 126 126 tracing::warn!("Failed to cleanup expired authorization requests: {:?}", e);
+10 -10
crates/tranquil-oauth-server/src/endpoints/token/grants.rs
··· 47 47 }; 48 48 let auth_code = AuthorizationCode::from(code); 49 49 let auth_request = state 50 - .oauth_repo 50 + .repos.oauth 51 51 .consume_authorization_request_by_code(&auth_code) 52 52 .await 53 53 .map_err(tranquil_pds::oauth::db_err_to_oauth)? ··· 104 104 let token_endpoint = format!("https://{}/oauth/token", pds_hostname); 105 105 let result = verifier.verify_proof(proof, Method::POST.as_str(), &token_endpoint, None)?; 106 106 if !state 107 - .oauth_repo 107 + .repos.oauth 108 108 .check_and_record_dpop_jti(&result.jti) 109 109 .await 110 110 .map_err(tranquil_pds::oauth::db_err_to_oauth)? ··· 140 140 .parse() 141 141 .map_err(|_| OAuthError::InvalidRequest("Invalid controller DID format".to_string()))?; 142 142 let grant = state 143 - .delegation_repo 143 + .repos.delegation 144 144 .get_delegation(&did_parsed, &controller_parsed) 145 145 .await 146 146 .ok() ··· 200 200 controller_did: controller_did.clone(), 201 201 }; 202 202 state 203 - .oauth_repo 203 + .repos.oauth 204 204 .create_token(&token_data) 205 205 .await 206 206 .map_err(tranquil_pds::oauth::db_err_to_oauth)?; ··· 211 211 "Authorization code grant completed, token created" 212 212 ); 213 213 tokio::spawn({ 214 - let oauth_repo = state.oauth_repo.clone(); 214 + let oauth_repo = state.repos.oauth.clone(); 215 215 let did_clone = did.clone(); 216 216 async move { 217 217 if let Ok(did_typed) = did_clone.parse::<tranquil_types::Did>() ··· 267 267 ); 268 268 269 269 let refresh_token_typed = RefreshTokenType::from(refresh_token_str.clone()); 270 - let lookup = lookup_refresh_token(state.oauth_repo.as_ref(), &refresh_token_typed).await?; 270 + let lookup = lookup_refresh_token(state.repos.oauth.as_ref(), &refresh_token_typed).await?; 271 271 let token_state = lookup.state(); 272 272 tracing::debug!(state = %token_state, "Refresh token state"); 273 273 ··· 320 320 "Refresh token reuse detected, revoking token family" 321 321 ); 322 322 state 323 - .oauth_repo 323 + .repos.oauth 324 324 .delete_token_family(original_token_id) 325 325 .await 326 326 .map_err(tranquil_pds::oauth::db_err_to_oauth)?; ··· 331 331 RefreshTokenLookup::Expired { db_id } => { 332 332 tracing::warn!(refresh_token_prefix = %token_prefix, "Refresh token has expired"); 333 333 state 334 - .oauth_repo 334 + .repos.oauth 335 335 .delete_token_family(db_id) 336 336 .await 337 337 .map_err(tranquil_pds::oauth::db_err_to_oauth)?; ··· 353 353 let token_endpoint = format!("https://{}/oauth/token", pds_hostname); 354 354 let result = verifier.verify_proof(proof, Method::POST.as_str(), &token_endpoint, None)?; 355 355 if !state 356 - .oauth_repo 356 + .repos.oauth 357 357 .check_and_record_dpop_jti(&result.jti) 358 358 .await 359 359 .map_err(tranquil_pds::oauth::db_err_to_oauth)? ··· 386 386 let new_expires_at = Utc::now() + Duration::days(refresh_expiry_days); 387 387 let new_refresh_typed = RefreshTokenType::from(new_refresh_token.0.clone()); 388 388 state 389 - .oauth_repo 389 + .repos.oauth 390 390 .rotate_token(db_id, &new_refresh_typed, new_expires_at) 391 391 .await 392 392 .map_err(tranquil_pds::oauth::db_err_to_oauth)?;
+4 -4
crates/tranquil-oauth-server/src/endpoints/token/introspect.rs
··· 24 24 if let Some(token) = &request.token { 25 25 let refresh_token = RefreshToken::from(token.clone()); 26 26 if let Some((db_id, _)) = state 27 - .oauth_repo 27 + .repos.oauth 28 28 .get_token_by_refresh_token(&refresh_token) 29 29 .await 30 30 .map_err(tranquil_pds::oauth::db_err_to_oauth)? 31 31 { 32 32 state 33 - .oauth_repo 33 + .repos.oauth 34 34 .delete_token_family(db_id) 35 35 .await 36 36 .map_err(tranquil_pds::oauth::db_err_to_oauth)?; 37 37 } else { 38 38 let token_id = TokenId::from(token.clone()); 39 39 state 40 - .oauth_repo 40 + .repos.oauth 41 41 .delete_token(&token_id) 42 42 .await 43 43 .map_err(tranquil_pds::oauth::db_err_to_oauth)?; ··· 104 104 Err(_) => return Ok(Json(inactive_response)), 105 105 }; 106 106 let token_id = TokenId::from(token_info.sid.clone()); 107 - let token_data = match state.oauth_repo.get_token_by_id(&token_id).await { 107 + let token_data = match state.repos.oauth.get_token_by_id(&token_id).await { 108 108 Ok(Some(data)) => data, 109 109 _ => return Ok(Json(inactive_response)), 110 110 };
+37 -42
crates/tranquil-oauth-server/src/sso_endpoints.rs
··· 100 100 let extracted = 101 101 extract_auth_token_from_header(auth_header).ok_or(ApiError::SsoNotAuthenticated)?; 102 102 let auth_user = validate_bearer_token_cached( 103 - state.user_repo.as_ref(), 103 + state.repos.user.as_ref(), 104 104 state.cache.as_ref(), 105 105 &extracted.token, 106 106 ) ··· 112 112 _ => { 113 113 let request_id = RequestId::new(request_uri.clone()); 114 114 let _request_data = state 115 - .oauth_repo 115 + .repos.oauth 116 116 .get_authorization_request(&request_id) 117 117 .await? 118 118 .ok_or(ApiError::InvalidRequest( ··· 135 135 })?; 136 136 137 137 state 138 - .sso_repo 138 + .repos.sso 139 139 .create_sso_auth_state( 140 140 &sso_state, 141 141 &request_uri, ··· 230 230 _ => return redirect_to_error("Missing code or state parameter"), 231 231 }; 232 232 233 - let auth_state = match state.sso_repo.consume_sso_auth_state(&sso_state).await { 233 + let auth_state = match state.repos.sso.consume_sso_auth_state(&sso_state).await { 234 234 Ok(Some(s)) => s, 235 235 Ok(None) => return redirect_to_error("SSO session expired or invalid"), 236 236 Err(e) => { ··· 363 363 user_info: &tranquil_pds::sso::providers::SsoUserInfo, 364 364 ) -> Response { 365 365 let identity = match state 366 - .sso_repo 366 + .repos.sso 367 367 .get_external_identity_by_provider(provider, &user_info.provider_user_id) 368 368 .await 369 369 { ··· 371 371 Ok(None) => { 372 372 let token = generate_registration_token(); 373 373 if let Err(e) = state 374 - .sso_repo 374 + .repos.sso 375 375 .create_pending_registration( 376 376 &token, 377 377 request_uri, ··· 398 398 } 399 399 }; 400 400 401 - let is_verified = match state.user_repo.get_session_info_by_did(&identity.did).await { 401 + let is_verified = match state.repos.user.get_session_info_by_did(&identity.did).await { 402 402 Ok(Some(info)) => info.channel_verification.has_any_verified(), 403 403 Ok(None) => { 404 404 tracing::error!("User not found for SSO login: {}", identity.did); ··· 423 423 } 424 424 425 425 if let Err(e) = state 426 - .sso_repo 426 + .repos.sso 427 427 .update_external_identity_login( 428 428 identity.id, 429 429 user_info.username.as_deref(), ··· 436 436 437 437 let request_id = RequestId::new(request_uri.to_string()); 438 438 if let Err(e) = state 439 - .oauth_repo 439 + .repos.oauth 440 440 .set_authorization_did(&request_id, &identity.did, None) 441 441 .await 442 442 { ··· 452 452 ); 453 453 454 454 let has_totp = matches!( 455 - state.user_repo.get_totp_record_state(&identity.did).await, 455 + state.repos.user.get_totp_record_state(&identity.did).await, 456 456 Ok(Some(tranquil_db_traits::TotpRecordState::Verified(_))) 457 457 ); 458 458 ··· 478 478 user_info: &tranquil_pds::sso::providers::SsoUserInfo, 479 479 ) -> Response { 480 480 let existing = state 481 - .sso_repo 481 + .repos.sso 482 482 .get_external_identity_by_provider(provider, &user_info.provider_user_id) 483 483 .await; 484 484 ··· 517 517 } 518 518 519 519 if let Err(e) = state 520 - .sso_repo 520 + .repos.sso 521 521 .create_external_identity( 522 522 &did, 523 523 provider, ··· 551 551 user_info: &tranquil_pds::sso::providers::SsoUserInfo, 552 552 ) -> Response { 553 553 match state 554 - .sso_repo 554 + .repos.sso 555 555 .get_external_identity_by_provider(provider, &user_info.provider_user_id) 556 556 .await 557 557 { ··· 569 569 570 570 let token = generate_registration_token(); 571 571 if let Err(e) = state 572 - .sso_repo 572 + .repos.sso 573 573 .create_pending_registration( 574 574 &token, 575 575 request_uri, ··· 612 612 auth: tranquil_pds::auth::Auth<tranquil_pds::auth::Active>, 613 613 ) -> Result<Json<LinkedAccountsResponse>, ApiError> { 614 614 let identities = state 615 - .sso_repo 615 + .repos.sso 616 616 .get_external_identities_by_did(&auth.did) 617 617 .await?; 618 618 ··· 657 657 let id = uuid::Uuid::parse_str(&input.id).map_err(|_| ApiError::InvalidId)?; 658 658 659 659 let has_password = state 660 - .user_repo 660 + .repos.user 661 661 .has_password_by_did(&auth.did) 662 662 .await? 663 663 .unwrap_or(false); 664 664 665 - let passkeys = state.user_repo.get_passkeys_for_user(&auth.did).await?; 665 + let passkeys = state.repos.user.get_passkeys_for_user(&auth.did).await?; 666 666 let has_passkeys = !passkeys.is_empty(); 667 667 668 668 if !has_password && !has_passkeys { 669 669 let identities = state 670 - .sso_repo 670 + .repos.sso 671 671 .get_external_identities_by_did(&auth.did) 672 672 .await?; 673 673 ··· 680 680 } 681 681 682 682 let deleted = state 683 - .sso_repo 683 + .repos.sso 684 684 .delete_external_identity(id, &auth.did) 685 685 .await?; 686 686 ··· 718 718 } 719 719 720 720 let pending = state 721 - .sso_repo 721 + .repos.sso 722 722 .get_pending_registration(&query.token) 723 723 .await? 724 724 .ok_or(ApiError::SsoSessionExpired)?; ··· 780 780 }; 781 781 782 782 let db_available = state 783 - .user_repo 783 + .repos.user 784 784 .check_handle_available_for_new_account(&handle_typed) 785 785 .await 786 786 .unwrap_or(false); ··· 850 850 } 851 851 852 852 let pending_preview = state 853 - .sso_repo 853 + .repos.sso 854 854 .get_pending_registration(&input.token) 855 855 .await? 856 856 .ok_or(ApiError::SsoSessionExpired)?; ··· 962 962 }; 963 963 964 964 let _validated_invite_code = if let Some(ref code) = input.invite_code { 965 - match state.infra_repo.validate_invite_code(code).await { 965 + match state.repos.infra.validate_invite_code(code).await { 966 966 Ok(validated) => Some(validated), 967 967 Err(_) => return Err(ApiError::InvalidInviteCode), 968 968 } ··· 977 977 let handle_typed: tranquil_pds::types::Handle = 978 978 handle.parse().map_err(|_| ApiError::InvalidHandle(None))?; 979 979 let reserved = state 980 - .user_repo 980 + .repos.user 981 981 .reserve_handle(&handle_typed, client_ip) 982 982 .await 983 983 .unwrap_or(false); ··· 1160 1160 pending_registration_token: input.token.clone(), 1161 1161 }; 1162 1162 1163 - let create_result = match state.user_repo.create_sso_account(&create_input).await { 1163 + let create_result = match state.repos.user.create_sso_account(&create_input).await { 1164 1164 Ok(r) => r, 1165 1165 Err(tranquil_db_traits::CreateAccountError::HandleTaken) => { 1166 1166 return Err(ApiError::HandleNotAvailable(None)); ··· 1178 1178 }; 1179 1179 1180 1180 let _ = state 1181 - .user_repo 1181 + .repos.user 1182 1182 .release_handle_reservation(&handle_typed) 1183 1183 .await; 1184 1184 ··· 1216 1216 1217 1217 let app_password = generate_app_password(); 1218 1218 let app_password_name = "bsky.app".to_string(); 1219 - let app_password_hash = match bcrypt::hash(&app_password, bcrypt::DEFAULT_COST) { 1220 - Ok(h) => h, 1221 - Err(e) => { 1222 - tracing::error!("Failed to hash app password: {:?}", e); 1223 - return Err(ApiError::InternalError(None)); 1224 - } 1225 - }; 1219 + let app_password_hash = 1220 + tranquil_api::common::hash_or_internal_error(&app_password)?; 1226 1221 1227 1222 let app_password_data = tranquil_db_traits::AppPasswordCreate { 1228 1223 user_id: create_result.user_id, ··· 1233 1228 created_by_controller_did: None, 1234 1229 }; 1235 1230 if let Err(e) = state 1236 - .session_repo 1231 + .repos.session 1237 1232 .create_app_password(&app_password_data) 1238 1233 .await 1239 1234 { ··· 1245 1240 if !is_standalone { 1246 1241 let request_id = RequestId::new(pending_preview.request_uri.clone()); 1247 1242 if let Err(e) = state 1248 - .oauth_repo 1243 + .repos.oauth 1249 1244 .set_authorization_did(&request_id, &did_typed, None) 1250 1245 .await 1251 1246 { ··· 1264 1259 ); 1265 1260 1266 1261 let user_id = state 1267 - .user_repo 1262 + .repos.user 1268 1263 .get_id_by_did(&did_typed) 1269 1264 .await 1270 1265 .unwrap_or(None); ··· 1275 1270 1276 1271 if channel_auto_verified { 1277 1272 let _ = state 1278 - .user_repo 1273 + .repos.user 1279 1274 .set_channel_verified(&did_typed, tranquil_db_traits::CommsChannel::Email) 1280 1275 .await; 1281 1276 tracing::info!(did = %did, "Auto-verified email from SSO provider"); ··· 1321 1316 controller_did: None, 1322 1317 app_password_name: None, 1323 1318 }; 1324 - if let Err(e) = state.session_repo.create_session(&session_data).await { 1319 + if let Err(e) = state.repos.session.create_session(&session_data).await { 1325 1320 tracing::error!("Failed to insert session: {:?}", e); 1326 1321 return Err(ApiError::InternalError(None)); 1327 1322 } 1328 1323 1329 1324 let hostname = &tranquil_config::get().server.hostname; 1330 1325 if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_welcome( 1331 - state.user_repo.as_ref(), 1332 - state.infra_repo.as_ref(), 1326 + state.repos.user.as_ref(), 1327 + state.repos.infra.as_ref(), 1333 1328 user_id.unwrap_or(uuid::Uuid::nil()), 1334 1329 hostname, 1335 1330 ) ··· 1372 1367 let formatted_token = 1373 1368 tranquil_pds::auth::verification_token::format_token_for_display(&verification_token); 1374 1369 if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_signup_verification( 1375 - state.user_repo.as_ref(), 1376 - state.infra_repo.as_ref(), 1370 + state.repos.user.as_ref(), 1371 + state.repos.infra.as_ref(), 1377 1372 uid, 1378 1373 verification_channel, 1379 1374 &verification_recipient,
+5 -5
crates/tranquil-server/src/main.rs
··· 127 127 128 128 tranquil_sync::listener::start_sequencer_listener(state.clone()).await; 129 129 130 - let backfill_repo_repo = state.repo_repo.clone(); 130 + let backfill_repo_repo = state.repos.repo.clone(); 131 131 let backfill_block_store = state.block_store.clone(); 132 132 tokio::spawn(async move { 133 133 tokio::join!( ··· 141 141 ); 142 142 }); 143 143 144 - let mut comms_service = CommsService::new(state.infra_repo.clone()); 144 + let mut comms_service = CommsService::new(state.repos.infra.clone()); 145 145 let mut deferred_discord_endpoint: Option<(DiscordSender, String, String)> = None; 146 146 147 147 let cfg = tranquil_config::get(); ··· 249 249 }; 250 250 251 251 let scheduled_handle = tokio::spawn(start_scheduled_tasks( 252 - state.user_repo.clone(), 253 - state.blob_repo.clone(), 252 + state.repos.user.clone(), 253 + state.repos.blob.clone(), 254 254 state.blob_store.clone(), 255 - state.sso_repo.clone(), 255 + state.repos.sso.clone(), 256 256 shutdown.clone(), 257 257 )); 258 258
+5 -5
crates/tranquil-sync/src/blob.rs
··· 27 27 let cid = params.cid; 28 28 29 29 let _account = 30 - match assert_repo_availability(state.repo_repo.as_ref(), &did, RepoAccessLevel::Public) 30 + match assert_repo_availability(state.repos.repo.as_ref(), &did, RepoAccessLevel::Public) 31 31 .await 32 32 { 33 33 Ok(a) => a, 34 34 Err(e) => return e.into_response(), 35 35 }; 36 36 37 - let blob_result = state.blob_repo.get_blob_metadata(&cid).await; 37 + let blob_result = state.repos.blob.get_blob_metadata(&cid).await; 38 38 match blob_result { 39 39 Ok(Some(metadata)) => match state.blob_store.get(&metadata.storage_key).await { 40 40 Ok(data) => Response::builder() ··· 80 80 let did = params.did; 81 81 82 82 let account = 83 - match assert_repo_availability(state.repo_repo.as_ref(), &did, RepoAccessLevel::Public) 83 + match assert_repo_availability(state.repos.repo.as_ref(), &did, RepoAccessLevel::Public) 84 84 .await 85 85 { 86 86 Ok(a) => a, ··· 93 93 94 94 let cids_result: Result<Vec<String>, _> = if let Some(since) = &params.since { 95 95 state 96 - .blob_repo 96 + .repos.blob 97 97 .list_blobs_since_rev(&did, since) 98 98 .await 99 99 .map(|cids| { ··· 107 107 }) 108 108 } else { 109 109 state 110 - .blob_repo 110 + .repos.blob 111 111 .list_blobs_by_user(user_id, Some(cursor_cid), limit + 1) 112 112 .await 113 113 .map(|cids| cids.into_iter().map(|c| c.to_string()).collect())
+3 -3
crates/tranquil-sync/src/commit.rs
··· 43 43 let did = params.did; 44 44 45 45 let account = 46 - match assert_repo_availability(state.repo_repo.as_ref(), &did, RepoAccessLevel::Public) 46 + match assert_repo_availability(state.repos.repo.as_ref(), &did, RepoAccessLevel::Public) 47 47 .await 48 48 { 49 49 Ok(a) => a, ··· 104 104 let cursor_did: Option<Did> = params.cursor.as_ref().and_then(|s| s.parse().ok()); 105 105 let cursor_ref = cursor_did.as_ref(); 106 106 let result = state 107 - .repo_repo 107 + .repos.repo 108 108 .list_repos_paginated(cursor_ref, limit + 1) 109 109 .await; 110 110 match result { ··· 175 175 ) -> Response { 176 176 let did = params.did; 177 177 178 - let account = match get_account_with_status(state.repo_repo.as_ref(), &did).await { 178 + let account = match get_account_with_status(state.repos.repo.as_ref(), &did).await { 179 179 Ok(Some(a)) => a, 180 180 Ok(None) => { 181 181 return ApiError::RepoNotFound(Some(format!("Could not find repo for DID: {}", did)))
+4 -4
crates/tranquil-sync/src/deprecated.rs
··· 27 27 let dpop_proof = tranquil_pds::util::get_header_str(headers, tranquil_pds::util::HEADER_DPOP); 28 28 let http_uri = "/"; 29 29 match tranquil_pds::auth::validate_token_with_dpop( 30 - state.user_repo.as_ref(), 31 - state.oauth_repo.as_ref(), 30 + state.repos.user.as_ref(), 31 + state.repos.oauth.as_ref(), 32 32 &extracted.token, 33 33 extracted.scheme, 34 34 dpop_proof, ··· 68 68 }; 69 69 let is_admin_or_self = check_admin_or_self(&state, &headers, &did).await; 70 70 let account = match assert_repo_availability( 71 - state.repo_repo.as_ref(), 71 + state.repos.repo.as_ref(), 72 72 &did, 73 73 if is_admin_or_self { 74 74 RepoAccessLevel::Privileged ··· 108 108 }; 109 109 let is_admin_or_self = check_admin_or_self(&state, &headers, &did).await; 110 110 let account = match assert_repo_availability( 111 - state.repo_repo.as_ref(), 111 + state.repos.repo.as_ref(), 112 112 &did, 113 113 if is_admin_or_self { 114 114 RepoAccessLevel::Privileged
+5 -5
crates/tranquil-sync/src/listener.rs
··· 8 8 9 9 pub async fn start_sequencer_listener(state: AppState) { 10 10 let initial_seq = state 11 - .repo_repo 11 + .repos.repo 12 12 .get_max_seq() 13 13 .await 14 14 .unwrap_or(SequenceNumber::ZERO); ··· 30 30 31 31 async fn listen_loop(state: AppState) -> anyhow::Result<()> { 32 32 let mut receiver = state 33 - .event_notifier 33 + .repos.event_notifier 34 34 .subscribe() 35 35 .await 36 36 .map_err(|e| anyhow::anyhow!("Failed to subscribe to events: {:?}", e))?; 37 37 info!("Connected to database and listening for repo updates"); 38 38 let catchup_start = SequenceNumber::from_raw(LAST_BROADCAST_SEQ.load(Ordering::SeqCst)); 39 39 let events = state 40 - .repo_repo 40 + .repos.repo 41 41 .get_events_since_seq(catchup_start, None) 42 42 .await 43 43 .map_err(|e| anyhow::anyhow!("Failed to fetch catchup events: {:?}", e))?; ··· 70 70 } 71 71 if seq_id > last_seq + 1 { 72 72 let gap_events = state 73 - .repo_repo 73 + .repos.repo 74 74 .get_events_in_seq_range( 75 75 SequenceNumber::from_raw(last_seq), 76 76 SequenceNumber::from_raw(seq_id), ··· 88 88 } 89 89 } 90 90 let event = state 91 - .repo_repo 91 + .repos.repo 92 92 .get_event_by_seq(SequenceNumber::from_raw(seq_id)) 93 93 .await 94 94 .ok()
+6 -6
crates/tranquil-sync/src/repo.rs
··· 46 46 }; 47 47 48 48 let _account = 49 - match assert_repo_availability(state.repo_repo.as_ref(), &did, RepoAccessLevel::Public) 49 + match assert_repo_availability(state.repos.repo.as_ref(), &did, RepoAccessLevel::Public) 50 50 .await 51 51 { 52 52 Ok(a) => a, ··· 123 123 ) -> Response { 124 124 let did = query.did; 125 125 let account = 126 - match assert_repo_availability(state.repo_repo.as_ref(), &did, RepoAccessLevel::Public) 126 + match assert_repo_availability(state.repos.repo.as_ref(), &did, RepoAccessLevel::Public) 127 127 .await 128 128 { 129 129 Ok(a) => a, ··· 143 143 } 144 144 145 145 let car_bytes = match generate_repo_car_from_user_blocks( 146 - state.repo_repo.as_ref(), 146 + state.repos.repo.as_ref(), 147 147 &state.block_store, 148 148 account.user_id, 149 149 &head_cid, ··· 166 166 } 167 167 168 168 async fn get_repo_since(state: &AppState, did: &Did, head_cid: &Cid, since: &str) -> Response { 169 - let user_id = match state.user_repo.get_id_by_did(did).await { 169 + let user_id = match state.repos.user.get_id_by_did(did).await { 170 170 Ok(Some(id)) => id, 171 171 Ok(None) => { 172 172 return ApiError::RepoNotFound(Some(format!("Could not find repo for DID: {}", did))) ··· 179 179 }; 180 180 181 181 let block_cid_bytes = match state 182 - .repo_repo 182 + .repos.repo 183 183 .get_user_block_cids_since_rev(user_id, since) 184 184 .await 185 185 { ··· 252 252 253 253 let did = query.did; 254 254 let account = 255 - match assert_repo_availability(state.repo_repo.as_ref(), &did, RepoAccessLevel::Public) 255 + match assert_repo_availability(state.repos.repo.as_ref(), &did, RepoAccessLevel::Public) 256 256 .await 257 257 { 258 258 Ok(a) => a,
+5 -5
crates/tranquil-sync/src/subscribe_repos.rs
··· 73 73 if let Some(cursor) = params.cursor { 74 74 let cursor_seq = SequenceNumber::from_raw(cursor); 75 75 let current_seq = state 76 - .repo_repo 76 + .repos.repo 77 77 .get_max_seq() 78 78 .await 79 79 .unwrap_or(SequenceNumber::ZERO); ··· 91 91 let backfill_time = chrono::Utc::now() - chrono::Duration::hours(get_backfill_hours()); 92 92 93 93 let first_event = state 94 - .repo_repo 94 + .repos.repo 95 95 .get_events_since_cursor(cursor_seq, 1) 96 96 .await 97 97 .ok() ··· 110 110 } 111 111 112 112 let earliest = state 113 - .repo_repo 113 + .repos.repo 114 114 .get_min_seq_since(backfill_time) 115 115 .await 116 116 .ok() ··· 125 125 126 126 loop { 127 127 let events = state 128 - .repo_repo 128 + .repos.repo 129 129 .get_events_since_cursor(current_cursor, BACKFILL_BATCH_SIZE) 130 130 .await; 131 131 match events { ··· 171 171 } 172 172 } 173 173 174 - let cutover_events = state.repo_repo.get_events_since_seq(last_seen, None).await; 174 + let cutover_events = state.repos.repo.get_events_since_seq(last_seen, None).await; 175 175 176 176 if let Ok(events) = cutover_events 177 177 && !events.is_empty()