PyObservability 2.0.0__py3-none-any.whl → 2.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyobservability/kuma.py +14 -29
- pyobservability/main.py +4 -6
- pyobservability/static/app.js +12 -14
- pyobservability/templates/index.html +3 -3
- pyobservability/version.py +1 -1
- {pyobservability-2.0.0.dist-info → pyobservability-2.1.0.dist-info}/METADATA +2 -2
- pyobservability-2.1.0.dist-info/RECORD +17 -0
- pyobservability-2.0.0.dist-info/RECORD +0 -17
- {pyobservability-2.0.0.dist-info → pyobservability-2.1.0.dist-info}/WHEEL +0 -0
- {pyobservability-2.0.0.dist-info → pyobservability-2.1.0.dist-info}/entry_points.txt +0 -0
- {pyobservability-2.0.0.dist-info → pyobservability-2.1.0.dist-info}/licenses/LICENSE +0 -0
- {pyobservability-2.0.0.dist-info → pyobservability-2.1.0.dist-info}/top_level.txt +0 -0
pyobservability/kuma.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import time
|
|
3
|
-
from collections import
|
|
4
|
-
from typing import Any, Dict
|
|
3
|
+
from collections.abc import Generator
|
|
4
|
+
from typing import Any, Dict
|
|
5
5
|
from urllib.parse import urlparse
|
|
6
6
|
|
|
7
7
|
import socketio
|
|
@@ -81,18 +81,16 @@ class UptimeKumaClient:
|
|
|
81
81
|
return self.monitors
|
|
82
82
|
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
"""Convert raw API payload into a list of dicts with name, url,
|
|
84
|
+
def extract_monitors(payload: Dict[int, Dict[str, Any]]) -> Generator[Dict[str, Any]]:
|
|
85
|
+
"""Convert raw API payload into a list of dicts with name, url, tags, host.
|
|
86
86
|
|
|
87
87
|
Args:
|
|
88
88
|
payload: Raw payload from Uptime Kuma server.
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
Yields:
|
|
91
|
+
Dict[str, Any]:
|
|
92
|
+
Monitors with relevant fields.
|
|
93
93
|
"""
|
|
94
|
-
monitors = []
|
|
95
|
-
|
|
96
94
|
grouped = {}
|
|
97
95
|
for monitor in payload.values():
|
|
98
96
|
if children_ids := monitor.get("childrenIDs"):
|
|
@@ -104,23 +102,10 @@ async def extract_monitors(payload: Dict[int, Dict[str, Any]]) -> List[Dict[str,
|
|
|
104
102
|
host = urlparse(url).hostname if url else None
|
|
105
103
|
if not host:
|
|
106
104
|
continue
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
)
|
|
116
|
-
return monitors
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
async def group_by_host(monitors: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
120
|
-
"""Group monitors by host."""
|
|
121
|
-
grouped = defaultdict(list)
|
|
122
|
-
|
|
123
|
-
for monitor in monitors:
|
|
124
|
-
grouped[monitor["host"]].append(monitor)
|
|
125
|
-
|
|
126
|
-
return dict(grouped)
|
|
105
|
+
yield {
|
|
106
|
+
"name": monitor.get("name"),
|
|
107
|
+
"parent": grouped.get(monitor.get("id")),
|
|
108
|
+
"url": url,
|
|
109
|
+
"host": host,
|
|
110
|
+
"tags": [tag.get("name") for tag in monitor.get("tags", []) if "name" in tag],
|
|
111
|
+
}
|
pyobservability/main.py
CHANGED
|
@@ -13,7 +13,7 @@ from fastapi.staticfiles import StaticFiles
|
|
|
13
13
|
from fastapi.templating import Jinja2Templates
|
|
14
14
|
|
|
15
15
|
from pyobservability.config import enums, settings
|
|
16
|
-
from pyobservability.kuma import UptimeKumaClient, extract_monitors
|
|
16
|
+
from pyobservability.kuma import UptimeKumaClient, extract_monitors
|
|
17
17
|
from pyobservability.transport import websocket_endpoint
|
|
18
18
|
from pyobservability.version import __version__
|
|
19
19
|
|
|
@@ -41,8 +41,8 @@ async def index(request: Request):
|
|
|
41
41
|
TemplateResponse:
|
|
42
42
|
Rendered HTML template with targets and version.
|
|
43
43
|
"""
|
|
44
|
-
kuma_data = {} if all((settings.env.kuma_url, settings.env.kuma_username, settings.env.kuma_password)) else None
|
|
45
|
-
args = dict(request=request,
|
|
44
|
+
kuma_data = [{}] if all((settings.env.kuma_url, settings.env.kuma_username, settings.env.kuma_password)) else None
|
|
45
|
+
args = dict(request=request, kuma_data=kuma_data, targets=settings.env.targets, version=__version__)
|
|
46
46
|
if settings.env.username and settings.env.password:
|
|
47
47
|
args["logout"] = uiauth.enums.APIEndpoints.fastapi_logout.value
|
|
48
48
|
return templates.TemplateResponse("index.html", args)
|
|
@@ -63,9 +63,7 @@ async def kuma():
|
|
|
63
63
|
status_code=HTTPStatus.SERVICE_UNAVAILABLE.real,
|
|
64
64
|
detail="Unable to retrieve data from kuma server.",
|
|
65
65
|
)
|
|
66
|
-
|
|
67
|
-
LOGGER.info("Extracted JSON monitors from kuma payload.")
|
|
68
|
-
return await group_by_host(json_monitors)
|
|
66
|
+
return list(extract_monitors(kuma_data))
|
|
69
67
|
|
|
70
68
|
|
|
71
69
|
async def health() -> Dict[str, str]:
|
pyobservability/static/app.js
CHANGED
|
@@ -366,26 +366,24 @@
|
|
|
366
366
|
});
|
|
367
367
|
}
|
|
368
368
|
|
|
369
|
-
function
|
|
369
|
+
function normalizeKumaMap(monitors) {
|
|
370
370
|
const rows = [];
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
URL: `<a href="${svc.url}" target="_blank">${svc.url}</a>`
|
|
379
|
-
});
|
|
371
|
+
monitors.forEach(monitor => {
|
|
372
|
+
rows.push({
|
|
373
|
+
Node: monitor.host,
|
|
374
|
+
Name: monitor.name,
|
|
375
|
+
Parent: monitor.parent || "—",
|
|
376
|
+
Tags: (monitor.tags || []).join(", "),
|
|
377
|
+
URL: `<a href="${monitor.url}" target="_blank">${monitor.url}</a>`
|
|
380
378
|
});
|
|
381
379
|
});
|
|
382
380
|
return rows;
|
|
383
381
|
}
|
|
384
382
|
|
|
385
383
|
function renderEndpoints() {
|
|
386
|
-
if (!window.
|
|
384
|
+
if (!window.KUMA_DATA) return;
|
|
387
385
|
|
|
388
|
-
const rows =
|
|
386
|
+
const rows = normalizeKumaMap(window.KUMA_DATA);
|
|
389
387
|
const columns = ["Node", "Name", "Parent", "Tags", "URL"];
|
|
390
388
|
|
|
391
389
|
PAG_ENDPOINTS.setData(rows, columns);
|
|
@@ -1197,12 +1195,12 @@
|
|
|
1197
1195
|
|
|
1198
1196
|
try {
|
|
1199
1197
|
const response = await fetch('/kuma');
|
|
1200
|
-
if (!response.ok) throw new Error('Failed to fetch
|
|
1198
|
+
if (!response.ok) throw new Error('Failed to fetch kuma map');
|
|
1201
1199
|
|
|
1202
1200
|
kumaMapData = await response.json();
|
|
1203
1201
|
kumaMapLoaded = true;
|
|
1204
1202
|
|
|
1205
|
-
allKumaRows =
|
|
1203
|
+
allKumaRows = normalizeKumaMap(kumaMapData);
|
|
1206
1204
|
PAG_KUMA_TAB.setData(allKumaRows, ["Node", "Name", "Parent", "Tags", "URL"]);
|
|
1207
1205
|
} catch (err) {
|
|
1208
1206
|
console.error("Error loading Kuma map:", err);
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
<header class="topbar">
|
|
20
20
|
<div class="brand">Node Monitor</div>
|
|
21
21
|
|
|
22
|
-
{% if
|
|
22
|
+
{% if kuma_data is not none %}
|
|
23
23
|
<div class="tab-navigation">
|
|
24
24
|
<button id="nodes-tab" class="tab-btn active">Nodes</button>
|
|
25
25
|
<button id="kuma-tab" class="tab-btn">Uptime Kuma</button>
|
|
@@ -293,9 +293,9 @@
|
|
|
293
293
|
window.MONITOR_TARGETS = {{ targets | tojson }};
|
|
294
294
|
</script>
|
|
295
295
|
|
|
296
|
-
{% if
|
|
296
|
+
{% if kuma_data is not none %}
|
|
297
297
|
<script>
|
|
298
|
-
window.
|
|
298
|
+
window.KUMA_DATA = {{ kuma_data | tojson }};
|
|
299
299
|
</script>
|
|
300
300
|
{% endif %}
|
|
301
301
|
|
pyobservability/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "2.
|
|
1
|
+
__version__ = "2.1.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyObservability
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Lightweight OS-agnostic observability UI for PyNinja
|
|
5
5
|
Author-email: Vignesh Rao <svignesh1793@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -43,7 +43,7 @@ Description-Content-Type: text/markdown
|
|
|
43
43
|
License-File: LICENSE
|
|
44
44
|
Requires-Dist: aiohttp==3.13.*
|
|
45
45
|
Requires-Dist: fastapi==0.128.*
|
|
46
|
-
Requires-Dist: FastAPI-UI-Auth==0.2
|
|
46
|
+
Requires-Dist: FastAPI-UI-Auth==0.2.*
|
|
47
47
|
Requires-Dist: Jinja2==3.1.*
|
|
48
48
|
Requires-Dist: pydantic==2.12.*
|
|
49
49
|
Requires-Dist: pydantic-settings==2.12.*
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
pyobservability/__init__.py,sha256=yVBLyTohBiBKp0Otyl04IggPh8mhg3Er25u6eFyxMto,2618
|
|
2
|
+
pyobservability/kuma.py,sha256=4sB1M7-exzrObGGIh1yYQ22P40kHwsTfv4G1Xj9YYWw,3260
|
|
3
|
+
pyobservability/main.py,sha256=c2sA-gGuAS9flO7FgeyQyzTMSHmZqhaeQAm1mWCUi9E,5992
|
|
4
|
+
pyobservability/monitor.py,sha256=i_Xf_DB-qLOp1b9wryekjwHIM8AnMrGTkuEg7e08bcM,7539
|
|
5
|
+
pyobservability/transport.py,sha256=S-84mgf-9yMj0H7VSAmueW9yosX_1XxdyNJC2EuQHQQ,8493
|
|
6
|
+
pyobservability/version.py,sha256=Xybt2skBZamGMNlLuOX1IG-h4uIxqUDGAO8MIGWrJac,22
|
|
7
|
+
pyobservability/config/enums.py,sha256=rQZh2Q4-9ItQTQgxsYjqw-jNePpWHhvQUrbrQJBG5CI,313
|
|
8
|
+
pyobservability/config/settings.py,sha256=aCz1tZEg8fUA5gHCCDN_m3fgrH1axz2fvdalj4bszGs,6121
|
|
9
|
+
pyobservability/static/app.js,sha256=XTy4lE2WxYDnJCjaigAOXde8-wZ36Wt_580HWlE3JFo,48557
|
|
10
|
+
pyobservability/static/styles.css,sha256=P6Xg-IAXO3WNeBLGH9Q5HAdNeDMRbFcM5P_cq60Jf00,9405
|
|
11
|
+
pyobservability/templates/index.html,sha256=aqvHrxP_D2gSxYmsUkNMi1QH07pQNXh4VuXc51KBo5c,11826
|
|
12
|
+
pyobservability-2.1.0.dist-info/licenses/LICENSE,sha256=_sOIKJWdD2o1WwwDIwYB2qTP2nlSWqT5Tyg9jr1Xa4w,1070
|
|
13
|
+
pyobservability-2.1.0.dist-info/METADATA,sha256=aUlk5iphyiJ9WYIUXQQVrzWBvgFrp2Q1OnSYvv7Xsug,7026
|
|
14
|
+
pyobservability-2.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
15
|
+
pyobservability-2.1.0.dist-info/entry_points.txt,sha256=DSGIr_VA8Tb3FYa2iNUYpf55eAvuFCAoInNS4ngXaME,57
|
|
16
|
+
pyobservability-2.1.0.dist-info/top_level.txt,sha256=p20T0EmihDYW1uMintRXr7X9bg3XWYKyoSbBHOVC1xI,16
|
|
17
|
+
pyobservability-2.1.0.dist-info/RECORD,,
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
pyobservability/__init__.py,sha256=yVBLyTohBiBKp0Otyl04IggPh8mhg3Er25u6eFyxMto,2618
|
|
2
|
-
pyobservability/kuma.py,sha256=RL6ZsK6ZE3vUufsUQ3r4gnH8uka0k8ARd41GP5YozbU,3634
|
|
3
|
-
pyobservability/main.py,sha256=EbP6xkOvj2cV1cOEmmq6E6QsLOhbvKI_jdpfcWjSz-M,6124
|
|
4
|
-
pyobservability/monitor.py,sha256=i_Xf_DB-qLOp1b9wryekjwHIM8AnMrGTkuEg7e08bcM,7539
|
|
5
|
-
pyobservability/transport.py,sha256=S-84mgf-9yMj0H7VSAmueW9yosX_1XxdyNJC2EuQHQQ,8493
|
|
6
|
-
pyobservability/version.py,sha256=_7OlQdbVkK4jad0CLdpI0grT-zEAb-qgFmH5mFzDXiA,22
|
|
7
|
-
pyobservability/config/enums.py,sha256=rQZh2Q4-9ItQTQgxsYjqw-jNePpWHhvQUrbrQJBG5CI,313
|
|
8
|
-
pyobservability/config/settings.py,sha256=aCz1tZEg8fUA5gHCCDN_m3fgrH1axz2fvdalj4bszGs,6121
|
|
9
|
-
pyobservability/static/app.js,sha256=lE-Oybq50FNr_0SrZJFiJihiXyIcyhw7-SNbGA81ySQ,48675
|
|
10
|
-
pyobservability/static/styles.css,sha256=P6Xg-IAXO3WNeBLGH9Q5HAdNeDMRbFcM5P_cq60Jf00,9405
|
|
11
|
-
pyobservability/templates/index.html,sha256=WQNOT_uHfAvVqOnYc1olYdyrjM11tDpeKwTkbPQ853Q,11834
|
|
12
|
-
pyobservability-2.0.0.dist-info/licenses/LICENSE,sha256=_sOIKJWdD2o1WwwDIwYB2qTP2nlSWqT5Tyg9jr1Xa4w,1070
|
|
13
|
-
pyobservability-2.0.0.dist-info/METADATA,sha256=hu_7k6rRxUXitpfLhkAB6GZxMYQNDeqGaQAHJDKa0Ss,7026
|
|
14
|
-
pyobservability-2.0.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
15
|
-
pyobservability-2.0.0.dist-info/entry_points.txt,sha256=DSGIr_VA8Tb3FYa2iNUYpf55eAvuFCAoInNS4ngXaME,57
|
|
16
|
-
pyobservability-2.0.0.dist-info/top_level.txt,sha256=p20T0EmihDYW1uMintRXr7X9bg3XWYKyoSbBHOVC1xI,16
|
|
17
|
-
pyobservability-2.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|