From 816411fe22a2e6a34adddba431a72e44506e1156 Mon Sep 17 00:00:00 2001 From: James Bunton Date: Tue, 30 Jan 2024 07:38:43 +1100 Subject: [PATCH 1/1] syncthing-healthcheck --- hacks/syncthing-healthcheck | 94 +++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100755 hacks/syncthing-healthcheck diff --git a/hacks/syncthing-healthcheck b/hacks/syncthing-healthcheck new file mode 100755 index 0000000..ffc668e --- /dev/null +++ b/hacks/syncthing-healthcheck @@ -0,0 +1,94 @@ +#!/usr/bin/env node + +/* eslint-env es2020 */ + +const API_URL = process.env.API_URL; +const API_KEY = process.env.API_KEY; +const VERBOSE = !!process.env.VERBOSE; + +async function main() { + const config = await fetchConfig(); + const devicesStatus = await fetchDevicesStatus(); + const connectionsStatus = await fetchConnections(); + + for (const {deviceID, name} of config.devices) { + const {connected} = connectionsStatus.connections[deviceID]; + const {lastSeen: lastSeenString} = devicesStatus[deviceID]; + const lastSeenDate = new Date(lastSeenString); + const {completion, needItems} = await fetchCompletion(deviceID); + //console.log(name, await fetchCompletion(deviceID)); + + checkDevice({name, connected, lastSeenDate, completion, needItems}); + } +} + +async function checkDevice({name, connected, lastSeenDate, completion, needItems}) { + if (VERBOSE) { + console.error('checkDevice', {name, connected, lastSeenDate, completion, needItems}); + } + + if (lastSeenDate.getTime() === 0 || connected) { + return; + } + + if (lastSeenDate < newDateDays(-10)) { + console.log(`Alert! '${name}' has been missing since ` + + lastSeenDate.toISOString().replace(/T.*/, '')); + } + if (needItems > 0 || completion < 100) { + console.log(`Alert! '${name}' is out of sync! ` + + `${Math.round(completion)}%, missing ${needItems} files.`); + } +} + +function newDateDays(days) { + return new Date(Date.now() + days * 24 * 3600 * 1000); +} + +async function fetchConfig() { + const response = await fetchSyncthing('/rest/config'); + await assertResponseOk(response); + return response.json(); +} + +async function fetchDevicesStatus() { + const response = await fetchSyncthing('/rest/stats/device'); + await assertResponseOk(response); + return response.json(); +} + +async function fetchConnections() { + const response = await fetchSyncthing('/rest/system/connections'); + await assertResponseOk(response); + return response.json(); +} + +async function fetchCompletion(deviceID) { + const response = await fetchSyncthing(`/rest/db/completion?device=${deviceID}`); + await assertResponseOk(response); + return response.json(); +} + +async function fetchSyncthing(path) { + return fetch(`${API_URL}${path}`, { + headers: { + 'X-API-Key': API_KEY, + } + }); +} + +async function assertResponseOk(response) { + if (response.ok) { + return; + } + throw new Error( + 'Response error! ' + + response.status + ' ' + response.statusText + + ' -- ' + (await response.text()) + ); +} + +main().catch((err) => { + console.error('Exiting due to error!', err); + process.exit(1); +}); -- 2.39.2