| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- import { RouterOptions, json } from 'express';
- import { HttpCheckData, HttpCheckStatus, ServiceCheckData, ServiceCheckDataEntry } from '../../../common/lib/http-check-data.module';
- import { HttpStatusException } from '../../../common/lib/http-status.exception';
- import { ControllerPool } from '../ctrl/controller-pool.interface';
- import { HealthCheckDataProvider } from '../ctrl/health-check-data-provider.interface';
- import { ServiceChangedStatus } from '../lib/service-changed-status.enum';
- import { WebHandler } from './web-handler.base';
- export class ServicesAPIHandler extends WebHandler {
- constructor(protected ctrlPool: ControllerPool, options?: RouterOptions) {
- super(ctrlPool, options);
- this.router.use(json());
- this.router.use(this.avoidCache);
- this.router.get('/:serverID', async (req, res, next) => {
- try {
- const serverID = this.validateNumber(req.params.serverID, 'server id');
- const services = await req.db.getHttpCheckConfigs(serverID);
- res.send(services);
- } catch (err) {
- next(err);
- }
- });
- this.router.put('/:serverID', async (req, res, next) => {
- try {
- const serverID = this.validateNumber(req.params.serverID, 'server id');
- const result = await req.db.saveHttpCheckConfig(serverID, req.body);
- if (result.status !== ServiceChangedStatus.None) {
- await this.ctrlPool.httpChecks.updateCheck(result.status, result.result);
- }
- res.send(result.result);
- } catch (err) {
- next(err);
- }
- });
- this.router.delete('/:serverID/:serviceID', async (req, res, next) => {
- try {
- const serverID = this.validateNumber(req.params.serverID, 'server id');
- const serviceID = this.validateNumber(req.params.serviceID, 'service id');
- const deleted = await req.db.deleteHealthCheckConfig(serverID, serviceID);
- if (deleted) {
- await this.ctrlPool.httpChecks.updateCheck(ServiceChangedStatus.Deactivated, { id: serviceID } as HttpCheckConfig);
- }
- res.send({ deleted });
- } catch (err) {
- next(err);
- }
- });
- this.router.get('/:serverID/:serviceID', async (req, res, next) => {
- try {
- const serverID = this.validateNumber(req.params.serverID, 'server id');
- const serviceID = this.validateNumber(req.params.serviceID, 'service id');
- const result = await req.db.getHttpCheckConfigByID(serverID, serviceID);
- res.send(result);
- } catch (err) {
- next(err);
- }
- });
- this.router.get('/:serverID/:serviceID/data', async (req, res, next) => {
- try {
- const serverID = this.validateNumber(req.params.serverID, 'server id');
- const serviceID = this.validateNumber(req.params.serviceID, 'service id');
- const qStart = (req.query.start || '').toString();
- const qEnd = (req.query.end || '').toString();
- if (!qStart || !qEnd) throw new HttpStatusException("QueryParams 'start' and 'end' are mandatory.", 400);
- const start = new Date(qStart);
- const end = new Date(qEnd);
- if ([start.toString(), end.toString()].includes('Invalid Date')) {
- throw new HttpStatusException("QueryParams 'start' and 'end' must be parseable dates or unix epoch timestamps (ms).", 400);
- }
- const data = await req.db.queryServiceCheckData(serverID, serviceID, start, end);
- res.send({
- start,
- end,
- data
- } as QueryResponse<ServiceCheckData[]>);
- } catch (err) {
- next(err);
- }
- });
- this.router.get('/:serverID/:serviceID/logs', async (req, res, next) => {
- try {
- const serverID = this.validateNumber(req.params.serverID, 'server id');
- const serviceID = this.validateNumber(req.params.serviceID, 'service id');
- const qStart = (req.query.start || '').toString();
- const qEnd = (req.query.end || '').toString();
- if (!qStart || !qEnd) throw new HttpStatusException("QueryParams 'start' and 'end' are mandatory.", 400);
- const start = new Date(qStart);
- const end = new Date(qEnd);
- if ([start.toString(), end.toString()].includes('Invalid Date')) {
- throw new HttpStatusException("QueryParams 'start' and 'end' must be parseable dates or unix epoch timestamps (ms).", 400);
- }
- const data = await req.db.queryServiceCheckLogs(serverID, serviceID, start, end);
- res.send({
- start,
- end,
- data
- } as QueryResponse<ServiceCheckData[]>);
- } catch (err) {
- next(err);
- }
- });
- this.router.post('/test', async (req, res, next) => {
- try {
- const config = req.body as HttpCheckConfig;
- const mockDB = new HealthCheckDatabaseMock(config);
- await this.ctrlPool.httpChecks.runCheck(config, mockDB);
- res.send(mockDB.log);
- } catch (err) {
- next(err);
- }
- });
- }
- private validateNumber(id: string, field: string) {
- const num = Number(id);
- if (Number.isNaN(num)) {
- throw new HttpStatusException(`Not a valid ${field}: ${id}`, 400);
- }
- return num;
- }
- }
- class HealthCheckDatabaseMock implements HealthCheckDataProvider {
- public log: HttpCheckData[] = [];
- constructor(private config: HttpCheckConfig) {}
- async open() {}
- async getHttpCheckConfigByID(serverID: number, configID: number) {
- return this.config;
- }
- async insertHealthCheckData(confID: number, time: Date, status: HttpCheckStatus, message: string) {
- const logEntry = { configId: confID, id: new Date().getTime(), time, status, message };
- this.log.push(logEntry);
- return logEntry;
- }
- async getLastErrors(confID: number, threshold: number) {
- if (this.log.length === 0) return [];
- const mapByTimestamp = new Map<number, ServiceCheckDataEntry[]>();
- mapByTimestamp.set(this.log[0].time.getTime(), this.log);
- const errors: ServiceCheckData[] = [];
- for (const entry of mapByTimestamp.entries()) {
- const time = entry[0];
- const data = entry[1];
- const errorData = data.filter(d => d.status !== HttpCheckStatus.OK);
- if (!errorData.length) break;
- errors.push({
- time: new Date(time),
- data: errorData
- });
- }
- return errors;
- }
- async close() {}
- }
|