Fix MongoDB DateTime serialization issues - Replace chrono::DateTime with mongodb::bson::DateTime in models - Update API responses to use timestamp_millis() for JSON serialization - Fix User, Share model DateTime fields - Update all handler responses to return i64 timestamps - This fixes the Kind: invalid type: map, expected RFC 3339 error
Some checks failed
Lint and Build / Lint (push) Failing after 5s
Lint and Build / Build (push) Has been skipped
Lint and Build / Docker Build (push) Has been skipped

This commit is contained in:
goose 2026-02-26 09:22:36 -03:00
parent 1e914089d5
commit 3a6bcbd94d
5 changed files with 25 additions and 20 deletions

View file

@ -9,10 +9,13 @@ pub async fn health_check(State(state): State<AppState>) -> Json<Value> {
"error" "error"
}; };
// Use timestamp_millis for consistency with other endpoints
let timestamp = mongodb::bson::DateTime::now().timestamp_millis();
Json(json!({ Json(json!({
"status": "ok", "status": "ok",
"database": status, "database": status,
"timestamp": chrono::Utc::now().to_rfc3339() "timestamp": timestamp
})) }))
} }

View file

@ -32,8 +32,8 @@ pub struct ShareResponse {
pub resource_type: String, pub resource_type: String,
pub resource_id: Option<String>, pub resource_id: Option<String>,
pub permissions: Vec<String>, pub permissions: Vec<String>,
pub expires_at: Option<String>, pub expires_at: Option<i64>,
pub created_at: String, pub created_at: i64,
pub active: bool, pub active: bool,
} }
@ -47,8 +47,8 @@ impl TryFrom<Share> for ShareResponse {
resource_type: share.resource_type, resource_type: share.resource_type,
resource_id: share.resource_id.map(|id| id.to_string()), resource_id: share.resource_id.map(|id| id.to_string()),
permissions: share.permissions.into_iter().map(|p| p.to_string()).collect(), permissions: share.permissions.into_iter().map(|p| p.to_string()).collect(),
expires_at: share.expires_at.map(|dt| dt.to_rfc3339()), expires_at: share.expires_at.map(|dt| dt.timestamp_millis()),
created_at: share.created_at.to_rfc3339(), created_at: share.created_at.timestamp_millis(),
active: share.active, active: share.active,
}) })
} }
@ -136,7 +136,7 @@ pub async fn create_share(
// Calculate expiration // Calculate expiration
let expires_at = req.expires_days.map(|days| { let expires_at = req.expires_days.map(|days| {
chrono::Utc::now() + chrono::Duration::days(days as i64) mongodb::bson::DateTime::now().saturating_add_millis(days as i64 * 24 * 60 * 60 * 1000)
}); });
let share = Share::new( let share = Share::new(
@ -289,7 +289,7 @@ pub async fn update_share(
} }
if let Some(days) = req.expires_days { if let Some(days) = req.expires_days {
share.expires_at = Some(chrono::Utc::now() + chrono::Duration::days(days as i64)); share.expires_at = Some(mongodb::bson::DateTime::now().saturating_add_millis(days as i64 * 24 * 60 * 60 * 1000));
} }
match state.db.update_share(&share).await { match state.db.update_share(&share).await {

View file

@ -20,8 +20,8 @@ pub struct UserProfileResponse {
pub id: String, pub id: String,
pub email: String, pub email: String,
pub username: String, pub username: String,
pub created_at: String, pub created_at: i64,
pub last_active: String, pub last_active: i64,
pub email_verified: bool, pub email_verified: bool,
} }
@ -33,8 +33,8 @@ impl TryFrom<User> for UserProfileResponse {
id: user.id.map(|id| id.to_string()).unwrap_or_default(), id: user.id.map(|id| id.to_string()).unwrap_or_default(),
email: user.email, email: user.email,
username: user.username, username: user.username,
created_at: user.created_at.to_rfc3339(), created_at: user.created_at.timestamp_millis(),
last_active: user.last_active.to_rfc3339(), last_active: user.last_active.timestamp_millis(),
email_verified: user.email_verified, email_verified: user.email_verified,
}) })
} }

View file

@ -1,6 +1,7 @@
use mongodb::bson::{doc, oid::ObjectId}; use mongodb::bson::{doc, oid::ObjectId};
use mongodb::Collection; use mongodb::Collection;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use mongodb::bson::DateTime;
use super::permission::Permission; use super::permission::Permission;
@ -15,8 +16,8 @@ pub struct Share {
pub resource_id: Option<ObjectId>, pub resource_id: Option<ObjectId>,
pub permissions: Vec<Permission>, pub permissions: Vec<Permission>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub expires_at: Option<chrono::DateTime<chrono::Utc>>, pub expires_at: Option<DateTime>,
pub created_at: chrono::DateTime<chrono::Utc>, pub created_at: DateTime,
pub active: bool, pub active: bool,
} }
@ -27,7 +28,7 @@ impl Share {
resource_type: String, resource_type: String,
resource_id: Option<ObjectId>, resource_id: Option<ObjectId>,
permissions: Vec<Permission>, permissions: Vec<Permission>,
expires_at: Option<chrono::DateTime<chrono::Utc>>, expires_at: Option<DateTime>,
) -> Self { ) -> Self {
Self { Self {
id: None, id: None,
@ -37,14 +38,14 @@ impl Share {
resource_id, resource_id,
permissions, permissions,
expires_at, expires_at,
created_at: chrono::Utc::now(), created_at: DateTime::now(),
active: true, active: true,
} }
} }
pub fn is_expired(&self) -> bool { pub fn is_expired(&self) -> bool {
if let Some(expires) = self.expires_at { if let Some(expires) = self.expires_at {
chrono::Utc::now() > expires DateTime::now() > expires
} else { } else {
false false
} }

View file

@ -2,6 +2,7 @@ use mongodb::bson::{doc, oid::ObjectId};
use mongodb::Collection; use mongodb::Collection;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use mongodb::bson::DateTime;
use crate::auth::password::verify_password; use crate::auth::password::verify_password;
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@ -26,10 +27,10 @@ pub struct User {
pub token_version: i32, pub token_version: i32,
/// When the user was created /// When the user was created
pub created_at: chrono::DateTime<chrono::Utc>, pub created_at: DateTime,
/// Last time the user was active /// Last time the user was active
pub last_active: chrono::DateTime<chrono::Utc>, pub last_active: DateTime,
/// Email verification status /// Email verification status
pub email_verified: bool, pub email_verified: bool,
@ -40,7 +41,7 @@ pub struct User {
/// When the verification token expires /// When the verification token expires
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub verification_expires: Option<chrono::DateTime<chrono::Utc>>, pub verification_expires: Option<DateTime>,
} }
impl User { impl User {
@ -64,7 +65,7 @@ impl User {
None None
}; };
let now = chrono::Utc::now(); let now = DateTime::now();
let recovery_enabled = recovery_phrase_hash.is_some(); let recovery_enabled = recovery_phrase_hash.is_some();
Ok(User { Ok(User {