mirror of
https://github.com/unraid/api.git
synced 2026-01-16 13:39:51 -06:00
fix: nchan oom
This commit is contained in:
@@ -7,12 +7,11 @@ import path from 'path';
|
||||
import glob from 'glob';
|
||||
import camelCase from 'camelcase';
|
||||
import globby from 'globby';
|
||||
import pWaitFor from 'p-wait-for';
|
||||
import pIteration from 'p-iteration';
|
||||
import clearModule from 'clear-module';
|
||||
import { coreLogger } from './log';
|
||||
import { paths } from './paths';
|
||||
import { subscribeToNchanEndpoint, isNchanUp } from './utils';
|
||||
import { subscribeToNchanEndpoint } from './utils';
|
||||
import { config } from './config';
|
||||
import { pluginManager } from './plugin-manager';
|
||||
import * as watchers from './watchers';
|
||||
@@ -160,23 +159,8 @@ const loadNchan = async (): Promise<void> => {
|
||||
|
||||
coreLogger.debug('Trying to connect to nchan');
|
||||
|
||||
// Wait for nchan to be up.
|
||||
await pWaitFor(isNchanUp, {
|
||||
timeout: TEN_SECONDS,
|
||||
interval: ONE_SECOND
|
||||
})
|
||||
// Once connected open a connection to each known endpoint
|
||||
.then(async () => connectToNchanEndpoints(endpoints))
|
||||
.catch(error => {
|
||||
// Nchan is likely unreachable
|
||||
if (error.message.includes('Promise timed out')) {
|
||||
coreLogger.error('Nchan timed out while trying to establish a connection.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Some other error occured
|
||||
throw error;
|
||||
});
|
||||
// Connect to each known endpoint
|
||||
await connectToNchanEndpoints(endpoints);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,38 +3,31 @@
|
||||
* Written by: Alexis Tyler
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import fetch from 'node-fetch';
|
||||
import { debugTimer, parseConfig, sleep } from '..';
|
||||
import xhr2 from 'xhr2';
|
||||
import windowPolyFill from 'node-window-polyfill';
|
||||
import { EventSource } from 'launchdarkly-eventsource';
|
||||
import { parseConfig } from '..';
|
||||
import * as states from '../../states';
|
||||
import { coreLogger } from '../../log';
|
||||
import { log } from '../../log';
|
||||
import { AppError } from '../../errors';
|
||||
|
||||
const data = {};
|
||||
const nchanLogger = log.createChild({
|
||||
prefix: 'nchan'
|
||||
});
|
||||
|
||||
// Load polyfills for nchan
|
||||
windowPolyFill.register(false);
|
||||
global.XMLHttpRequest = xhr2;
|
||||
global.EventSource = EventSource;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const NchanSubscriber = require('nchan');
|
||||
|
||||
const getSubEndpoint = () => {
|
||||
const httpPort: string = states.varState.data?.port;
|
||||
return `http://localhost:${httpPort}/sub`;
|
||||
};
|
||||
|
||||
export const isNchanUp = async () => {
|
||||
const isUp = await fetch(`${getSubEndpoint()}/non-existant`, {
|
||||
method: 'HEAD'
|
||||
})
|
||||
.then(() => true)
|
||||
.catch(error => {
|
||||
// Socket is up but this endpoint is invalid
|
||||
// That's to be expected though.
|
||||
if (error.code === 'ECONNRESET') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return isUp;
|
||||
};
|
||||
|
||||
const endpointToStateMapping = {
|
||||
// Cpuload: ,
|
||||
devs: states.devicesState,
|
||||
@@ -49,66 +42,34 @@ const endpointToStateMapping = {
|
||||
var: states.varState
|
||||
};
|
||||
|
||||
const subscribe = async (endpoint: string) => {
|
||||
// Wait 1s before subscribing
|
||||
await sleep(1000);
|
||||
|
||||
debugTimer(`subscribe(${endpoint})`);
|
||||
const response = await fetch(`${getSubEndpoint()}/${endpoint}`).catch(async () => {
|
||||
// If we throw then let's check if nchan is down
|
||||
// or if it's an actual error
|
||||
const isUp = await isNchanUp();
|
||||
|
||||
if (isUp) {
|
||||
throw new AppError(`Cannot connect to nchan at ${getSubEndpoint()}/${endpoint}`);
|
||||
}
|
||||
|
||||
throw new AppError('Cannot connect to nchan');
|
||||
const subscribe = async (endpoint: string) => new Promise<void>(resolve => {
|
||||
const sub = new NchanSubscriber(`${getSubEndpoint()}/${endpoint}`, {
|
||||
subscriber: 'eventsource'
|
||||
});
|
||||
|
||||
if (response.status === 502) {
|
||||
// Status 502 is a connection timeout error,
|
||||
// may happen when the connection was pending for too long,
|
||||
// and the remote server or a proxy closed it
|
||||
// let's reconnect
|
||||
await subscribe(endpoint);
|
||||
} else if (response.status === 200) {
|
||||
// Get and show the message
|
||||
const message = await response.text();
|
||||
sub.on('connect', function (_event) {
|
||||
nchanLogger.debug('Connected!');
|
||||
resolve();
|
||||
});
|
||||
|
||||
// Create endpoint field on data
|
||||
if (!data[endpoint]) {
|
||||
const fileName = endpoint + '.js';
|
||||
data[endpoint] = {
|
||||
handlerPath: path.resolve(__dirname, '../../states', fileName)
|
||||
};
|
||||
}
|
||||
sub.on('message', function (message, _messageMetadata) {
|
||||
try {
|
||||
const state = parseConfig({
|
||||
file: message,
|
||||
type: 'ini'
|
||||
});
|
||||
|
||||
// Only re-run parser if the message changed
|
||||
if (data[endpoint].message !== message) {
|
||||
data[endpoint].updated = new Date();
|
||||
data[endpoint].message = message;
|
||||
// Update state
|
||||
endpointToStateMapping[endpoint].parse(state);
|
||||
} catch {}
|
||||
});
|
||||
|
||||
try {
|
||||
const state = parseConfig({
|
||||
file: message,
|
||||
type: 'ini'
|
||||
});
|
||||
sub.on('error', function (error, error_description) {
|
||||
nchanLogger.error('Error: "%s" \nDescription: "%s"', error, error_description);
|
||||
});
|
||||
|
||||
// Update state
|
||||
endpointToStateMapping[endpoint].parse(state);
|
||||
} catch { }
|
||||
}
|
||||
|
||||
debugTimer(`subscribe(${endpoint})`);
|
||||
} else {
|
||||
// An error - let's show it
|
||||
coreLogger.error(JSON.stringify(response));
|
||||
}
|
||||
|
||||
// Re-subscribe
|
||||
await subscribe(endpoint);
|
||||
};
|
||||
sub.start();
|
||||
});
|
||||
|
||||
export const subscribeToNchanEndpoint = async (endpoint: string) => {
|
||||
if (!Object.keys(endpointToStateMapping).includes(endpoint)) {
|
||||
|
||||
74
package-lock.json
generated
74
package-lock.json
generated
@@ -1244,6 +1244,12 @@
|
||||
"integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/nanoajax": {
|
||||
"version": "0.2.30",
|
||||
"resolved": "https://registry.npmjs.org/@types/nanoajax/-/nanoajax-0.2.30.tgz",
|
||||
"integrity": "sha512-gsC1dTR1cC3BVXQ0J3ZSe5Romn4BjdkJF1Q2aLcT+wpmFfYcfcNarn4nmLT+EZUoJcs7tjj7rezxJFgxZ1uT4g==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "14.14.22",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.22.tgz",
|
||||
@@ -9189,6 +9195,14 @@
|
||||
"package-json": "^6.3.0"
|
||||
}
|
||||
},
|
||||
"launchdarkly-eventsource": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/launchdarkly-eventsource/-/launchdarkly-eventsource-1.4.0.tgz",
|
||||
"integrity": "sha512-NhyafaXyX2EuO8fDlQORY51eeDx0w5TJWVNqbjDc+3N69grj0zt9FYf8TE+KoFPL1IoxZMLq9mmPvhtBtpfSSQ==",
|
||||
"requires": {
|
||||
"original": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"leven": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
|
||||
@@ -10139,6 +10153,11 @@
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
|
||||
"integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ=="
|
||||
},
|
||||
"nanoajax": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/nanoajax/-/nanoajax-0.4.3.tgz",
|
||||
"integrity": "sha1-r3q+wQjXJPuX9vdfEcKmPPMVopg="
|
||||
},
|
||||
"nanoassert": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-1.1.0.tgz",
|
||||
@@ -10210,6 +10229,11 @@
|
||||
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
|
||||
"dev": true
|
||||
},
|
||||
"nchan": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/nchan/-/nchan-1.0.10.tgz",
|
||||
"integrity": "sha512-jB6fV/03M1AXT08I+hldsU5m2r4t2c7J1UnOsCIK/idowxtJ/nYAPwUahMwhFsuv453miOOCvhmao+kpcScb4Q=="
|
||||
},
|
||||
"ncp": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz",
|
||||
@@ -10335,6 +10359,24 @@
|
||||
"process-on-spawn": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node-window-polyfill": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-window-polyfill/-/node-window-polyfill-1.0.0.tgz",
|
||||
"integrity": "sha1-MEJv+fRNHKDYPaS20eJEqA3J5l4=",
|
||||
"requires": {
|
||||
"ws": "^5.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ws": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz",
|
||||
"integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==",
|
||||
"requires": {
|
||||
"async-limiter": "~1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"nodemailer-fetch": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz",
|
||||
@@ -10702,6 +10744,14 @@
|
||||
"readable-stream": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"original": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
|
||||
"integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==",
|
||||
"requires": {
|
||||
"url-parse": "^1.4.3"
|
||||
}
|
||||
},
|
||||
"os-tmpdir": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||
@@ -11252,6 +11302,11 @@
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
||||
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
|
||||
},
|
||||
"querystringify": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
|
||||
},
|
||||
"quick-format-unescaped": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-3.0.3.tgz",
|
||||
@@ -11711,6 +11766,11 @@
|
||||
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
|
||||
},
|
||||
"requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
|
||||
},
|
||||
"reserved-words": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/reserved-words/-/reserved-words-0.1.2.tgz",
|
||||
@@ -14034,6 +14094,15 @@
|
||||
"integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
|
||||
"dev": true
|
||||
},
|
||||
"url-parse": {
|
||||
"version": "1.4.7",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz",
|
||||
"integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==",
|
||||
"requires": {
|
||||
"querystringify": "^2.1.1",
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"url-parse-lax": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
|
||||
@@ -14273,6 +14342,11 @@
|
||||
"resolved": "https://registry.npmjs.org/xerror/-/xerror-1.1.3.tgz",
|
||||
"integrity": "sha512-2l5hmDymDUIuKT53v/nYxofTMUDQuu5P/Y3qHOjQiih6QUHBCgWpbpL3I8BoE5TVfUVTMmUQ0jdUAimTGc9UIg=="
|
||||
},
|
||||
"xhr2": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.2.1.tgz",
|
||||
"integrity": "sha512-sID0rrVCqkVNUn8t6xuv9+6FViXjUVXq8H5rWOH2rz9fDNQEd4g0EA2XlcEdJXRz5BMEn4O1pJFdT+z4YHhoWw=="
|
||||
},
|
||||
"xss": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/xss/-/xss-1.0.8.tgz",
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
"graphql-type-uuid": "^0.2.0",
|
||||
"htpasswd-js": "^1.0.2",
|
||||
"ini": "^2.0.0",
|
||||
"launchdarkly-eventsource": "^1.4.0",
|
||||
"lodash.get": "^4.4.2",
|
||||
"logger": "github:unraid/logger#master",
|
||||
"map-obj": "^4.1.0",
|
||||
@@ -90,9 +91,12 @@
|
||||
"ms": "^2.1.3",
|
||||
"multi-ini": "^2.1.2",
|
||||
"mustache": "^4.1.0",
|
||||
"nanoajax": "^0.4.3",
|
||||
"nanobus": "^4.4.0",
|
||||
"nchan": "^1.0.10",
|
||||
"node-cache": "5.1.2",
|
||||
"node-fetch": "^2.6.1",
|
||||
"node-window-polyfill": "^1.0.0",
|
||||
"number-to-color": "^0.4.1",
|
||||
"observable-to-promise": "^1.0.0",
|
||||
"os-uptime": "^2.0.2",
|
||||
@@ -119,7 +123,8 @@
|
||||
"unix-dgram": "^2.0.4",
|
||||
"upcast": "^4.0.0",
|
||||
"uuid": "^8.3.2",
|
||||
"uuid-apikey": "^1.5.1"
|
||||
"uuid-apikey": "^1.5.1",
|
||||
"xhr2": "^0.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^11.0.0",
|
||||
@@ -127,6 +132,7 @@
|
||||
"@types/cli-table": "^0.3.0",
|
||||
"@types/dockerode": "^3.2.2",
|
||||
"@types/lodash.get": "^4.4.6",
|
||||
"@types/nanoajax": "^0.2.30",
|
||||
"@types/pify": "^5.0.0",
|
||||
"@types/semver-regex": "^3.1.0",
|
||||
"@types/stoppable": "^1.1.0",
|
||||
|
||||
Reference in New Issue
Block a user