diff --git a/bridge/server.py b/bridge/server.py index 81e29f3..9c0cd21 100644 --- a/bridge/server.py +++ b/bridge/server.py @@ -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)