|
@@ -6,12 +6,18 @@ import { Database as SQLiteDB, OPEN_CREATE, OPEN_READWRITE } from 'sqlite3';
|
|
|
|
|
|
|
|
import { ServiceConfig, validateParamType } from '../../../common/interfaces/service-config.interface';
|
|
import { ServiceConfig, validateParamType } from '../../../common/interfaces/service-config.interface';
|
|
|
import { Logger } from '../../../common/util/logger.class';
|
|
import { Logger } from '../../../common/util/logger.class';
|
|
|
-import { arrayDiff } from '../../../common/util/object-utils';
|
|
|
|
|
|
|
|
|
|
|
|
+import { ValidationException } from '../lib/validation-exception.class';
|
|
|
import { DBMigration } from './db-migration.class';
|
|
import { DBMigration } from './db-migration.class';
|
|
|
import { SQLiteController } from './sqlite-controller.base';
|
|
import { SQLiteController } from './sqlite-controller.base';
|
|
|
|
|
|
|
|
export class Database extends SQLiteController {
|
|
export class Database extends SQLiteController {
|
|
|
|
|
+ public set onError(listener: (error: any) => void) {
|
|
|
|
|
+ this._onError = listener;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private _onError: (error: any) => void = err => console.error('[DB.ONERROR]', err);
|
|
|
|
|
+
|
|
|
public async open(migrate = false) {
|
|
public async open(migrate = false) {
|
|
|
try {
|
|
try {
|
|
|
const DATA_DIR = process.env.DATA_DIR || 'data';
|
|
const DATA_DIR = process.env.DATA_DIR || 'data';
|
|
@@ -23,6 +29,7 @@ export class Database extends SQLiteController {
|
|
|
|
|
|
|
|
await new Promise<void>((res, rej) => {
|
|
await new Promise<void>((res, rej) => {
|
|
|
this.db = new SQLiteDB(DATA_FILE, OPEN_READWRITE | OPEN_CREATE, err => (err ? rej(err) : res()));
|
|
this.db = new SQLiteDB(DATA_FILE, OPEN_READWRITE | OPEN_CREATE, err => (err ? rej(err) : res()));
|
|
|
|
|
+ this.db.on('error', this._onError);
|
|
|
});
|
|
});
|
|
|
Logger.info('[INFO]', exists ? 'Opened' : 'Created', 'SQLite3 Database file', DATA_FILE);
|
|
Logger.info('[INFO]', exists ? 'Opened' : 'Created', 'SQLite3 Database file', DATA_FILE);
|
|
|
|
|
|
|
@@ -246,7 +253,7 @@ export class Database extends SQLiteController {
|
|
|
return (await this.getHealthCheckConfigs(serverID)).map(this.httpCheckConfigFrom);
|
|
return (await this.getHealthCheckConfigs(serverID)).map(this.httpCheckConfigFrom);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private async getHealtCheckConfigByID(serverID: number, configID: number) {
|
|
|
|
|
|
|
+ private async getHealthCheckConfigByID(serverID: number, configID: number) {
|
|
|
if (!serverID && !configID) return null;
|
|
if (!serverID && !configID) return null;
|
|
|
|
|
|
|
|
const res = await this.stmt(
|
|
const res = await this.stmt(
|
|
@@ -257,7 +264,7 @@ export class Database extends SQLiteController {
|
|
|
HealthCheckParams.Value as '_ParamValue'
|
|
HealthCheckParams.Value as '_ParamValue'
|
|
|
FROM HealthCheckConfig
|
|
FROM HealthCheckConfig
|
|
|
LEFT OUTER JOIN HealthCheckParams ON HealthCheckConfig.ID = HealthCheckParams.ConfigID
|
|
LEFT OUTER JOIN HealthCheckParams ON HealthCheckConfig.ID = HealthCheckParams.ConfigID
|
|
|
- WHERE HealtCheckConfig.ID = ?
|
|
|
|
|
|
|
+ WHERE HealthCheckConfig.ID = ?
|
|
|
AND HealthCheckConfig.ServerID = ?
|
|
AND HealthCheckConfig.ServerID = ?
|
|
|
ORDER BY HealthCheckConfig.Title, _ParamType, _ParamKey`,
|
|
ORDER BY HealthCheckConfig.Title, _ParamType, _ParamKey`,
|
|
|
[configID, serverID]
|
|
[configID, serverID]
|
|
@@ -271,10 +278,13 @@ export class Database extends SQLiteController {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public async getHttpCheckConfigByID(serverID: number, configID: number) {
|
|
public async getHttpCheckConfigByID(serverID: number, configID: number) {
|
|
|
- return this.httpCheckConfigFrom(await this.getHealtCheckConfigByID(serverID, configID));
|
|
|
|
|
|
|
+ return this.httpCheckConfigFrom(await this.getHealthCheckConfigByID(serverID, configID));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public async saveHttpCheckConfig(serverID: number, conf: HttpCheckConfig) {
|
|
public async saveHttpCheckConfig(serverID: number, conf: HttpCheckConfig) {
|
|
|
|
|
+ const validationErrors = this.validateHttpCheckConfig(conf);
|
|
|
|
|
+ if (validationErrors) throw new ValidationException('Validation of HttpCheckConfig object failed', validationErrors);
|
|
|
|
|
+
|
|
|
conf.serverId = serverID;
|
|
conf.serverId = serverID;
|
|
|
|
|
|
|
|
const oldConf = await this.getHttpCheckConfigByID(serverID, conf.id);
|
|
const oldConf = await this.getHttpCheckConfigByID(serverID, conf.id);
|
|
@@ -286,20 +296,63 @@ export class Database extends SQLiteController {
|
|
|
await this.stmt('UPDATE HealthCheckConfig SET Title = ?', [conf.title]);
|
|
await this.stmt('UPDATE HealthCheckConfig SET Title = ?', [conf.title]);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const sql = `UPDATE HealthCheckParams SET Value = ? WHERE ConfigID = ? AND Key = ?;`;
|
|
|
|
|
-
|
|
|
|
|
- const updValues: any[][] = [];
|
|
|
|
|
|
|
+ let updValues: any[][] = [];
|
|
|
if (oldConf.interval !== conf.interval) updValues.push([conf.interval, conf.id, 'interval']);
|
|
if (oldConf.interval !== conf.interval) updValues.push([conf.interval, conf.id, 'interval']);
|
|
|
if (oldConf.url !== conf.url) updValues.push([conf.url, conf.id, 'url']);
|
|
if (oldConf.url !== conf.url) updValues.push([conf.url, conf.id, 'url']);
|
|
|
- const checksDiff = arrayDiff(oldConf.checks, conf.checks);
|
|
|
|
|
|
|
+ if (updValues.length) {
|
|
|
|
|
+ for (const data of updValues) {
|
|
|
|
|
+ await this.run(`UPDATE HealthCheckParams SET Value = ? WHERE ConfigID = ? AND Key = ?;`, data);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const res = await this.stmt('SELECT * FROM HealthCheckParams WHERE ConfigID = ? and Key = "check";', [conf.id]);
|
|
|
|
|
+ updValues = [];
|
|
|
|
|
+ const delIDs: any[][] = [];
|
|
|
|
|
+ res.rows.forEach((row, i) => {
|
|
|
|
|
+ if (i < conf.checks.length) {
|
|
|
|
|
+ updValues.push([conf.checks[i], row['ID']]);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ delIDs.push(row['ID']);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (delIDs.length) {
|
|
|
|
|
+ const delSql = 'DELETE FROM HealthCheckParams WHERE ID IN (?);';
|
|
|
|
|
+ await this.run(delSql, [delIDs]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (updValues.length) {
|
|
|
|
|
+ for (const data of updValues) {
|
|
|
|
|
+ await this.run('UPDATE HealthCheckParams SET Value = ? WHERE ID = ?;', data);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ const insValues = conf.checks.filter((c, i) => i > res.rows.length - 1).map(c => [conf.id, 'regexp', 'check', c]);
|
|
|
|
|
+ if (insValues.length) {
|
|
|
|
|
+ for (const data of insValues) {
|
|
|
|
|
+ await this.run('INSERT INTO HealthCheckParams(ConfigID, Type, Key, Value) VALUES(?, ?, ?, ?);', data);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
} else {
|
|
} else {
|
|
|
// INSERT
|
|
// INSERT
|
|
|
|
|
+ const res = await this.run('INSERT INTO HealthCheckConfig(ServerID, Type, Title) VALUES(?, ?, ?);', [serverID, 'http', conf.title]);
|
|
|
|
|
+
|
|
|
|
|
+ const insCheckValues = conf.checks.map(c => [res.lastID, 'regexp', 'check', c]);
|
|
|
|
|
+ await this.run(
|
|
|
|
|
+ `INSERT INTO HealthCheckParams(ConfigID, Type, Key, Value) VALUES
|
|
|
|
|
+ (?, ?, ?, ?),
|
|
|
|
|
+ (?, ?, ?, ?,)${conf.checks.length ? `,${insCheckValues.map(() => '(?, ?, ?, ?)').join(',')}` : ''}`,
|
|
|
|
|
+ [
|
|
|
|
|
+ ...[res.lastID, 'text', 'url', conf.url],
|
|
|
|
|
+ ...[res.lastID, 'number', 'interval', conf.interval],
|
|
|
|
|
+ ...conf.checks.reduce((res, arr) => [...res, ...arr], [] as any[])
|
|
|
|
|
+ ]
|
|
|
|
|
+ );
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- this.commit();
|
|
|
|
|
|
|
+ await this.commit();
|
|
|
return conf;
|
|
return conf;
|
|
|
} catch (err) {
|
|
} catch (err) {
|
|
|
- this.rollback();
|
|
|
|
|
|
|
+ await this.rollback();
|
|
|
throw err;
|
|
throw err;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -362,4 +415,17 @@ export class Database extends SQLiteController {
|
|
|
...params
|
|
...params
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ private validateHttpCheckConfig(conf: Partial<HttpCheckConfig>): { [key: string]: string } | null {
|
|
|
|
|
+ const errors = {} as any;
|
|
|
|
|
+ if (!conf) return { null: 'Object was null or undefined' };
|
|
|
|
|
+ if (!conf.title?.trim().length) errors['required|title'] = `Field 'title' is required.`;
|
|
|
|
|
+ if (!conf.url?.trim().length) errors['required|url'] = `Field 'url' is required.`;
|
|
|
|
|
+ if ((!conf.interval && conf.interval !== 0) || Number.isNaN(Number(conf.interval))) errors['required|interval'] = `Field 'interval' is required.`;
|
|
|
|
|
+
|
|
|
|
|
+ if (!conf.checks || !Array.isArray(conf.checks))
|
|
|
|
|
+ errors['required|checks'] = `Field 'checks' is required and must be an array of check expressions.`;
|
|
|
|
|
+
|
|
|
|
|
+ return Object.keys(errors).length ? errors : null;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|