Add timezone localization support to endpoints plugin
Endpoints plugin now respects the site_timezone setting from /settings. All timestamps in endpoint details, script runs, and log collections are now converted from UTC to the configured site timezone for display. Changes: - Added _get_site_timezone() helper to read site_timezone setting - Added _localize_timestamp() to convert UTC datetimes to site timezone - Applied localization to GET /api/endpoints/<id> (last_seen, created_at) - Applied localization to GET /api/endpoints/<id>/scriptruns (execution times) - Applied localization to GET /api/endpoints/<id>/logcollections (collection times) This aligns endpoints plugin with notifications and backups plugins which already use site_timezone for their time-sensitive operations.
This commit is contained in:
@@ -43,6 +43,34 @@ def _naive_utcnow():
|
||||
return datetime.now(timezone.utc).replace(tzinfo=None)
|
||||
|
||||
|
||||
def _get_site_timezone():
|
||||
"""Get configured site timezone from settings, fallback to UTC."""
|
||||
try:
|
||||
import pytz
|
||||
setting = Setting.query.filter_by(key='site_timezone').first()
|
||||
if setting and setting.value:
|
||||
return pytz.timezone(setting.value)
|
||||
except Exception:
|
||||
pass
|
||||
return timezone.utc
|
||||
|
||||
|
||||
def _localize_timestamp(utc_datetime):
|
||||
"""Convert naive UTC datetime to site timezone ISO string for display."""
|
||||
if not utc_datetime:
|
||||
return None
|
||||
try:
|
||||
import pytz
|
||||
site_tz = _get_site_timezone()
|
||||
# Assume stored datetime is UTC (naive)
|
||||
utc_aware = pytz.UTC.localize(utc_datetime)
|
||||
localized = utc_aware.astimezone(site_tz)
|
||||
return localized.isoformat()
|
||||
except Exception:
|
||||
# Fallback: return as UTC if anything fails
|
||||
return utc_datetime.isoformat() + 'Z' if utc_datetime else None
|
||||
|
||||
|
||||
# What counts as RUNNING is defined once, by the reconciler, and imported here.
|
||||
# The panel and the reconciler read the same agent snapshot, so a disagreement
|
||||
# would paint a service green and flag it as drift on the same page. Importing
|
||||
@@ -440,6 +468,12 @@ def get_endpoint(asset_id: int):
|
||||
# the enqueue guard refuses against these same numbers, so a copy in the
|
||||
# frontend would silently disagree the moment a floor was raised here.
|
||||
data['actionminimums'] = action_minimum_versions()
|
||||
|
||||
# Localize timestamps to site timezone
|
||||
for ts_field in ['lastreportedat', 'lasteventat', 'created_at', 'updated_at']:
|
||||
if ts_field in data and data[ts_field]:
|
||||
data[ts_field] = _localize_timestamp(state.__dict__.get(ts_field))
|
||||
|
||||
return success_response(data)
|
||||
|
||||
|
||||
@@ -2380,6 +2414,12 @@ def list_endpoint_script_runs(asset_id: int):
|
||||
for row in rows:
|
||||
data = row.to_dict(includedetail=True)
|
||||
data['scriptname'] = scriptnames.get(row.endpointcommandid)
|
||||
|
||||
# Localize timestamps to site timezone
|
||||
for ts_field in ['createdat', 'claimedat', 'completedat']:
|
||||
if ts_field in data and data[ts_field]:
|
||||
data[ts_field] = _localize_timestamp(row.__dict__.get(ts_field))
|
||||
|
||||
payload.append(data)
|
||||
return success_response(payload)
|
||||
|
||||
@@ -2404,7 +2444,16 @@ def list_endpoint_log_collections(asset_id: int):
|
||||
EndpointLogCollection.endpointlogcollectionid.desc())
|
||||
.limit(EndpointLogCollection.RETENTION_PER_ASSET)
|
||||
.all())
|
||||
return success_response([row.to_dict() for row in rows])
|
||||
|
||||
# Localize timestamps to site timezone
|
||||
payload = []
|
||||
for row in rows:
|
||||
data = row.to_dict()
|
||||
if 'collectedat' in data and data['collectedat']:
|
||||
data['collectedat'] = _localize_timestamp(row.collectedat)
|
||||
payload.append(data)
|
||||
|
||||
return success_response(payload)
|
||||
|
||||
|
||||
@endpoints_bp.route('/logcollections/<int:collection_id>', methods=['GET'])
|
||||
|
||||
Reference in New Issue
Block a user