Explorar o código

(untested) first draft of dis-/conjunctive content checks

Christian Kahlau %!s(int64=2) %!d(string=hai) anos
pai
achega
798cb12e9d

+ 2 - 2
common/types/http-check-config.d.ts

@@ -1,5 +1,5 @@
-type CheckDisjunction = Array<string | string[] | CheckConjunction>;
-type CheckConjunction = { and: string[] };
+type CheckDisjunction = Array<string | CheckDisjunction | CheckConjunction>;
+type CheckConjunction = { and: Array<string | CheckDisjunction | CheckConjunction> };
 
 type HttpCheckConfig = {
   id: number;

+ 60 - 8
server/src/ctrl/http-check-controller.class.ts

@@ -10,6 +10,7 @@ import { Database, ServiceChangedStatus } from './database.class';
 import { FCMController } from './fcm-controller.class';
 
 type Subscriber = { id: number; interval: number; conf: HttpCheckConfig };
+type ContentCheckError = { type: 'contentCheck'; status: HttpCheckStatus; message: string };
 
 export class HttpCheckController {
   private subscriptions: Array<Subscriber> = [];
@@ -104,15 +105,11 @@ export class HttpCheckController {
       options.timeout = conf.timeout;
       let response = await axios.get(conf.url, options);
       const responseText = new String(response.data).toString();
+      const errors = this.recurseDisjunctChecks(conf.checks, responseText);
 
-      for (const check of conf.checks) {
-        // TODO: new CheckDisjunction app logic
-        // const reg = new RegExp(check, 'i');
-        // if (!reg.test(responseText)) {
-        //   Logger.debug(`[DEBUG] Regular expression /${check}/i not found in response`);
-        //   await this.db.insertHealthCheckData(conf.id, now, HttpCheckStatus.CheckFailed, `Regular expression /${check}/i not found in response`);
-        //   success = false;
-        // }
+      for (const error of errors) {
+        await this.db.insertHealthCheckData(conf.id, now, error.status, error.message);
+        success = false;
       }
 
       if (success) {
@@ -181,6 +178,61 @@ export class HttpCheckController {
     }
   }
 
+  private recurseDisjunctChecks(checks: CheckDisjunction, responseText: string): ContentCheckError[] {
+    const errorBuffer: ContentCheckError[] = [];
+    for (const check of checks) {
+      const errors: ContentCheckError[] = [];
+      if (typeof check === 'string') {
+        try {
+          this.doCheck(check, responseText);
+        } catch (error: any) {
+          if (error.type === 'contentCheck') {
+            errors.push(error as ContentCheckError);
+          } else throw error;
+        }
+      } else if (Array.isArray(check)) {
+        errors.push(...this.recurseDisjunctChecks(check, responseText));
+      } else {
+        errors.push(...this.recurseConjunctChecks(check, responseText));
+      }
+
+      if (errors.length) {
+        errorBuffer.push(...errors);
+      } else {
+        return [];
+      }
+    }
+    return errorBuffer;
+  }
+
+  private recurseConjunctChecks(check: CheckConjunction, responseText: string): ContentCheckError[] {
+    const errorBuffer: ContentCheckError[] = [];
+    for (const con of check.and) {
+      try {
+        if (typeof con === 'string') {
+          this.doCheck(con, responseText);
+        } else if (Array.isArray(con)) {
+          errorBuffer.push(...this.recurseDisjunctChecks(con, responseText));
+        } else {
+          errorBuffer.push(...this.recurseConjunctChecks(con, responseText));
+        }
+      } catch (error: any) {
+        if (error.type === 'contentCheck') {
+          errorBuffer.push(error as ContentCheckError);
+        } else throw error;
+      }
+    }
+    return errorBuffer;
+  }
+
+  private async doCheck(check: string, responseText: string) {
+    const reg = new RegExp(check, 'i');
+    if (!reg.test(responseText)) {
+      Logger.debug(`[DEBUG] Regular expression /${check}/i not found in response`);
+      throw { type: 'contentCheck', status: HttpCheckStatus.CheckFailed, message: `Regular expression /${check}/i not found in response` };
+    }
+  }
+
   async close() {
     if (!this.db) return;
     await this.db.close();