From e2785899a2c5c6ef1ec8af0ab54bd93fc3ddd715 Mon Sep 17 00:00:00 2001 From: AWOOOI CD Date: Wed, 13 May 2026 12:17:10 +0800 Subject: [PATCH 1/4] chore(cd): deploy e57474a [skip ci] --- k8s/awoooi-prod/kustomization.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/k8s/awoooi-prod/kustomization.yaml b/k8s/awoooi-prod/kustomization.yaml index 8f6ba36f..b862fc0b 100644 --- a/k8s/awoooi-prod/kustomization.yaml +++ b/k8s/awoooi-prod/kustomization.yaml @@ -40,7 +40,7 @@ resources: images: - name: 192.168.0.110:5000/library/api:IMAGE_TAG_PLACEHOLDER newName: 192.168.0.110:5000/awoooi/api - newTag: 7fa9f743ddaaa16c9f9225ecff8816a014b8bc25 + newTag: e57474adfb222ac069645a40f15e6b8239071675 - name: 192.168.0.110:5000/library/web:IMAGE_TAG_PLACEHOLDER newName: 192.168.0.110:5000/awoooi/web - newTag: 7fa9f743ddaaa16c9f9225ecff8816a014b8bc25 + newTag: e57474adfb222ac069645a40f15e6b8239071675 From d449ba4720c23424a43366b284175da88fd861bc Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 13 May 2026 12:20:15 +0800 Subject: [PATCH 2/4] fix(awooop): write outbound sent timestamp as parameter --- apps/api/src/services/channel_hub.py | 5 +++-- apps/api/tests/test_channel_hub_grouped_alert_events.py | 3 ++- docs/LOGBOOK.md | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/api/src/services/channel_hub.py b/apps/api/src/services/channel_hub.py index facf0395..e6903986 100644 --- a/apps/api/src/services/channel_hub.py +++ b/apps/api/src/services/channel_hub.py @@ -489,6 +489,7 @@ async def record_outbound_message( source_envelope_json = json.dumps(envelope, ensure_ascii=False, default=str) actual_status = "shadow" if is_shadow else send_status + sent_at = datetime.now(UTC) if actual_status == "sent" else None await ensure_completed_shadow_run( db, @@ -522,8 +523,7 @@ async def record_outbound_message( :content_hash, :content_preview, :content_redacted, :redaction_version, CAST(:source_envelope AS jsonb), :provider_message_id, - :send_status, NOW(), - CASE WHEN CAST(:send_status AS text) = 'sent' THEN NOW() ELSE NULL END, + :send_status, NOW(), :sent_at, :triggered_by_state, :waiting_since ) RETURNING message_id @@ -542,6 +542,7 @@ async def record_outbound_message( "source_envelope": source_envelope_json, "provider_message_id": provider_message_id, "send_status": actual_status, + "sent_at": sent_at, "triggered_by_state": triggered_by_state, "waiting_since": waiting_since, }, diff --git a/apps/api/tests/test_channel_hub_grouped_alert_events.py b/apps/api/tests/test_channel_hub_grouped_alert_events.py index 44e23b4a..cd1714b6 100644 --- a/apps/api/tests/test_channel_hub_grouped_alert_events.py +++ b/apps/api/tests/test_channel_hub_grouped_alert_events.py @@ -132,5 +132,6 @@ async def test_record_outbound_message_sets_sent_at_for_sent_messages() -> None: insert_statement = session.statements[-1] assert "sent_at" in insert_statement - assert "CASE WHEN CAST(:send_status AS text) = 'sent' THEN NOW() ELSE NULL END" in insert_statement + assert ":sent_at" in insert_statement assert session.param_sets[-1]["send_status"] == "sent" + assert session.param_sets[-1]["sent_at"] is not None diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 0a3d5209..20d24276 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -7307,6 +7307,6 @@ OK **production smoke 途中補修**: - rollback transaction smoke 抓到 asyncpg bind parameter 型別推論問題:`CASE WHEN :send_status = 'sent'` 會被推成 text/varchar ambiguous。 -- 已改成 `CASE WHEN CAST(:send_status AS text) = 'sent' THEN NOW() ELSE NULL END`,避免 outbound mirror 在 production 寫入時失敗。 +- 第一版 `CAST(:send_status AS text)` 仍會因同一 bind param 同時插入 varchar 與比較而 ambiguous;最終改成 Python 端計算 `sent_at` 參數,SQL 只插入 `:sent_at`,避免 outbound mirror 在 production 寫入時失敗。 **目前整體進度**:約 69%。 From 3a1cedc90d9294114bae3da30ff6e28aecf93977 Mon Sep 17 00:00:00 2001 From: AWOOOI CD Date: Wed, 13 May 2026 04:25:23 +0000 Subject: [PATCH 3/4] chore(cd): deploy d449ba4 [skip ci] --- k8s/awoooi-prod/kustomization.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/k8s/awoooi-prod/kustomization.yaml b/k8s/awoooi-prod/kustomization.yaml index b862fc0b..b2babe2b 100644 --- a/k8s/awoooi-prod/kustomization.yaml +++ b/k8s/awoooi-prod/kustomization.yaml @@ -40,7 +40,7 @@ resources: images: - name: 192.168.0.110:5000/library/api:IMAGE_TAG_PLACEHOLDER newName: 192.168.0.110:5000/awoooi/api - newTag: e57474adfb222ac069645a40f15e6b8239071675 + newTag: d449ba4720c23424a43366b284175da88fd861bc - name: 192.168.0.110:5000/library/web:IMAGE_TAG_PLACEHOLDER newName: 192.168.0.110:5000/awoooi/web - newTag: e57474adfb222ac069645a40f15e6b8239071675 + newTag: d449ba4720c23424a43366b284175da88fd861bc From 04c7bb1c9700a964355e8232147c0e80a191495c Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 13 May 2026 12:27:58 +0800 Subject: [PATCH 4/4] fix(awooop): store outbound sent timestamp as naive utc --- apps/api/src/services/channel_hub.py | 6 +++++- apps/api/tests/test_channel_hub_grouped_alert_events.py | 1 + docs/LOGBOOK.md | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/api/src/services/channel_hub.py b/apps/api/src/services/channel_hub.py index e6903986..5b8e7c65 100644 --- a/apps/api/src/services/channel_hub.py +++ b/apps/api/src/services/channel_hub.py @@ -489,7 +489,11 @@ async def record_outbound_message( source_envelope_json = json.dumps(envelope, ensure_ascii=False, default=str) actual_status = "shadow" if is_shadow else send_status - sent_at = datetime.now(UTC) if actual_status == "sent" else None + sent_at = ( + datetime.now(UTC).replace(tzinfo=None) + if actual_status == "sent" + else None + ) await ensure_completed_shadow_run( db, diff --git a/apps/api/tests/test_channel_hub_grouped_alert_events.py b/apps/api/tests/test_channel_hub_grouped_alert_events.py index cd1714b6..02f08ea4 100644 --- a/apps/api/tests/test_channel_hub_grouped_alert_events.py +++ b/apps/api/tests/test_channel_hub_grouped_alert_events.py @@ -135,3 +135,4 @@ async def test_record_outbound_message_sets_sent_at_for_sent_messages() -> None: assert ":sent_at" in insert_statement assert session.param_sets[-1]["send_status"] == "sent" assert session.param_sets[-1]["sent_at"] is not None + assert session.param_sets[-1]["sent_at"].tzinfo is None diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 20d24276..c9192f7e 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -7308,5 +7308,6 @@ OK - rollback transaction smoke 抓到 asyncpg bind parameter 型別推論問題:`CASE WHEN :send_status = 'sent'` 會被推成 text/varchar ambiguous。 - 第一版 `CAST(:send_status AS text)` 仍會因同一 bind param 同時插入 varchar 與比較而 ambiguous;最終改成 Python 端計算 `sent_at` 參數,SQL 只插入 `:sent_at`,避免 outbound mirror 在 production 寫入時失敗。 +- 第二次 rollback smoke 抓到 DB 欄位是 `timestamp without time zone`,已改成 naive UTC `datetime.now(UTC).replace(tzinfo=None)`,避免 asyncpg timezone-aware bind 失敗。 **目前整體進度**:約 69%。