diff --git a/hive-c0re/src/audit_log.rs b/hive-c0re/src/audit_log.rs index 4a64efa7..43f1772a 100644 --- a/hive-c0re/src/audit_log.rs +++ b/hive-c0re/src/audit_log.rs @@ -89,6 +89,7 @@ impl AuditOutcome { #[derive(Debug, Clone, Serialize)] pub struct AuditEntry { pub id: i64, + #[serde(with = "hive_sh4re::wire_time::iso")] pub ts_unix: i64, /// Agent on whose behalf the action was taken. pub agent: String, diff --git a/hive-c0re/src/broker.rs b/hive-c0re/src/broker.rs index b2caef09..aedf314f 100644 --- a/hive-c0re/src/broker.rs +++ b/hive-c0re/src/broker.rs @@ -74,7 +74,9 @@ pub struct PendingReminder { pub message: String, #[serde(skip_serializing_if = "Option::is_none")] pub file_path: Option, + #[serde(with = "hive_sh4re::wire_time::iso")] pub due_at: i64, + #[serde(with = "hive_sh4re::wire_time::iso")] pub created_at: i64, /// Most recent delivery failure for this row, if any. Cleared /// to NULL on operator retry. Surfaced inline in the dashboard diff --git a/hive-c0re/src/dashboard.rs b/hive-c0re/src/dashboard.rs index 61d8dd49..5a0399e7 100644 --- a/hive-c0re/src/dashboard.rs +++ b/hive-c0re/src/dashboard.rs @@ -432,7 +432,8 @@ struct ApprovalHistoryView { sha_short: Option, /// `approved` / `denied` / `failed`. status: &'static str, - /// Unix seconds. Renders as a relative time on the dashboard. + /// RFC 3339 UTC. Renders as a relative time on the dashboard. + #[serde(with = "hive_sh4re::wire_time::iso")] resolved_at: i64, /// Operator-supplied deny reason (for `denied`) or build error /// (for `failed`). None on `approved`. @@ -471,8 +472,10 @@ struct ApprovalView { /// `None` for every other kind. #[serde(skip_serializing_if = "Option::is_none")] commit_ref: Option, - /// Unix seconds the approval was queued. Rendered as a relative - /// time on the card so the operator can spot a stale request. + /// RFC 3339 UTC time the approval was queued. Rendered as a + /// relative time on the card so the operator can spot a stale + /// request. + #[serde(with = "hive_sh4re::wire_time::iso")] requested_at: i64, } @@ -1373,7 +1376,7 @@ async fn api_operator_inbox(State(state): State) -> Response { "id": id, "from": from, "body": body, - "at": at, + "at": hive_sh4re::wire_time::to_iso(at), "in_reply_to": in_reply_to, "file_refs": file_refs, })) diff --git a/hive-c0re/src/dashboard_events.rs b/hive-c0re/src/dashboard_events.rs index 13307bcb..48610473 100644 --- a/hive-c0re/src/dashboard_events.rs +++ b/hive-c0re/src/dashboard_events.rs @@ -38,6 +38,7 @@ pub enum DashboardEvent { from: String, to: String, body: String, + #[serde(with = "hive_sh4re::wire_time::iso")] at: i64, #[serde(default, skip_serializing_if = "Option::is_none")] in_reply_to: Option, @@ -53,6 +54,7 @@ pub enum DashboardEvent { from: String, to: String, body: String, + #[serde(with = "hive_sh4re::wire_time::iso")] at: i64, #[serde(default, skip_serializing_if = "Option::is_none")] in_reply_to: Option, @@ -95,6 +97,7 @@ pub enum DashboardEvent { sha_short: Option, /// `"approved"` / `"denied"` / `"failed"`. status: &'static str, + #[serde(with = "hive_sh4re::wire_time::iso")] resolved_at: i64, note: Option, description: Option, @@ -111,7 +114,9 @@ pub enum DashboardEvent { question: String, options: Vec, multi: bool, + #[serde(with = "hive_sh4re::wire_time::iso")] asked_at: i64, + #[serde(with = "hive_sh4re::wire_time::iso_opt")] deadline_at: Option, target: Option, /// Verified file-path tokens that appear in `question`. @@ -130,6 +135,7 @@ pub enum DashboardEvent { id: i64, answer: String, answerer: String, + #[serde(with = "hive_sh4re::wire_time::iso")] answered_at: i64, cancelled: bool, target: Option, diff --git a/hive-c0re/src/operator_questions.rs b/hive-c0re/src/operator_questions.rs index 1dcf14ad..ab51e9f0 100644 --- a/hive-c0re/src/operator_questions.rs +++ b/hive-c0re/src/operator_questions.rs @@ -74,11 +74,14 @@ pub struct OpQuestion { pub question: String, pub options: Vec, pub multi: bool, + #[serde(with = "hive_sh4re::wire_time::iso")] pub asked_at: i64, - /// Absolute unix-seconds deadline after which a watchdog auto- - /// resolves the question with answer `[expired]`. `None` = no - /// expiry. Surfaced on the dashboard as a remaining-time chip. + /// Deadline after which a watchdog auto-resolves the question with + /// answer `[expired]`. `None` = no expiry. Surfaced on the + /// dashboard as a remaining-time chip. + #[serde(with = "hive_sh4re::wire_time::iso_opt")] pub deadline_at: Option, + #[serde(with = "hive_sh4re::wire_time::iso_opt")] pub answered_at: Option, pub answer: Option, /// Recipient of the question. `None` = the operator (dashboard diff --git a/hive-sh4re/src/wire_time.rs b/hive-sh4re/src/wire_time.rs index 680eaa87..cbc3dae7 100644 --- a/hive-sh4re/src/wire_time.rs +++ b/hive-sh4re/src/wire_time.rs @@ -16,7 +16,7 @@ //! (keep the usual `default` + `skip_serializing_if` attributes). use chrono::{DateTime, SecondsFormat, Utc}; -use serde::{Deserialize, Deserializer}; +use serde::Deserialize; /// Format unix-epoch seconds as an RFC 3339 UTC string with a `Z` /// suffix. Out-of-range values (never produced by our clocks) clamp to