use std::collections::HashMap; use kenauth::proto::common::v1::UserPublic; use crate::AppState; use crate::error::ExportError; /// Bulk identity lookup. KenApi keeps no local copy of usernames, display /// names or avatars: every caller goes through KenOAuth. pub async fn load_public_users( state: &AppState, uids: &[i64], ) -> Result, ExportError> { if uids.is_empty() { return Ok(HashMap::new()); } let mut ids: Vec = uids.to_vec(); ids.sort_unstable(); ids.dedup(); let client = state .kenauth .as_ref() .ok_or_else(|| ExportError::InvalidConfig("kenoauth is not configured".to_string()))?; let users = client .batch_get_user_public(ids) .await .map_err(|error| ExportError::Connection(error.to_string()))?; Ok(users.into_iter().map(|user| (user.user_id, user)).collect()) } pub async fn get_public_user( state: &AppState, uid: i64, ) -> Result, ExportError> { let client = state .kenauth .as_ref() .ok_or_else(|| ExportError::InvalidConfig("kenoauth is not configured".to_string()))?; match client.get_user_public(uid).await { Ok(user) => Ok(Some(user)), Err(kenauth::KenAuthError::NotFound) => Ok(None), Err(error) => Err(ExportError::Connection(error.to_string())), } } pub async fn find_by_email( state: &AppState, email: &str, ) -> Result, ExportError> { let client = state .kenauth .as_ref() .ok_or_else(|| ExportError::InvalidConfig("kenoauth is not configured".to_string()))?; match client .get_user_public_by_ref(kenauth::proto::common::v1::user_ref::Key::Email( email.to_string(), )) .await { Ok(user) => Ok(Some(user)), Err(kenauth::KenAuthError::NotFound) => Ok(None), Err(error) => Err(ExportError::Connection(error.to_string())), } } pub async fn find_by_username( state: &AppState, username: &str, ) -> Result, ExportError> { let client = state .kenauth .as_ref() .ok_or_else(|| ExportError::InvalidConfig("kenoauth is not configured".to_string()))?; match client .get_user_public_by_ref(kenauth::proto::common::v1::user_ref::Key::Username( username.to_string(), )) .await { Ok(user) => Ok(Some(user)), Err(kenauth::KenAuthError::NotFound) => Ok(None), Err(error) => Err(ExportError::Connection(error.to_string())), } } /// Create the local settings/anchor row on demand. It contains no identity /// projection; it exists so local preferences and foreign keys have a stable /// row even when the user never went through KenApi's login callback. pub async fn ensure_local_user(state: &AppState, uid: i64) -> Result<(), ExportError> { use models::users::user::{self, ProfileVisibility}; use sea_orm::{ActiveModelTrait, EntityTrait, Set}; let db = state.database.write(); if user::Entity::find_by_id(uid).one(db).await?.is_some() { return Ok(()); } user::ActiveModel { uid: Set(uid), is_restricted: Set(false), visibility: Set(ProfileVisibility::Public), last_login_at: Set(None), last_login_ip_hash: Set(None), local_avatar_url: Set(String::new()), timezone: Set(None), locale: Set(None), language: Set(None), version: Set(1), updated_at: Set(time::OffsetDateTime::now_utc()), } .insert(db) .await?; Ok(()) }