Skip to content
Clone
main
107 lines 3.2 KB Rust
use std::time::Duration;
use chrono::{DateTime, Utc};
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use crate::AuthService;
use crate::errors::{AuthError, AuthResult};
use crate::models::users::users;
use crate::session::Session;
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, ToSchema)]
pub struct MeResponse {
pub user_id: i64,
pub username: String,
pub displayname: Option<String>,
pub email: String,
pub is_admin: bool,
pub avatar_url: Option<String>,
pub email_verified_at: Option<DateTime<Utc>>,
pub created_at: DateTime<Utc>,
pub version: i32,
}
impl From<users::Model> for MeResponse {
fn from(user: users::Model) -> Self {
Self {
user_id: user.id,
username: user.username,
displayname: user.displayname,
email: user.email,
is_admin: user.is_admin,
avatar_url: None,
email_verified_at: user.email_verified_at,
created_at: user.created_at,
version: user.version,
}
}
}
pub const ME_CACHE_TTL_SECS: u64 = 300;
const ME_COUNT_TTL_SECS: u64 = 600;
const ME_REFRESH_EVERY: i64 = 5;
pub fn me_cache_keys(user_id: i64) -> (String, String) {
(
format!("auth:me:{user_id}"),
format!("auth:me:count:{user_id}"),
)
}
pub async fn invalidate_me_cache(cache: &crate::cache::Cache, user_id: i64) {
let (cache_key, count_key) = me_cache_keys(user_id);
let dropped = async {
cache.delete(&cache_key).await?;
cache.delete(&count_key).await?;
Ok::<(), crate::cache::CacheError>(())
}
.await;
if let Err(error) = dropped {
tracing::warn!(%error, user_id, "failed to drop the me cache");
}
}
impl AuthService {
#[tracing::instrument(skip_all, fields(request_id = %_request_id, client_ip = ?session.client_ip().ok().flatten()))]
pub async fn me(&self, session: Session, _request_id: String) -> AuthResult<MeResponse> {
let user_id = self
.accounts
.require_session(&session, Some(_request_id.as_str()))
.await?;
self.me_for_user(user_id).await
}
pub(crate) async fn me_for_user(&self, user_id: i64) -> AuthResult<MeResponse> {
let (cache_key, count_key) = me_cache_keys(user_id);
let count = self.cache().incr(&count_key).await?;
if count == 1 {
let _ = self
.cache()
.expire(&count_key, Duration::from_secs(ME_COUNT_TTL_SECS))
.await;
}
if count % ME_REFRESH_EVERY != 0
&& let Some(cached) = self.cache().get_json::<MeResponse>(&cache_key).await?
{
return Ok(cached);
}
let user = users::Entity::find_by_id(user_id)
.filter(users::Column::DeletedAt.is_null())
.one(self.db().read())
.await?
.ok_or(AuthError::Unauthenticated)?;
let me = MeResponse::from(user);
let _ = self
.cache()
.set_json_ex(&cache_key, &me, Duration::from_secs(ME_CACHE_TTL_SECS))
.await;
Ok(me)
}
}