From 5ef5701c9fdfca23c0b27894f47ec27f1dcb5462 Mon Sep 17 00:00:00 2001 From: Rerowros <85008083+Rerowros@users.noreply.github.com> Date: Sun, 10 May 2026 22:42:20 +0700 Subject: [PATCH] fix: serialize controller datetimes as UTC JSON --- PasarGuardNodeBridge/controller.py | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/PasarGuardNodeBridge/controller.py b/PasarGuardNodeBridge/controller.py index 8664fd7..3e9ad80 100644 --- a/PasarGuardNodeBridge/controller.py +++ b/PasarGuardNodeBridge/controller.py @@ -2,9 +2,11 @@ import logging import math import ssl +import time +from datetime import datetime, timezone from enum import IntEnum from json import JSONDecodeError -from typing import Optional +from typing import Any, Optional from uuid import UUID import aiohttp @@ -25,6 +27,22 @@ DEFAULT_INTERNAL_TIMEOUT = 15 # Default timeout for internal gRPC/HTTP operations +class UTCFormatter(logging.Formatter): + converter = time.gmtime + + +def _json_safe(value: Any) -> Any: + if isinstance(value, datetime): + if value.tzinfo is None or value.utcoffset() is None: + raise NodeAPIError(code=-6, detail="Naive datetime values are not supported in JSON payloads") + return value.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") + if isinstance(value, dict): + return {key: _json_safe(val) for key, val in value.items()} + if isinstance(value, (list, tuple)): + return [_json_safe(item) for item in value] + return value + + class NodeAPIError(Exception): def __init__(self, code, detail): self.code = code @@ -61,7 +79,12 @@ def __init__( logger = logging.getLogger(self.name) logger.setLevel(logging.INFO) handler = logging.StreamHandler() - handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")) + handler.setFormatter( + UTCFormatter( + "%(asctime)s - %(name)s - %(levelname)s - %(message)s", + datefmt="%Y-%m-%dT%H:%M:%SZ", + ) + ) logger.addHandler(handler) self.logger = logger @@ -532,7 +555,7 @@ async def _make_json_request( try: async with self._json_client.request( - method=method, url=endpoint, json=json, timeout=make_timeout(timeout) + method=method, url=endpoint, json=_json_safe(json), timeout=make_timeout(timeout) ) as raw_response: response = await buffer_response(raw_response) response.raise_for_status()