|
@@ -8,6 +8,7 @@ import { Logger } from '../../../common/util/logger.class';
|
|
|
import { Timer } from '../timer.class';
|
|
import { Timer } from '../timer.class';
|
|
|
import { Database, ServiceChangedStatus } from './database.class';
|
|
import { Database, ServiceChangedStatus } from './database.class';
|
|
|
import { FCMController } from './fcm-controller.class';
|
|
import { FCMController } from './fcm-controller.class';
|
|
|
|
|
+import { HealthCheckDataProvider } from './health-check-data-provider.interface';
|
|
|
|
|
|
|
|
type Subscriber = { id: number; interval: number; conf: HttpCheckConfig };
|
|
type Subscriber = { id: number; interval: number; conf: HttpCheckConfig };
|
|
|
type ContentCheckError = { type: 'contentCheck'; status: HttpCheckStatus; message: string };
|
|
type ContentCheckError = { type: 'contentCheck'; status: HttpCheckStatus; message: string };
|
|
@@ -30,7 +31,7 @@ export class HttpCheckController {
|
|
|
this.scheduleCheck(conf);
|
|
this.scheduleCheck(conf);
|
|
|
|
|
|
|
|
Logger.info('[INFO] Initial HTTP Service Check for', conf.title, '...');
|
|
Logger.info('[INFO] Initial HTTP Service Check for', conf.title, '...');
|
|
|
- await this.timerTick(conf);
|
|
|
|
|
|
|
+ await this.runCheck(conf, this.db);
|
|
|
}
|
|
}
|
|
|
} catch (err) {
|
|
} catch (err) {
|
|
|
Logger.error('[FATAL] Initializing ServerConnector failed:', err);
|
|
Logger.error('[FATAL] Initializing ServerConnector failed:', err);
|
|
@@ -64,7 +65,7 @@ export class HttpCheckController {
|
|
|
if (Number.isNaN(interval)) interval = defaults.serviceChecks.interval;
|
|
if (Number.isNaN(interval)) interval = defaults.serviceChecks.interval;
|
|
|
|
|
|
|
|
if (log) Logger.info(`[INFO] Starting HTTP Service Check Controller for "${conf.title}" with interval ${interval} seconds ...`);
|
|
if (log) Logger.info(`[INFO] Starting HTTP Service Check Controller for "${conf.title}" with interval ${interval} seconds ...`);
|
|
|
- const id = Timer.instance.subscribe(interval, async () => await this.timerTick(conf));
|
|
|
|
|
|
|
+ const id = Timer.instance.subscribe(interval, async () => await this.runCheck(conf, this.db));
|
|
|
const sub = { id, interval, conf };
|
|
const sub = { id, interval, conf };
|
|
|
this.subscriptions.push(sub);
|
|
this.subscriptions.push(sub);
|
|
|
return sub;
|
|
return sub;
|
|
@@ -84,7 +85,7 @@ export class HttpCheckController {
|
|
|
this.subscriptions = this.subscriptions.filter(s => s.id !== sub.id);
|
|
this.subscriptions = this.subscriptions.filter(s => s.id !== sub.id);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private async timerTick(conf: HttpCheckConfig) {
|
|
|
|
|
|
|
+ public async runCheck(conf: HttpCheckConfig, db: HealthCheckDataProvider) {
|
|
|
Logger.debug('[DEBUG] TICK', new Date(), JSON.stringify(conf));
|
|
Logger.debug('[DEBUG] TICK', new Date(), JSON.stringify(conf));
|
|
|
|
|
|
|
|
const now = new Date();
|
|
const now = new Date();
|
|
@@ -95,7 +96,7 @@ export class HttpCheckController {
|
|
|
let success = true;
|
|
let success = true;
|
|
|
try {
|
|
try {
|
|
|
const id = conf.id;
|
|
const id = conf.id;
|
|
|
- conf = (await this.db.getHttpCheckConfigByID(conf.serverId ?? 0, id)) as HttpCheckConfig;
|
|
|
|
|
|
|
+ conf = (await db.getHttpCheckConfigByID(conf.serverId ?? 0, id)) as HttpCheckConfig;
|
|
|
|
|
|
|
|
if (!conf) {
|
|
if (!conf) {
|
|
|
Logger.warn(`[WARN] HealthCheckConfig(${id}) not found in Database but still scheduled in Timer!`);
|
|
Logger.warn(`[WARN] HealthCheckConfig(${id}) not found in Database but still scheduled in Timer!`);
|
|
@@ -108,14 +109,14 @@ export class HttpCheckController {
|
|
|
const errors = this.recurseDisjunctChecks(conf.checks, responseText);
|
|
const errors = this.recurseDisjunctChecks(conf.checks, responseText);
|
|
|
|
|
|
|
|
for (const error of errors) {
|
|
for (const error of errors) {
|
|
|
- await this.db.insertHealthCheckData(conf.id, now, error.status, error.message);
|
|
|
|
|
|
|
+ await db.insertHealthCheckData(conf.id, now, error.status, error.message);
|
|
|
success = false;
|
|
success = false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (success) {
|
|
if (success) {
|
|
|
if (conf.notify) {
|
|
if (conf.notify) {
|
|
|
try {
|
|
try {
|
|
|
- const lastErrors = await this.db.getLastErrors(conf.id, conf.notifyThreshold + 1);
|
|
|
|
|
|
|
+ const lastErrors = await db.getLastErrors(conf.id, conf.notifyThreshold + 1);
|
|
|
if (lastErrors.length > conf.notifyThreshold) {
|
|
if (lastErrors.length > conf.notifyThreshold) {
|
|
|
Logger.debug(`[DEBUG] Sending [RECOVERY] FCM Notification for`, conf.title);
|
|
Logger.debug(`[DEBUG] Sending [RECOVERY] FCM Notification for`, conf.title);
|
|
|
await FCMController.instance.sendNotificationToTopic(defaults.fcmTopics.services, {
|
|
await FCMController.instance.sendNotificationToTopic(defaults.fcmTopics.services, {
|
|
@@ -129,7 +130,7 @@ export class HttpCheckController {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
Logger.debug(`[DEBUG] HTTP Service Check "${conf.title}": OK.`);
|
|
Logger.debug(`[DEBUG] HTTP Service Check "${conf.title}": OK.`);
|
|
|
- await this.db.insertHealthCheckData(conf.id, now, HttpCheckStatus.OK, 'OK');
|
|
|
|
|
|
|
+ await db.insertHealthCheckData(conf.id, now, HttpCheckStatus.OK, 'OK');
|
|
|
}
|
|
}
|
|
|
} catch (err) {
|
|
} catch (err) {
|
|
|
let log = false;
|
|
let log = false;
|
|
@@ -138,11 +139,11 @@ export class HttpCheckController {
|
|
|
// err.code = 'ECONNREFUSED' | 'ECONNABORTED' | 'ERR_BAD_REQUEST' | 'ERR_BAD_RESPONSE' | ...?
|
|
// err.code = 'ECONNREFUSED' | 'ECONNABORTED' | 'ERR_BAD_REQUEST' | 'ERR_BAD_RESPONSE' | ...?
|
|
|
try {
|
|
try {
|
|
|
if (err.code === 'ECONNABORTED') {
|
|
if (err.code === 'ECONNABORTED') {
|
|
|
- await this.db.insertHealthCheckData(conf.id, now, HttpCheckStatus.Timeout, err.message);
|
|
|
|
|
|
|
+ await db.insertHealthCheckData(conf.id, now, HttpCheckStatus.Timeout, err.message);
|
|
|
} else if (err.code && ['ERR_BAD_REQUEST', 'ERR_BAD_RESPONSE'].includes(err.code)) {
|
|
} else if (err.code && ['ERR_BAD_REQUEST', 'ERR_BAD_RESPONSE'].includes(err.code)) {
|
|
|
- await this.db.insertHealthCheckData(conf.id, now, HttpCheckStatus.RequestFailed, `${err.response?.status} ${err.response?.statusText}`);
|
|
|
|
|
|
|
+ await db.insertHealthCheckData(conf.id, now, HttpCheckStatus.RequestFailed, `${err.response?.status} ${err.response?.statusText}`);
|
|
|
} else {
|
|
} else {
|
|
|
- await this.db.insertHealthCheckData(conf.id, now, HttpCheckStatus.RequestFailed, err.message);
|
|
|
|
|
|
|
+ await db.insertHealthCheckData(conf.id, now, HttpCheckStatus.RequestFailed, err.message);
|
|
|
}
|
|
}
|
|
|
} catch (insertErr) {
|
|
} catch (insertErr) {
|
|
|
Logger.error(`[ERROR] Inserting HealthCheckData on Error failed:`, insertErr);
|
|
Logger.error(`[ERROR] Inserting HealthCheckData on Error failed:`, insertErr);
|
|
@@ -150,7 +151,7 @@ export class HttpCheckController {
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
|
try {
|
|
try {
|
|
|
- await this.db.insertHealthCheckData(conf.id, now, HttpCheckStatus.Unknown, new String(err).toString());
|
|
|
|
|
|
|
+ await db.insertHealthCheckData(conf.id, now, HttpCheckStatus.Unknown, new String(err).toString());
|
|
|
} catch (insertErr) {
|
|
} catch (insertErr) {
|
|
|
Logger.error(`[ERROR] Inserting HealthCheckData on Error failed:`, insertErr);
|
|
Logger.error(`[ERROR] Inserting HealthCheckData on Error failed:`, insertErr);
|
|
|
}
|
|
}
|
|
@@ -160,7 +161,7 @@ export class HttpCheckController {
|
|
|
}
|
|
}
|
|
|
if (!success && conf.notify && !process.env.DEV_MODE) {
|
|
if (!success && conf.notify && !process.env.DEV_MODE) {
|
|
|
try {
|
|
try {
|
|
|
- const lastErrors = await this.db.getLastErrors(conf.id, conf.notifyThreshold + 1);
|
|
|
|
|
|
|
+ const lastErrors = await db.getLastErrors(conf.id, conf.notifyThreshold + 1);
|
|
|
if (lastErrors.length > conf.notifyThreshold) {
|
|
if (lastErrors.length > conf.notifyThreshold) {
|
|
|
Logger.debug(`[DEBUG] Sending [CRIT] FCM Notification for`, conf.title);
|
|
Logger.debug(`[DEBUG] Sending [CRIT] FCM Notification for`, conf.title);
|
|
|
const lastCheck = lastErrors[0];
|
|
const lastCheck = lastErrors[0];
|