use crate::models::users::user_sessions; use crate::protos::auth::common::v1::SessionInfo; use chrono::{DateTime, NaiveDate, TimeZone, Utc}; use prost_types::Timestamp; pub const DEFAULT_PAGE_SIZE: i32 = 20; pub const MAX_PAGE_SIZE: i32 = 100; pub fn timestamp(seconds: i64, nanos: i32) -> Timestamp { Timestamp { seconds, nanos } } pub fn timestamp_from_datetime(value: DateTime) -> Timestamp { Timestamp { seconds: value.timestamp(), nanos: value.timestamp_subsec_nanos() as i32, } } pub fn now_timestamp() -> Timestamp { timestamp_from_datetime(Utc::now()) } pub fn datetime(value: &Timestamp) -> Option> { let nanos = value.nanos.clamp(0, 999_999_999) as u32; Utc.timestamp_opt(value.seconds, nanos).single() } pub fn date_timestamp(value: NaiveDate) -> Option { value .and_hms_opt(0, 0, 0) .map(|naive| timestamp_from_datetime(Utc.from_utc_datetime(&naive))) } pub fn datetime_opt(value: Option>) -> Option { value.map(timestamp_from_datetime) } pub fn session_info(row: &user_sessions::Model) -> SessionInfo { SessionInfo { session_id: row.id.to_string(), user_id: row.user_id, security_version: row.security_version, client_ip: row.ip_address.clone().unwrap_or_default(), user_agent: row.user_agent.clone().unwrap_or_default(), created_at: datetime_opt(Some(row.created_at)), last_seen_at: datetime_opt(Some(row.last_seen_at)), expires_at: datetime_opt(Some(row.expires_at)), refresh_expires_at: datetime_opt(row.refresh_expires_at), mfa_verified_at: datetime_opt(row.mfa_verified_at), revoked_at: datetime_opt(row.revoked_at), revoke_reason: String::new(), } } pub fn page_size(requested: i32) -> i32 { if requested <= 0 { DEFAULT_PAGE_SIZE } else { requested.min(MAX_PAGE_SIZE) } } pub fn offset(page: i32, page_size: i32) -> u64 { let page = page.max(1) as u64; let page_size = page_size.max(1) as u64; page.saturating_sub(1).saturating_mul(page_size) } pub fn has_more(offset: u64, page_size: i32, row_count: usize) -> bool { row_count as u64 == page_size.max(0) as u64 && offset + row_count as u64 > offset }