fix: proper datetime serialization

This commit is contained in:
2025-12-06 13:36:17 +08:00
parent 0e53ef4488
commit 66a2245733
4 changed files with 78 additions and 15 deletions

4
Cargo.lock generated
View File

@@ -2138,6 +2138,7 @@ checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6"
dependencies = [ dependencies = [
"base64", "base64",
"bytes", "bytes",
"chrono",
"crc", "crc",
"crossbeam-queue", "crossbeam-queue",
"either", "either",
@@ -2213,6 +2214,7 @@ dependencies = [
"bitflags", "bitflags",
"byteorder", "byteorder",
"bytes", "bytes",
"chrono",
"crc", "crc",
"digest", "digest",
"dotenvy", "dotenvy",
@@ -2254,6 +2256,7 @@ dependencies = [
"base64", "base64",
"bitflags", "bitflags",
"byteorder", "byteorder",
"chrono",
"crc", "crc",
"dotenvy", "dotenvy",
"etcetera", "etcetera",
@@ -2288,6 +2291,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea"
dependencies = [ dependencies = [
"atoi", "atoi",
"chrono",
"flume", "flume",
"futures-channel", "futures-channel",
"futures-core", "futures-core",

View File

@@ -6,7 +6,7 @@ edition = "2024"
[dependencies] [dependencies]
axum = "0.8" axum = "0.8"
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
tower-http = { version = "0.6", features = ["compression-full", "fs"] } tower-http = { version = "0.6", features = ["catch-panic", "compression-full", "fs"] }
hyper = { version = "1", features = ["full"] } hyper = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_with = { version = "3.16", features = ["hashbrown_0_16", "json"] } serde_with = { version = "3.16", features = ["hashbrown_0_16", "json"] }
@@ -20,12 +20,12 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
anyhow = "1" anyhow = "1"
thiserror = "2" thiserror = "2"
http-body-util = "0.1.1" http-body-util = "0.1.1"
chrono = { version = "0.4", features = ["clock"] } chrono = { version = "0.4", features = ["clock", "serde"] }
reqwest = { version = "0.12", features = ["json", "rustls-tls", "gzip"], default-features = false } reqwest = { version = "0.12", features = ["json", "rustls-tls", "gzip"], default-features = false }
hashbrown = { version = "0.16", features = ["serde"] } hashbrown = { version = "0.16", features = ["serde"] }
concat-idents = "1.1" concat-idents = "1.1"
indoc = "2.0" indoc = "2.0"
sqlx = { version = "0.8", features = ["runtime-tokio", "sqlite"] } sqlx = { version = "0.8", features = ["runtime-tokio", "sqlite", "chrono"] }
rust-embed = "8.0" rust-embed = "8.0"
mime_guess = "2.0" mime_guess = "2.0"
jsonwebtoken = "9" jsonwebtoken = "9"

View File

@@ -1,4 +1,7 @@
use serde::{Deserialize, Serialize}; use std::fmt::Display;
use chrono::{DateTime, TimeZone, Utc};
use serde::{Deserialize, Serialize, Serializer};
use serde_json::Value; use serde_json::Value;
// Authentication models // Authentication models
@@ -48,8 +51,10 @@ pub struct RuleResponse {
pub action: String, pub action: String,
pub custom_response: Option<String>, pub custom_response: Option<String>,
pub is_enabled: bool, pub is_enabled: bool,
pub created_at: String, #[serde(serialize_with = "serialize_dt")]
pub updated_at: String, pub created_at: DateTime<Utc>,
#[serde(serialize_with = "serialize_dt")]
pub updated_at: DateTime<Utc>,
} }
impl From<crate::db::models::InterceptionRule> for RuleResponse { impl From<crate::db::models::InterceptionRule> for RuleResponse {
@@ -71,8 +76,10 @@ pub struct CommandResponse {
pub id: i64, pub id: i64,
pub command: Value, pub command: Value,
pub status: String, pub status: String,
pub received_at: String, #[serde(serialize_with = "serialize_dt")]
pub processed_at: Option<String>, pub received_at: DateTime<Utc>,
#[serde(serialize_with = "serialize_option_dt")]
pub processed_at: Option<DateTime<Utc>>,
pub notes: Option<String>, pub notes: Option<String>,
} }
@@ -99,3 +106,24 @@ impl ApiError {
Self { error: msg.into() } Self { error: msg.into() }
} }
} }
fn serialize_dt<S, TZ>(dt: &DateTime<TZ>, serializer: S) -> Result<S::Ok, S::Error>
where
TZ: TimeZone,
TZ::Offset: Display,
S: Serializer,
{
let s = dt.format("%Y-%m-%d %H:%M:%S %z").to_string();
serializer.serialize_str(&s)
}
fn serialize_option_dt<S, TZ>(dt: &Option<DateTime<TZ>>, serializer: S) -> Result<S::Ok, S::Error>
where
TZ: TimeZone,
TZ::Offset: Display,
S: Serializer,
{
dt.as_ref()
.map(|dt| dt.format("%Y-%m-%d %H:%M:%S %z").to_string())
.serialize(serializer)
}

View File

@@ -1,4 +1,7 @@
use serde::{Deserialize, Serialize}; use std::fmt::Display;
use chrono::{DateTime, TimeZone, Utc};
use serde::{Deserialize, Serialize, Serializer};
use serde_json::Value; use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
@@ -8,8 +11,10 @@ pub struct InterceptionRule {
pub action: String, pub action: String,
pub custom_response: Option<String>, pub custom_response: Option<String>,
pub is_enabled: bool, pub is_enabled: bool,
pub created_at: String, #[serde(serialize_with = "serialize_dt")]
pub updated_at: String, pub created_at: DateTime<Utc>,
#[serde(serialize_with = "serialize_dt")]
pub updated_at: DateTime<Utc>,
} }
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
@@ -17,8 +22,11 @@ pub struct Command {
pub id: i64, pub id: i64,
pub command_json: String, pub command_json: String,
pub status: String, pub status: String,
pub received_at: String, #[serde(serialize_with = "serialize_dt")]
pub processed_at: Option<String>, pub received_at: DateTime<Utc>,
#[serde(serialize_with = "serialize_option_dt")]
#[serde(default)]
pub processed_at: Option<DateTime<Utc>>,
pub notes: Option<String>, pub notes: Option<String>,
} }
@@ -27,7 +35,8 @@ pub struct Config {
pub key: String, pub key: String,
pub value: String, pub value: String,
pub description: Option<String>, pub description: Option<String>,
pub updated_at: String, #[serde(serialize_with = "serialize_dt")]
pub updated_at: DateTime<Utc>,
} }
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
@@ -38,5 +47,27 @@ pub struct RequestLog {
pub response_body: Value, pub response_body: Value,
pub request_interception_action: String, pub request_interception_action: String,
pub response_interception_action: String, pub response_interception_action: String,
pub created_at: String, #[serde(serialize_with = "serialize_dt")]
pub created_at: DateTime<Utc>,
}
fn serialize_dt<S, TZ>(dt: &DateTime<TZ>, serializer: S) -> Result<S::Ok, S::Error>
where
TZ: TimeZone,
TZ::Offset: Display,
S: Serializer,
{
let s = dt.format("%Y-%m-%d %H:%M:%S %z").to_string();
serializer.serialize_str(&s)
}
fn serialize_option_dt<S, TZ>(dt: &Option<DateTime<TZ>>, serializer: S) -> Result<S::Ok, S::Error>
where
TZ: TimeZone,
TZ::Offset: Display,
S: Serializer,
{
dt.as_ref()
.map(|dt| dt.format("%Y-%m-%d %H:%M:%S %z").to_string())
.serialize(serializer)
} }