feat: Add Uptime Kuma version check via Socket.IO
Retrieve the running Uptime Kuma version by authenticating over Socket.IO and reading the info event. This adds a new login_socket dataset field, the fetch_socket_info helper, and the python-socketio and websocket-client dependencies.
This commit is contained in:
parent
501a84e0a9
commit
f34e2f2d08
6 changed files with 90 additions and 0 deletions
|
|
@ -15,3 +15,7 @@ MATRIX_ACCESS_TOKEN=replace-with-access-token
|
|||
|
||||
# Uptime Kuma push URL
|
||||
UPTIME_KUMA_PUSH_URL=https://uptime.example.net/api/push/your-token
|
||||
|
||||
# Uptime Kuma login for version check
|
||||
UPTIME_KUMA_USERNAME=your-username
|
||||
UPTIME_KUMA_PASSWORD=your-password
|
||||
|
|
|
|||
|
|
@ -152,3 +152,12 @@ Acceptance criteria:
|
|||
- The script sends a request to the push URL when it runs.
|
||||
- The push URL is configured via `.env`.
|
||||
- Failures to reach the push URL are logged without crashing the run.
|
||||
|
||||
## US-17 - Uptime Kuma Service Check
|
||||
|
||||
As a maintainer, I want to track the running version of Uptime Kuma so updates are reported like other services.
|
||||
|
||||
Acceptance criteria:
|
||||
- The dataset includes Uptime Kuma with a current version endpoint and extraction rule.
|
||||
- The upstream latest version uses the official GitHub releases.
|
||||
- Any required authentication is documented in the dataset.
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ export MATRIX_HOMESERVER=...
|
|||
export MATRIX_ROOM_ID=...
|
||||
export MATRIX_ACCESS_TOKEN=...
|
||||
export UPTIME_KUMA_PUSH_URL=...
|
||||
export UPTIME_KUMA_USERNAME=...
|
||||
export UPTIME_KUMA_PASSWORD=...
|
||||
```
|
||||
|
||||
The script also reads `.env` automatically if present.
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ from urllib.parse import urlencode, quote
|
|||
from urllib.request import HTTPCookieProcessor, Request, build_opener, urlopen
|
||||
|
||||
import bcrypt
|
||||
import socketio
|
||||
|
||||
import yaml
|
||||
|
||||
|
|
@ -34,6 +35,7 @@ class ServiceConfig:
|
|||
upstream_latest_headers: Optional[Dict[str, str]]
|
||||
notes: Optional[str]
|
||||
login: Optional[Dict[str, Any]]
|
||||
login_socket: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
def load_yaml(path: str) -> Dict[str, Any]:
|
||||
|
|
@ -109,6 +111,10 @@ def load_services(config: Dict[str, Any]) -> Dict[str, ServiceConfig]:
|
|||
if login is not None and not isinstance(login, dict):
|
||||
raise ValueError("login must be a mapping")
|
||||
|
||||
login_socket = entry.get("login_socket")
|
||||
if login_socket is not None and not isinstance(login_socket, dict):
|
||||
raise ValueError("login_socket must be a mapping")
|
||||
|
||||
loaded[name] = ServiceConfig(
|
||||
name=name,
|
||||
base_url=base_url,
|
||||
|
|
@ -120,6 +126,7 @@ def load_services(config: Dict[str, Any]) -> Dict[str, ServiceConfig]:
|
|||
upstream_latest_headers=upstream_headers,
|
||||
notes=entry.get("notes"),
|
||||
login=login,
|
||||
login_socket=login_socket,
|
||||
)
|
||||
return loaded
|
||||
|
||||
|
|
@ -414,6 +421,53 @@ def authenticate_service(service: ServiceConfig, timeout: float, user_agent: str
|
|||
return "; ".join(cookies)
|
||||
|
||||
|
||||
def fetch_socket_info(service: ServiceConfig, timeout: float) -> Optional[str]:
|
||||
if not service.login_socket:
|
||||
return None
|
||||
base_url = service.login_socket.get("url")
|
||||
username_env = service.login_socket.get("username_env")
|
||||
password_env = service.login_socket.get("password_env")
|
||||
if not base_url or not username_env or not password_env:
|
||||
raise ValueError(f"Login socket config incomplete for {service.name}")
|
||||
|
||||
username = os.getenv(username_env)
|
||||
password = os.getenv(password_env)
|
||||
if not username or not password:
|
||||
raise ValueError(f"Missing credentials for {service.name}")
|
||||
|
||||
info_payload: Dict[str, Any] = {}
|
||||
sio = socketio.Client(reconnection=False, request_timeout=timeout)
|
||||
|
||||
@sio.on("info")
|
||||
def on_info(data):
|
||||
nonlocal info_payload
|
||||
info_payload = data or {}
|
||||
|
||||
sio.connect(base_url, transports=["websocket"], wait_timeout=timeout)
|
||||
|
||||
response = sio.call(
|
||||
"login",
|
||||
{
|
||||
"username": username,
|
||||
"password": password,
|
||||
"token": "",
|
||||
},
|
||||
timeout=timeout,
|
||||
)
|
||||
if not response or not response.get("ok"):
|
||||
sio.disconnect()
|
||||
raise ValueError(f"Socket login failed for {service.name}")
|
||||
|
||||
if not info_payload.get("version"):
|
||||
try:
|
||||
info_payload = sio.call("info", timeout=timeout) or info_payload
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
sio.disconnect()
|
||||
return info_payload.get("version")
|
||||
|
||||
|
||||
def check_service(service: ServiceConfig, timeout: float, user_agent: str) -> Dict[str, Any]:
|
||||
result: Dict[str, Any] = {
|
||||
"name": service.name,
|
||||
|
|
@ -444,6 +498,11 @@ def check_service(service: ServiceConfig, timeout: float, user_agent: str) -> Di
|
|||
)
|
||||
except Exception as exc:
|
||||
result["current_error"] = str(exc)
|
||||
elif service.login_socket:
|
||||
try:
|
||||
result["current"] = fetch_socket_info(service, timeout)
|
||||
except Exception as exc:
|
||||
result["current_error"] = str(exc)
|
||||
|
||||
if service.upstream_latest_version_url and service.upstream_latest_extract:
|
||||
headers = build_headers(service.upstream_latest_headers)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ requires-python = ">=3.10"
|
|||
dependencies = [
|
||||
"PyYAML>=6.0",
|
||||
"bcrypt>=4.0",
|
||||
"python-socketio>=5.11",
|
||||
"requests>=2.31",
|
||||
"websocket-client>=1.7",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
|
|
|
|||
|
|
@ -83,6 +83,19 @@ services:
|
|||
upstream_latest_extract:
|
||||
type: jsonpath
|
||||
value: $.tag_name
|
||||
- name: Uptime Kuma
|
||||
base_url: https://uptime.jeena.net/
|
||||
current_version_url:
|
||||
current_version_extract:
|
||||
login_socket:
|
||||
url: https://uptime.jeena.net/
|
||||
username_env: UPTIME_KUMA_USERNAME
|
||||
password_env: UPTIME_KUMA_PASSWORD
|
||||
upstream_latest_version_url: https://api.github.com/repos/louislam/uptime-kuma/releases/latest
|
||||
upstream_latest_extract:
|
||||
type: jsonpath
|
||||
value: $.tag_name
|
||||
notes: Version retrieved via authenticated Socket.IO info event.
|
||||
- name: FreshRSS
|
||||
base_url: https://rss.jeena.net/
|
||||
current_version_url: https://rss.jeena.net/i/?a=about
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue