bridge: configurable Nextcloud request timeout, dynamic Health metric discovery

Auto-detect which Health metrics the instance actually knows and enable
any that are known but disabled for the user, via GET/PUT configuration.
Also makes the previously hardcoded 10s request timeout configurable via
NC_TIMEOUT_SECONDS to accommodate slower instances.
This commit is contained in:
Matthias Jacob
2026-09-17 03:48:07 +02:00
parent 37f3399953
commit b3ed4cf430
+51 -2
View File
@@ -31,10 +31,58 @@ except KeyError:
credentials = json.loads(CREDENTIALS_FILE.read_text())
NC_SERVER = credentials["server"]
NC_AUTH = (credentials["login_name"], credentials["app_password"])
ENTRIES_URL = f"{NC_SERVER}/ocs/v2.php/apps/health/api/v2/entries"
HEALTH_API = f"{NC_SERVER}/ocs/v2.php/apps/health/api/v2"
ENTRIES_URL = f"{HEALTH_API}/entries"
CONFIGURATION_URL = f"{HEALTH_API}/configuration"
# Identifies this bridge in Nextcloud's server logs and admin UI instead of the
# default "python-requests/x.y.z".
USER_AGENT = "PrivateHealthBridge"
# Some instances (cold PHP-FPM, slow DB) need more than a few seconds to answer.
NC_TIMEOUT_SECONDS = float(os.environ.get("NC_TIMEOUT_SECONDS", "30"))
# Every metric this bridge can forward; new users only have "stress" enabled
# by default, so writes would otherwise fail with an "unsupported metric" error.
WANTED_METRICS = ("weight",)
# Filled at startup from the server's actual configuration response, since a
# Health instance may not know a metric yet at all or may just have it
# disabled for this user.
supported_metrics: set[str] = set()
def discover_supported_metrics() -> None:
resp = requests.get(
CONFIGURATION_URL,
auth=NC_AUTH,
headers={"OCS-APIRequest": "true", "Accept": "application/json", "User-Agent": USER_AGENT},
params={"format": "json"},
timeout=NC_TIMEOUT_SECONDS,
)
resp.raise_for_status()
current = resp.json()["ocs"]["data"]["metrics"]
unknown = [m for m in WANTED_METRICS if m not in current]
if unknown:
log.warning("This Health instance doesn't know these metrics yet, skipping them: %s", unknown)
disabled = [m for m in WANTED_METRICS if m in current and not current[m]["enabled"]]
if disabled:
log.info("Enabling Health metrics not yet active for this user: %s", disabled)
resp = requests.put(
CONFIGURATION_URL,
json={"metrics": {m: {"enabled": True} for m in disabled}},
auth=NC_AUTH,
headers={
"OCS-APIRequest": "true",
"Accept": "application/json",
"User-Agent": USER_AGENT,
},
params={"format": "json"},
timeout=NC_TIMEOUT_SECONDS,
)
if not resp.ok:
log.error("Nextcloud rejected configuration update: %s -> %s", resp.status_code, resp.text)
resp.raise_for_status()
supported_metrics.update(m for m in WANTED_METRICS if m not in unknown)
app = Flask(__name__)
@@ -80,7 +128,7 @@ def forward_weight_entry(record: dict) -> None:
"User-Agent": USER_AGENT,
},
params={"format": "json"},
timeout=10,
timeout=NC_TIMEOUT_SECONDS,
)
resp.raise_for_status()
@@ -112,4 +160,5 @@ def health_connect_webhook():
if __name__ == "__main__":
discover_supported_metrics()
app.run(host="0.0.0.0", port=8080)