f0a1552
ZhenYi
feat(chat): add chat module with room management, message handling, and session revocation
f0a1552
use models::users::user_sessions;
use models::users::user_sessions::SessionKind;
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter, Set};
use time::{Duration, OffsetDateTime};
use uuid::Uuid;
use super::AuthService;
use crate::errors::AuthError;
const DEFAULT_SESSION_LIFETIME: Duration = Duration::days(30);
impl AuthService {
pub fn session_lifetime(&self) -> Duration {
match self.config().session.lifespan_secs {
Some(secs) if secs > 0 => Duration::seconds(secs),
_ => DEFAULT_SESSION_LIFETIME,
}
}
pub async fn create_session(
&self,
token_hash: &str,
user_id: Uuid,
ua_hash: Option<&str>,
ip_hash: Option<&str>,
device_id: Option<Uuid>,
) -> Result<(), AuthError> {
let now = OffsetDateTime::now_utc();
let id = Uuid::now_v7();
let row = user_sessions::ActiveModel {
id: Set(id),
user_id: Set(user_id),
token_hash: Set(token_hash.to_string()),
user_agent_hash: Set(ua_hash.map(str::to_string)),
client_ip_hash: Set(ip_hash.map(str::to_string)),
device_id: Set(device_id),
kind: Set(SessionKind::WebLogin),
family_id: Set(id),
parent_refresh_id: Set(None),
is_revoked: Set(false),
revoked_at: Set(None),
last_seen_at: Set(now),
created_at: Set(now),
expires_at: Set(now + self.session_lifetime()),
replaced_at: Set(None),
grace_expires_at: Set(None),
updated_at: Set(now),
replaced_by: Set(None),
signing_key_id: Set(None),
app_id: Set(None),
};
row.insert(self.db().write()).await?;
Ok(())
}
pub async fn touch_session(&self, token_hash: &str) -> Result<(), AuthError> {
let now = OffsetDateTime::now_utc();
let Some(row) = user_sessions::Entity::find()
.filter(user_sessions::Column::TokenHash.eq(token_hash))
.one(self.db().write())
.await?
else {
return Ok(());
};
let mut active = row.into_active_model();
active.last_seen_at = Set(now);
active.updated_at = Set(now);
active.update(self.db().write()).await?;
Ok(())
}
pub async fn revoke_session(&self, token_hash: &str) -> Result<(), AuthError> {
let now = OffsetDateTime::now_utc();
let Some(row) = user_sessions::Entity::find()
.filter(user_sessions::Column::TokenHash.eq(token_hash))
.one(self.db().write())
.await?
else {
return Ok(());
};
let mut active = row.into_active_model();
active.is_revoked = Set(true);
active.revoked_at = Set(Some(now));
active.updated_at = Set(now);
active.update(self.db().write()).await?;
Ok(())
}
}