]> code.delx.au - monosys/blob - hacks/syncthing-healthcheck
notes: fix raspi install notes, also @home -> @username
[monosys] / hacks / syncthing-healthcheck
1 #!/usr/bin/env node
2
3 /* eslint-env es2020 */
4
5 const API_URL = process.env.API_URL;
6 const API_KEY = process.env.API_KEY;
7 const VERBOSE = !!process.env.VERBOSE;
8
9 async function main() {
10 const config = await fetchConfig();
11 const devicesStatus = await fetchDevicesStatus();
12 const connectionsStatus = await fetchConnections();
13
14 for (const {deviceID, name} of config.devices) {
15 const {connected} = connectionsStatus.connections[deviceID];
16 const {lastSeen: lastSeenString} = devicesStatus[deviceID];
17 const lastSeenDate = new Date(lastSeenString);
18 const {completion, needItems} = await fetchCompletion(deviceID);
19 //console.log(name, await fetchCompletion(deviceID));
20
21 checkDevice({name, connected, lastSeenDate, completion, needItems});
22 }
23 }
24
25 async function checkDevice({name, connected, lastSeenDate, completion, needItems}) {
26 if (VERBOSE) {
27 console.error('checkDevice', {name, connected, lastSeenDate, completion, needItems});
28 }
29
30 if (lastSeenDate.getTime() === 0 || connected) {
31 return;
32 }
33
34 if (lastSeenDate < newDateDays(-10)) {
35 console.log(`Alert! '${name}' has been missing since ` +
36 lastSeenDate.toISOString().replace(/T.*/, ''));
37 }
38 if (needItems > 0 || completion < 100) {
39 console.log(`Alert! '${name}' is out of sync! ` +
40 `${Math.round(completion)}%, missing ${needItems} files.`);
41 }
42 }
43
44 function newDateDays(days) {
45 return new Date(Date.now() + days * 24 * 3600 * 1000);
46 }
47
48 async function fetchConfig() {
49 const response = await fetchSyncthing('/rest/config');
50 await assertResponseOk(response);
51 return response.json();
52 }
53
54 async function fetchDevicesStatus() {
55 const response = await fetchSyncthing('/rest/stats/device');
56 await assertResponseOk(response);
57 return response.json();
58 }
59
60 async function fetchConnections() {
61 const response = await fetchSyncthing('/rest/system/connections');
62 await assertResponseOk(response);
63 return response.json();
64 }
65
66 async function fetchCompletion(deviceID) {
67 const response = await fetchSyncthing(`/rest/db/completion?device=${deviceID}`);
68 await assertResponseOk(response);
69 return response.json();
70 }
71
72 async function fetchSyncthing(path) {
73 return fetch(`${API_URL}${path}`, {
74 headers: {
75 'X-API-Key': API_KEY,
76 }
77 });
78 }
79
80 async function assertResponseOk(response) {
81 if (response.ok) {
82 return;
83 }
84 throw new Error(
85 'Response error! ' +
86 response.status + ' ' + response.statusText +
87 ' -- ' + (await response.text())
88 );
89 }
90
91 main().catch((err) => {
92 console.error('Exiting due to error!', err);
93 process.exit(1);
94 });