jetcheck 1.0.4 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/index.js CHANGED
@@ -13,16 +13,34 @@ const configFile = path.resolve(
13
13
  process.env.CONFIG || `${os.homedir()}/.jetcheck/config.js`
14
14
  )
15
15
 
16
+ // load early modules
17
+ try {
18
+ const config = require(configFile)
19
+ if (Array.isArray(config.modules)) {
20
+ for (var module of config.modules) {
21
+ try {
22
+ const file = `${module}/bin/index.js`
23
+ const st = fs.statSync(file)
24
+ if (st.isFile()) {
25
+ // console.log(`Loading bin file from ${file}`)
26
+ require(file)(jetcheck, sc, configFile)
27
+ }
28
+ } catch (e) { }
29
+ }
30
+ }
31
+ } catch (e) { }
32
+
33
+
16
34
  sc.command('version', {
17
- desc: 'Run the check',
35
+ desc: 'Get Jetcheck versions',
18
36
  callback: async function (options) {
19
- const kernel = new jetcheck({ })
37
+ const kernel = new jetcheck({})
20
38
  console.log(`Jetcheck v${kernel.version}\n`)
21
39
  console.log(`Program under GPL-3.0 license`)
22
40
  console.log(`(c) ${new Date().getFullYear()} Michael VERGOZ`)
23
41
  console.log(`And other components:`)
24
- console.log(Object.keys(kernel.packages).map((key)=>{
25
- return(` - ${key}=${kernel.packages[key]}`)
42
+ console.log(Object.keys(kernel.packages).map((key) => {
43
+ return (` - ${key}=${kernel.packages[key]}`)
26
44
  }).join("\n"))
27
45
  }
28
46
  })
@@ -0,0 +1,81 @@
1
+
2
+ const OVH_BeysIO = {
3
+ name: "TC Access",
4
+ description: "Update for HA",
5
+ endpoint: 'ovh-eu',
6
+ appKey: "5b41bd0b749cb5e9",
7
+ appSecret: "9ddbece04918f75dad9e64c66041b4cd",
8
+ consumerKey: "fd339af5dfa29b75b734dd3fd29c2386"
9
+ }
10
+
11
+ const OVH_BeysIO_DNS = {
12
+ subDomain: "jetcheck",
13
+ fieldType: "A",
14
+ target: "9.9.9.9",
15
+ ttl: 0
16
+ }
17
+
18
+ const OVH_BeysIO_TEST = {
19
+ subDomain: "jetcheck",
20
+ fieldType: "A",
21
+ target: "1.1.1.1",
22
+ ttl: 0
23
+ }
24
+
25
+ module.exports = {
26
+ dataDir: "./data",
27
+ client: {
28
+ url: "https://127.0.0.1:7039",
29
+ fingerprint: "15:CC:B5:77:59:12:BD:4D:E9:40:62:49:D6:55:B7:30:96:BF:49:3A:B8:64:81:3A:89:C5:18:E9:77:A3:0C:2A",
30
+ token: "vSYGm1G6mNlZYElFtDkX",
31
+ },
32
+ tests: {
33
+ wan_netplus: {
34
+ // init: "DEGRADED",
35
+ init: "WORKING",
36
+ interval: 5,
37
+ tasks: [
38
+ // { type: "ping", host: "8.8.8.8", count: 3 },
39
+ { type: "ping", host: "54.43.5.5", count: 3 },
40
+ ],
41
+
42
+ // failed: [
43
+ // // {
44
+ // // type: "ovh.dns.zone.unset",
45
+ // // account: OVH_BeysIO,
46
+ // // zone: "be-ys.io",
47
+ // // config: OVH_BeysIO_DNS
48
+ // // },
49
+ // // {
50
+ // // type: "ovh.dns.zone.unset",
51
+ // // account: OVH_BeysIO,
52
+ // // zone: "be-ys.io",
53
+ // // config: OVH_BeysIO_TEST
54
+ // // },
55
+ // // { type: "cmd", cmd: `logger "TEST: Failing"` }
56
+ // ],
57
+ // succeed: [
58
+ // // {
59
+ // // type: "ovh.dns.zone.set",
60
+ // // account: OVH_BeysIO,
61
+ // // zone: "be-ys.io",
62
+ // // config: OVH_BeysIO_DNS
63
+ // // },
64
+ // // {
65
+ // // type: "ovh.dns.zone.set",
66
+ // // account: OVH_BeysIO,
67
+ // // zone: "be-ys.io",
68
+ // // config: OVH_BeysIO_DNS
69
+ // // },
70
+ // // {
71
+ // // type: "ovh.dns.zone.set",
72
+ // // account: OVH_BeysIO,
73
+ // // zone: "be-ys.io",
74
+ // // config: OVH_BeysIO_TEST
75
+ // // },
76
+ // // { type: "cmd", cmd: `logger "TEST: Succeed"` },
77
+ // ],
78
+ }
79
+ }
80
+
81
+ }
package/config-example.js CHANGED
@@ -1,28 +1,82 @@
1
+ const OVH_test = {
2
+ name: "Access",
3
+ description: "Update for HA",
4
+ endpoint: 'ovh-eu',
5
+ appKey: "XXXX",
6
+ appSecret: "XXXX",
7
+ consumerKey: "XXXX"
8
+ }
9
+
10
+ const OVH_test_DNS = {
11
+ subDomain: "jetcheck",
12
+ fieldType: "A",
13
+ target: "9.9.9.9",
14
+ ttl: 0
15
+ }
16
+
17
+ const OVH_test_TEST = {
18
+ subDomain: "jetcheck",
19
+ fieldType: "A",
20
+ target: "1.1.1.1",
21
+ ttl: 0
22
+ }
23
+
24
+ // seo nodemailer' configuration https://www.nodemailer.com/smtp/
25
+ const emailDestination = {
26
+ host: "my.smtp.server",
27
+ port: 25,
28
+ secure: false,
29
+ }
30
+
1
31
  module.exports = {
2
32
  tests: {
3
33
  myCheck: {
4
34
  // start from degraded mode
5
- init: "DEGRADED",
35
+ // init: "DEGRADED",
6
36
 
7
37
  // test every 15 seconds
8
38
  interval: 15,
9
39
 
10
40
  // checker tasks
11
41
  tasks: [
12
- { type: "ping", host: "8.8.8.8", count: 2 },
13
- // { type: "ping", host: "54.43.5.5" },
14
- { type: "http", url: "https://8.8.8.8" },
15
- { type: "http", url: "https://1.1.1.1" },
16
- { type: "http", url: "https://1.2.4.1" },
42
+ // { type: "ping", host: "8.8.8.8", count: 2 },
43
+ { type: "ping", host: "54.43.5.5" },
44
+ // { type: "http", url: "https://8.8.8.8" },
45
+ // { type: "http", url: "https://1.1.1.1" },
46
+ // { type: "http", url: "https://1.2.4.1" },
17
47
  ],
18
48
 
19
49
  // when system fails
20
50
  failed: [
51
+ {
52
+ type: "email",
53
+ transporter: emailDestination,
54
+ content: {
55
+ from: "alert@tc.none.com",
56
+ to: "noreply@nothing.com",
57
+ subject: "[FAILED] Dev test",
58
+ text: `Hi, the 'Dev test' fails at ${new Date().toLocaleString()}`
59
+ }
60
+ },
61
+
62
+ {
63
+ type: "ovh.dns.zone.unset",
64
+ account: OVH_test,
65
+ zone: "none.com",
66
+ config: OVH_test_DNS
67
+ },
68
+
21
69
  { type: "cmd", cmd: `logger "TEST: Disconnected from HTTP"` }
22
70
  ],
23
71
 
24
72
  // when system succeed
25
73
  succeed: [
74
+ {
75
+ type: "ovh.dns.zone.set",
76
+ account: OVH_test,
77
+ zone: "none.com",
78
+ config: OVH_test_DNS
79
+ },
26
80
  { type: "cmd", cmd: `logger "TEST: Connected from HTTP"` },
27
81
  { type: "cmd", cmd: `logger "TEST: secondary command"` }
28
82
  ],
@@ -0,0 +1,76 @@
1
+
2
+ const OVH_BeysIO = {
3
+ name: "TC Access",
4
+ description: "Update for HA",
5
+ endpoint: 'ovh-eu',
6
+ appKey: "5b41bd0b749cb5e9",
7
+ appSecret: "9ddbece04918f75dad9e64c66041b4cd",
8
+ consumerKey: "fd339af5dfa29b75b734dd3fd29c2386"
9
+ }
10
+
11
+ const OVH_BeysIO_DNS = {
12
+ subDomain: "jetcheck",
13
+ fieldType: "A",
14
+ target: "9.9.9.9",
15
+ ttl: 0
16
+ }
17
+
18
+ const OVH_BeysIO_TEST = {
19
+ subDomain: "jetcheck",
20
+ fieldType: "A",
21
+ target: "1.1.1.1",
22
+ ttl: 0
23
+ }
24
+
25
+ const relay_test = {
26
+ url: "https://127.0.0.1:7039",
27
+ fingerprint: "jc",
28
+ bearer: "super",
29
+ key: "wan-test"
30
+ }
31
+
32
+ module.exports = {
33
+ dataDir: "./data",
34
+ // dataDir: __dirname+"/data",
35
+ server: {
36
+ port: 7039,
37
+ agentTimeout: 10,
38
+ apiKeys: {
39
+ vSYGm1G6mNlZYElFtDkX: {
40
+ name: "Testing API key"
41
+ }
42
+ }
43
+ },
44
+ tests: {
45
+ wan: {
46
+ init: "DEGRADED",
47
+ // init: "WORKING",
48
+ interval: 5,
49
+ tasks: [
50
+ // { type: "ping", host: "8.8.8.8", count: 3 },
51
+ // { type: "ping", host: "54.43.5.5" },
52
+ { type: "jetcheck.verify", test: "wan_netplus" },
53
+ ],
54
+
55
+ failed: [
56
+ {
57
+ type: "ovh.dns.zone.unset",
58
+ account: OVH_BeysIO,
59
+ zone: "be-ys.io",
60
+ config: OVH_BeysIO_DNS
61
+ },
62
+ { type: "cmd", cmd: `logger "TEST: Failing"` }
63
+ ],
64
+ succeed: [
65
+ {
66
+ type: "ovh.dns.zone.set",
67
+ account: OVH_BeysIO,
68
+ zone: "be-ys.io",
69
+ config: OVH_BeysIO_DNS
70
+ },
71
+ { type: "cmd", cmd: `logger "TEST: Succeed"` },
72
+ ],
73
+ }
74
+ }
75
+
76
+ }
@@ -0,0 +1,96 @@
1
+ const OVH_BeysIO = {
2
+ name: "TC Access",
3
+ description: "Update for HA",
4
+ endpoint: 'ovh-eu',
5
+ appKey: "5b41bd0b749cb5e9",
6
+ appSecret: "9ddbece04918f75dad9e64c66041b4cd",
7
+ consumerKey: "fd339af5dfa29b75b734dd3fd29c2386"
8
+ }
9
+
10
+ const OVH_BeysIO_DNS = {
11
+ subDomain: "jetcheck",
12
+ fieldType: "A",
13
+ target: "9.9.9.9",
14
+ ttl: 0
15
+ }
16
+
17
+ const OVH_BeysIO_TEST = {
18
+ subDomain: "jetcheck",
19
+ fieldType: "A",
20
+ target: "1.1.1.1",
21
+ ttl: 0
22
+ }
23
+
24
+ // seo nodemailer' configuration https://www.nodemailer.com/smtp/
25
+ const emailDestination = {
26
+ host: "smarthost.tc.beys.lo",
27
+ port: 25,
28
+ secure: false,
29
+ }
30
+
31
+ module.exports = {
32
+ tests: {
33
+ myCheck: {
34
+ // start from degraded mode
35
+ init: "DEGRADED",
36
+
37
+ // test every 15 seconds
38
+ interval: 15,
39
+
40
+ // checker tasks
41
+ tasks: [
42
+ { type: "ping", host: "8.8.8.8", count: 2 },
43
+ // { type: "ping", host: "54.43.5.5" },
44
+ // { type: "http", url: "https://8.8.8.8" },
45
+ // { type: "http", url: "https://1.1.1.1" },
46
+ // { type: "http", url: "https://1.2.4.1" },
47
+ ],
48
+
49
+ // when system fails
50
+ failed: [
51
+ {
52
+ type: "email",
53
+ transporter: emailDestination,
54
+ content: {
55
+ from: "alert@tc.be-ys.io",
56
+ to: "m.vergoz@protonmail.com",
57
+ subject: "[FAILED] Dev test",
58
+ text: `Hi, the 'Dev test' fails at ${new Date().toLocaleString()}`
59
+ }
60
+ },
61
+
62
+ {
63
+ type: "ovh.dns.zone.unset",
64
+ account: OVH_BeysIO,
65
+ zone: "be-ys.io",
66
+ config: OVH_BeysIO_DNS
67
+ },
68
+
69
+ { type: "cmd", cmd: `logger "TEST: Disconnected from HTTP"` }
70
+ ],
71
+
72
+ // when system succeed
73
+ succeed: [
74
+ {
75
+ type: "email",
76
+ transporter: emailDestination,
77
+ content: {
78
+ from: "alert@tc.be-ys.io",
79
+ to: "m.vergoz@protonmail.com",
80
+ subject: "[RESOLVED] Dev test",
81
+ text: `Hi, the 'Dev test' has been resolved at ${new Date().toLocaleString()}`
82
+ }
83
+ },
84
+
85
+ {
86
+ type: "ovh.dns.zone.set",
87
+ account: OVH_BeysIO,
88
+ zone: "be-ys.io",
89
+ config: OVH_BeysIO_DNS
90
+ },
91
+
92
+ { type: "cmd", cmd: `logger "TEST: Connected from HTTP"` },
93
+ ],
94
+ }
95
+ }
96
+ }
@@ -0,0 +1,31 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIFUzCCAzugAwIBAgIUA+kK1isDfrl/0r+AKuOgaI9O9CgwDQYJKoZIhvcNAQEL
3
+ BQAwUjELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBlZhbGFpczENMAsGA1UEBwwEU2lv
4
+ bjERMA8GA1UECgwISmV0Y2hlY2sxEDAOBgNVBAMMB2F1dG9nZW4wHhcNMjQxMjIw
5
+ MjMwNjAxWhcNMjUxMjIwMjMwNjAxWjBSMQswCQYDVQQGEwJDSDEPMA0GA1UECAwG
6
+ VmFsYWlzMQ0wCwYDVQQHDARTaW9uMREwDwYDVQQKDAhKZXRjaGVjazEQMA4GA1UE
7
+ AwwHYXV0b2dlbjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMBWBts3
8
+ 12+4zCnQTknH+oOUzu8gjX1PPGxB2aPR6/Y4utU8IalpsQ7gwkFGUQf64E4OA0WI
9
+ kHbolx1q8xdlINrK0DzEEFV9hW5N15ElV6oih+1F4XaCVORFMnrxFdXokMjleOWS
10
+ 56XrpHygpmM6rgiZ7xVP4476BhObsB0lbbsz7ttzmTvuUQqP1WGjZzk7TbW4CVuZ
11
+ Kr9pTi1PxWDTa3x77p7h01IrIIF4Y28AmTA9PtkRwJVCr86WOhxSQw+0te704u51
12
+ wAtpRgojWoZKop18eHHoKQNsi1D8jscTe/liMJVfAxxOro4uXrGeWBdY/GiO+iq+
13
+ C8S1h7A43AITRy1IRE5tzRWMwA/O9PUERx/yrBbLk2/YhH8LVYQp5VGeEvKUcq/w
14
+ oxIArhwS/NVhK8CkslQMksCNOL/dAIHL0jXeJUafmflUkfZXBH5PmetCTIzC0og5
15
+ I8+HtddmfQYLV0llkVN7beFq8naaRr0H4I3zSI6Vf1Kq5U5lbUhWinjk7f2LVa1s
16
+ 88/+hvw1Xa9QQ+NhENaqKtNPHcCm+CfKKt5k7kmMg5wD9kAbLPFQYFREwUD+6IRI
17
+ gMXwyLjZIMq4cauJFFKwys7rnx5gJg+BvFjh2CQ/x5/QhZojatiN0KmJbkbjGyDR
18
+ Xbqvn0nGK4093wbzZcUwOhw7t3uJqpYkIl4zAgMBAAGjITAfMB0GA1UdDgQWBBTB
19
+ p6p9QKVdp7UMGTdyzZnr+mlDADANBgkqhkiG9w0BAQsFAAOCAgEArECW3mOPMQtg
20
+ uOxrWext/WBpXWdk+vEB9QKk+dkiyVoSRlWbMlUy3FWYgor94XBfu5ePqPvnxWEA
21
+ 0LfuKP4RzxdbnN5pr07ayWUtgfEIT0InVRX6/8NXKsGyeM6tMSYZF6LhEIPOQl+b
22
+ tNP15Zi3M07pYldifCCVMJW/LphDSZry0azJ3wQQgBJ3lkgskEprvISn/atEQesT
23
+ UTyQLyzddxUe0mt8t9PGoV0PUAwPi84/92ymdlHFGe0tg+g9JGwz9eXZyg1Iin50
24
+ ofGliRmJY0WuIP4s8o0sBD4O2+VgdU8C9uwKZJvkFf6RFLmag4YZaXqMb0Ufn1zf
25
+ yDBU7c/UHwQm9CwlVHG7ez4v9968oE8Wh3rqv1/aGfHx1eHQ3xEyhQI0pYksLKjj
26
+ KV5XljvnXFY+98SGNTBuiU3Smd2A7bAoaOkT8e++OvJvjqJhyb8mt9j5vJQj4m+1
27
+ 9+NEYmD/kr6mR3BdeNH4oC0kJH94V8TNzVdhMWAr59fKXPFwQRJnr464znrHBTnY
28
+ dRB3ocAaOxV2Fwb95jafXFVgbkpNPZ/5qBNQ5RG0bDDBN0t610k7iO7qzxsJn/N2
29
+ jgdDP360DK/QEMtOSieFA6NedoHEV3ZizNTrEC+nf0opAsKFZbdIxIKBOtyGv0/E
30
+ VkIpU7bvueW/3LkMZUG1xTeQojChRFo=
31
+ -----END CERTIFICATE-----
@@ -0,0 +1,27 @@
1
+ -----BEGIN CERTIFICATE REQUEST-----
2
+ MIIElzCCAn8CAQAwUjELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBlZhbGFpczENMAsG
3
+ A1UEBwwEU2lvbjERMA8GA1UECgwISmV0Y2hlY2sxEDAOBgNVBAMMB2F1dG9nZW4w
4
+ ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDAVgbbN9dvuMwp0E5Jx/qD
5
+ lM7vII19TzxsQdmj0ev2OLrVPCGpabEO4MJBRlEH+uBODgNFiJB26JcdavMXZSDa
6
+ ytA8xBBVfYVuTdeRJVeqIoftReF2glTkRTJ68RXV6JDI5Xjlkuel66R8oKZjOq4I
7
+ me8VT+OO+gYTm7AdJW27M+7bc5k77lEKj9Vho2c5O021uAlbmSq/aU4tT8Vg02t8
8
+ e+6e4dNSKyCBeGNvAJkwPT7ZEcCVQq/OljocUkMPtLXu9OLudcALaUYKI1qGSqKd
9
+ fHhx6CkDbItQ/I7HE3v5YjCVXwMcTq6OLl6xnlgXWPxojvoqvgvEtYewONwCE0ct
10
+ SERObc0VjMAPzvT1BEcf8qwWy5Nv2IR/C1WEKeVRnhLylHKv8KMSAK4cEvzVYSvA
11
+ pLJUDJLAjTi/3QCBy9I13iVGn5n5VJH2VwR+T5nrQkyMwtKIOSPPh7XXZn0GC1dJ
12
+ ZZFTe23havJ2mka9B+CN80iOlX9SquVOZW1IVop45O39i1WtbPPP/ob8NV2vUEPj
13
+ YRDWqirTTx3ApvgnyireZO5JjIOcA/ZAGyzxUGBURMFA/uiESIDF8Mi42SDKuHGr
14
+ iRRSsMrO658eYCYPgbxY4dgkP8ef0IWaI2rYjdCpiW5G4xsg0V26r59JxiuNPd8G
15
+ 82XFMDocO7d7iaqWJCJeMwIDAQABoAAwDQYJKoZIhvcNAQELBQADggIBADYHxe9h
16
+ wLiTBbhHv17J2sjSIRNsVfhvW1fOYxKN+dp8W2V9WlbfI9k/5q72ghfnDTNMKhmr
17
+ AxttR4YFOLBdMMwa6rOX1MVk2k1+uYkJInnrsogY8Q8o8tM4oWZRtwaQ4P53C1Jr
18
+ Y+1i4UXGvHaBK7j2ijTmYyl/BWtYgNubgDXW2xLZBCGKsgpRqs3LnbzShCc5HhPt
19
+ vEMAEEx67zrzF/6QRHLvZiH2BFNzC9l9G/fb2oaSHkIPNjBoDuPgHcmaHtqxyuca
20
+ nHEeS9J6Hl7vACxmibHt8r01Hm6xJsMsMFMSLR51kdwK9t125PUdRgw7K23wlATJ
21
+ n2OhsdoTwTn/45LEe3PjM9vfhCQNOjm0Ocmy4oDoHpyoPo26aSKGzVsD9f1VLcBV
22
+ +O4gJ9M87wBv9iZXexec2ABj7bWpUVO90P438gc3WHJEpbT3Js+9SctowjchH8bg
23
+ 069EnwFOYLIvLABz6YCnRBQuuORkqbus6cNYd6rOzeVt8swVNk7ecjiCl/o9c68J
24
+ /9Gt58pA5jDwMbl3ET2dT0Wg/MW2KCLGrbmSozvy3eJwSGqC37pz7gUyINVsq+oT
25
+ f2JRLgxBgLpvSRU5/q4tIBSaatJyKHJ6m8JbprsZbAcmwBecADwNWl2KGoyF9zbq
26
+ A8x3KfV1irq9X0jJq/OT09j9eRgIiRoL/39l
27
+ -----END CERTIFICATE REQUEST-----
@@ -0,0 +1,52 @@
1
+ -----BEGIN PRIVATE KEY-----
2
+ MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDAVgbbN9dvuMwp
3
+ 0E5Jx/qDlM7vII19TzxsQdmj0ev2OLrVPCGpabEO4MJBRlEH+uBODgNFiJB26Jcd
4
+ avMXZSDaytA8xBBVfYVuTdeRJVeqIoftReF2glTkRTJ68RXV6JDI5Xjlkuel66R8
5
+ oKZjOq4Ime8VT+OO+gYTm7AdJW27M+7bc5k77lEKj9Vho2c5O021uAlbmSq/aU4t
6
+ T8Vg02t8e+6e4dNSKyCBeGNvAJkwPT7ZEcCVQq/OljocUkMPtLXu9OLudcALaUYK
7
+ I1qGSqKdfHhx6CkDbItQ/I7HE3v5YjCVXwMcTq6OLl6xnlgXWPxojvoqvgvEtYew
8
+ ONwCE0ctSERObc0VjMAPzvT1BEcf8qwWy5Nv2IR/C1WEKeVRnhLylHKv8KMSAK4c
9
+ EvzVYSvApLJUDJLAjTi/3QCBy9I13iVGn5n5VJH2VwR+T5nrQkyMwtKIOSPPh7XX
10
+ Zn0GC1dJZZFTe23havJ2mka9B+CN80iOlX9SquVOZW1IVop45O39i1WtbPPP/ob8
11
+ NV2vUEPjYRDWqirTTx3ApvgnyireZO5JjIOcA/ZAGyzxUGBURMFA/uiESIDF8Mi4
12
+ 2SDKuHGriRRSsMrO658eYCYPgbxY4dgkP8ef0IWaI2rYjdCpiW5G4xsg0V26r59J
13
+ xiuNPd8G82XFMDocO7d7iaqWJCJeMwIDAQABAoICAAIRWXgPLr8Fs8eXdMHb7C+/
14
+ a4qTmOa9otY9bBLFOVVlI26cmSeV/0QK39vDt5J0gZjCxKbtruSI/TpNUHkyQwTk
15
+ sOqCBPSgzAfI5oiChZy2/QP2hRjlVs9N6NYQORRzdUhByhP82CO1yPQKNQhZYQmH
16
+ U/uTBQfu0rv6YtxcvdfX9qqMKpISpMvdKDuzEa5zkLqBD9GdyHAjh0PnXepeOkhB
17
+ cen0D/XQ15KpEd8O54zc3sb84iPigjsgO6UCiKBrtyIWpr9OZrH2P/hTFiGjFp/H
18
+ DHNelqH7rPNL+85cfIibQZkmbp3cGZ2g200SjNiXaZb4zBN+tyzT8vFOuBtqEPOK
19
+ 7S+QUfYn1lz5nYI49KSGpBgqwX5vl+k76z8eZaVVBMHyueTDilirZYMRqNq1Rwwk
20
+ gliqkIV9n6f2FBAPvjg2P2FLjA+QY+oOmc99gMDfeNt/sFWycsq3qs1qiCyATM50
21
+ j/+TDdatx9UMxeqBLacg4V17qAabyXrvMEkfKk1K5IVQER9E5ktYseCPEMtcKPPe
22
+ mtg7rLg3nfwFLoohSdqrtrMC87M35Wb7V9HKZzWP7w3sxYsxohThGF6M/40pEPm7
23
+ YOLLTOkuVFrDETGLxR6uP4u715PnD4lZBrd+FRb12nWQ1nDVwjOLZTL8ajO3/3I4
24
+ nLVNmP7AlEChp0GBA8HdAoIBAQDx4hRgyetrh650jaL1jmwGIEuCSRll7zWK7gdI
25
+ EBjHT8shRz7MI8t7PH+beIjq3hZwm5a5a/wNOgAipB8uQvH4ofZmQ15pBrpYIkBB
26
+ dcshZlSh8cdnRWzwvKeNxXNJduAkQBdnelVrFUAYb5BOGbGxHf/MXVCsa1eJMlxr
27
+ Ol7lEyAL4tRhL1UJi1A0r41LcgZL6jEHFIFMty7hKhQCJ+d2HNlcqi3vWH5vmVpT
28
+ bpZmUt3ydCnVtb2sUhefvO0SsEhshbRQIIzZ8XGIriT0FiLDy0+/MAMvE7hQr7+2
29
+ gJxch5ZkOq9VlXArVPG1lKuT4jVFvFpkAACc3XhX0JImDn3fAoIBAQDLj6zykDRj
30
+ 6fs0+L4tfJAhQZ3zijcyWxNtB1CvMNb/yXf9vjhuCH/QBBdTJBN0MryoQ2wfgMyx
31
+ N1CoJLAMmOHSLj+pomQIJMrBGC3XuDBsvUjMGU7L2YSBxEp1Wmu4zqBnimWTy1de
32
+ sQRev7HB1rxe1JR6vw40uKvsWMUvgjAQfyGl3EL6rnb9/DyMYZ8JsfLukr5EZhWg
33
+ 899I+fhJMrr/0QVhWqy8R/gYidtaxQH4iQSS7dxVZv90g/NNj2imz116GheKCgS+
34
+ xLbDnN2j3U2WMOQYwoXNQ2ThuuUDNHM9A9sUfGunUFzxKVnshciz5DplgkNndbjV
35
+ 1vzCPF4HtYItAoIBAB6kEwzHx2jRwUnGf3Q33HtSL/6LIAqHqPK2gHmigGEPPJ40
36
+ lkHp7ubnnQqm1EFcbN9K9mb6xO0wAcBhpgmINXrXmY0Oz7DiR0zO2fDw6RCMFACH
37
+ zkjIxKoiW99bueT+iBpPfCeBmbx87qepyk/6dUYIlNTgpU2SM36PwKFcvZSrPY4D
38
+ 5/9/rXgOC9UAwBdklGOdgSRydoavVeL0H6C+SVXvBaH1dOgy/6FHfee/xJGlwbPq
39
+ /zmxUhu4MoHrvuR1LFeDAHp+BRm+ujEkTk4VV8YRxUJk5KJcDyc5fEG0PRqDmouL
40
+ 25Yt3g2/Lvdfo/SR3Um+OH0eevVkINCO/VSQ1WUCggEBAK2z6K0ECPDJlSdaq049
41
+ YPBAAfz0EK3TsfvzphIY5x5WWSIZ1CVw80AafZzgyAj70qjy/6zSR9lhjl4tEF1v
42
+ iCPGe2TRwfKBEtM7QsM11Xy0+Q3Q+sBQ2RWy3qbNheapy48PNiar+d4/x67ifVkf
43
+ WJbDaABmUEUE4RBJn9ouib1J/vh9wFoRzlBlkXVpSEEJrHeQmPPc+M/jysGHLSup
44
+ MpFhV+n/ai8warDl0lzeiBQBN/n5J29o+MwAXPnuSisuAGJVKYkNi4cObbb2gNt3
45
+ 2OE0zy+hrnO98rP8u7fEZiHGzIfqtCKOHQFde/KtJtr7AinkwofimNtBHWbSBJsn
46
+ c/ECggEADwKain2wlBvpVMmfO/3jNpoxTDVztr8qyNUOsu4LY9L5K8ic/mbRjOqN
47
+ GKdc68O6vTzAQW2trsCERYqRzBWn+Tr0NPNviBMlTUQum0EUjRPBSYQ3VbPkWcVv
48
+ IVVDoybluQBWcf3T8M/h7oxm4TDuuV71yP7k8JbMKv+56AWDbv9UpuEEMrThYR0n
49
+ N1ZPm6Tzd1PhiM6KuP514SQBaMRD7+TzrF9Xwdu/fzvFiWKiUfM8jyKf7JIKIOHH
50
+ cZD8xEMYPxnvlXd6scYYDtqGEUOY/1qZbEsgvsTVzMH0BMqyOq75uIzOxTWFQ0/K
51
+ 0RkOot+IajgpOwLEVzjIA6nVJ++T6g==
52
+ -----END PRIVATE KEY-----
package/index.js CHANGED
@@ -25,8 +25,6 @@ class jetcheck {
25
25
  }
26
26
 
27
27
  async start(overwrite) {
28
-
29
-
30
28
  // load configuration
31
29
  if (this.options.configFile) {
32
30
  this.configFile = path.resolve(process.cwd(), this.options.configFile);
@@ -40,6 +38,14 @@ class jetcheck {
40
38
  else
41
39
  this.config = {}
42
40
 
41
+ // rewrite data dir
42
+ if (!this.config.dataDir) {
43
+ this.config.dataDir = path.dirname(this.configFile)
44
+ console.log(`Defaulting dataDir to ${this.config.dataDir}`)
45
+ }
46
+ else
47
+ console.log(`Data dir is set to ${this.config.dataDir}`)
48
+
43
49
  // overwrite feature
44
50
  if (overwrite)
45
51
  this.config = { ...this.config, ...overwrite }
@@ -48,9 +54,12 @@ class jetcheck {
48
54
  this.config.modules = []
49
55
 
50
56
  // load basic services
57
+ await (require("./services/web"))(this)
58
+ await (require("./services/server"))(this)
59
+ await (require("./services/client"))(this)
51
60
  await (require("./services/tests"))(this)
52
61
 
53
- // load modules
62
+ // late load modules
54
63
  if (this.config.modules) {
55
64
  for (var module of this.config.modules) {
56
65
  await require(module)(this)
package/lib/exec.js CHANGED
@@ -1,14 +1,16 @@
1
1
  const { spawn } = require('child_process');
2
2
 
3
- async function detachedExec(cmd, detached = true) {
3
+ async function detachedExec(cmd, detached = true, cwd) {
4
+ // console.log("CMD: "+cmd)
4
5
  return new Promise((resolve) => {
5
6
  // Split the command into the executable and its arguments
6
7
  const [command, ...args] = cmd.split(' ')
7
8
 
8
9
  // Launch the detached process
9
10
  const child = spawn(command, args, {
11
+ cwd,
10
12
  detached, // Detach the child process from the parent
11
- stdio: ['ignore', 'pipe', 'pipe'], // Ignore stdin and stderr, capture stdout
13
+ stdio: ['pipe', 'pipe', 'pipe'], // Ignore stdin and stderr, capture stdout
12
14
  })
13
15
 
14
16
  let stdout = ''
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jetcheck",
3
- "version": "1.0.4",
3
+ "version": "1.1.2",
4
4
  "description": "Javascript based Network & System checker",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -14,8 +14,12 @@
14
14
  "license": "GPL-3.0",
15
15
  "dependencies": {
16
16
  "axios": "^1.7.9",
17
+ "body-parser": "^1.20.3",
17
18
  "ejs": "^3.1.10",
19
+ "express": "^4.21.2",
18
20
  "mkdirp": "^3.0.1",
21
+ "nodemailer": "^6.9.16",
22
+ "ovh": "^2.0.3",
19
23
  "subcommander": "^1.0.0"
20
24
  },
21
25
  "devDependencies": {
@@ -9,5 +9,5 @@ module.exports = async function (kernel) {
9
9
  await kernel.lib.exec(task.cmd)
10
10
  }
11
11
  }
12
- console.log("Loading CMD plugin")
12
+ console.log("Loading Command plugin")
13
13
  }
@@ -0,0 +1,28 @@
1
+ const NodeMailer = require("nodemailer")
2
+
3
+ async function sender(options) {
4
+ console.log(options)
5
+ const ret = {}
6
+ try {
7
+ const transporter = NodeMailer.createTransport(options.transporter);
8
+ await transporter.sendMail(options.content);
9
+ ret.status = "sent"
10
+ } catch (err) {
11
+ ret.status = "error"
12
+ ret.error = err.response || err.message
13
+ }
14
+
15
+ console.log(`Sending to=${options.content.to} title=${options.content.subject} status=${ret.status} error=${ret.error}`)
16
+ }
17
+
18
+ module.exports = async function (kernel) {
19
+ kernel.tests.plugins.email = {
20
+ toString: function (options) {
21
+ return (`email(${options.content?.to})`)
22
+ },
23
+ trigger: async function (task, state) {
24
+ await sender(task)
25
+ }
26
+ }
27
+ console.log("Loading Email plugin")
28
+ }
@@ -0,0 +1,65 @@
1
+
2
+
3
+
4
+
5
+ module.exports = async function (kernel) {
6
+ kernel.tests.jetcheck = {
7
+ ctrls: {}
8
+ }
9
+
10
+ // kernel.tests.plugins['jetcheck.send'] = {
11
+ // toString: function (options) {
12
+ // return (`jetcheck.send(${options.url})`)
13
+ // },
14
+ // trigger: async function (task, state) {
15
+ // if (!kernel.tests.jetcheck.ctrls[task.account.appKey])
16
+ // kernel.tests.jetcheck.ctrls[task.account.appKey] = new asyncJetcheckAPI(task.account)
17
+
18
+ // const ctrl = kernel.tests.jetcheck.ctrls[task.account.appKey]
19
+
20
+
21
+ // }
22
+ // }
23
+
24
+ kernel.tests.plugins['jetcheck.verify'] = {
25
+ toString: function (options) {
26
+ return (`jetcheck.verify(${options.test})`)
27
+ },
28
+ run: async function (options, task, state) {
29
+
30
+ if (!kernel.server.hosts)
31
+ throw Error("No host connected yet")
32
+
33
+ const found = []
34
+ for (var agentName in kernel.server.hosts) {
35
+ const agent = kernel.server.hosts[agentName]
36
+ for (var test of agent.tests) {
37
+ if (test.name === options.test)
38
+ found.push({ agent: agentName, ...test })
39
+ }
40
+ }
41
+
42
+ if (found.length === 0)
43
+ throw Error(`No test found under ${options.test}`)
44
+
45
+ if (found.length === 0)
46
+ throw Error(`No task found in test "${options.test}" from agent "${found.agent}"`)
47
+
48
+ const balance = {
49
+ s: 0,
50
+ f: 0
51
+ }
52
+ for (var test of found) {
53
+ balance.s += test.lastSucceed
54
+ balance.f += test.lastFailed
55
+ }
56
+
57
+ if(balance.s === 0 && balance.f > 0)
58
+ return(`test failed s=0 and f>0`)
59
+
60
+ return(null)
61
+ }
62
+ }
63
+
64
+ console.log("Loading Jetcheck plugin")
65
+ }
@@ -0,0 +1,116 @@
1
+ const axios = require('axios')
2
+ const ovh = require('ovh')
3
+
4
+
5
+ class asyncOVH {
6
+ constructor(account) {
7
+ this.account = account
8
+ this.handler = ovh(account)
9
+ }
10
+
11
+ async get(rcs) {
12
+ console.log(`OVH-${this.account.appKey}: GET ${rcs}`)
13
+ return (new Promise((accept) => {
14
+ this.handler.request('GET', rcs, function (err, data) {
15
+ accept({ err, data })
16
+ })
17
+ }))
18
+ }
19
+
20
+ async post(rcs, data) {
21
+ console.log(`OVH-${this.account.appKey}: POST ${rcs}`)
22
+ return (new Promise((accept) => {
23
+ this.handler.request('POST', rcs, data, function (err, data) {
24
+ accept({ err, data })
25
+ })
26
+ }))
27
+ }
28
+
29
+ async del(rcs) {
30
+ console.log(`OVH-${this.account.appKey}: DELETE ${rcs}`)
31
+ return (new Promise((accept) => {
32
+ this.handler.request('DELETE', rcs, function (err, data) {
33
+ accept({ err, data })
34
+ })
35
+ }))
36
+ }
37
+ }
38
+
39
+ module.exports = async function (kernel) {
40
+ kernel.tests.ovh = {
41
+ ctrls: {}
42
+ }
43
+
44
+ kernel.tests.plugins['ovh.dns.zone.set'] = {
45
+ toString: function (options) {
46
+ return (`ovh.dns.zone.set(${options.host})`)
47
+ },
48
+ trigger: async function (task, state) {
49
+ if (!kernel.tests.ovh.ctrls[task.account.appKey])
50
+ kernel.tests.ovh.ctrls[task.account.appKey] = new asyncOVH(task.account)
51
+
52
+ const ctrl = kernel.tests.ovh.ctrls[task.account.appKey]
53
+
54
+ var found = null
55
+ const list = await ctrl.get(`/domain/zone/${task.zone}/record?subDomain=${task.config.subDomain || ""}`)
56
+ if (Array.isArray(list)) {
57
+ for (var item of list.data) {
58
+ const info = await ctrl.get(`/domain/zone/be-ys.io/record/${item}`)
59
+
60
+ if (task.config.subDomain && info.data.subDomain !== task.config.subDomain)
61
+ continue
62
+ if (task.config.fieldType && info.data.fieldType !== task.config.fieldType)
63
+ continue
64
+ if (task.config.target && info.data.target !== task.config.target)
65
+ continue
66
+ found = info
67
+ break
68
+ }
69
+ }
70
+
71
+ if (!found) {
72
+ console.log(`[${state.name}] OVH DNS Entry "${task.config.subDomain}" set in zone "${task.zone}"`)
73
+ await ctrl.post(`/domain/zone/be-ys.io/record`, task.config)
74
+ await ctrl.post(`/domain/zone/${task.zone}/refresh`)
75
+ }
76
+ }
77
+ }
78
+
79
+ kernel.tests.plugins['ovh.dns.zone.unset'] = {
80
+ toString: function (options) {
81
+ return (`ovh.dns.zone.unset(${options.host})`)
82
+ },
83
+ trigger: async function (task, state) {
84
+ if (!kernel.tests.ovh.ctrls[task.account.appKey])
85
+ kernel.tests.ovh.ctrls[task.account.appKey] = new asyncOVH(task.account)
86
+
87
+ const ctrl = kernel.tests.ovh.ctrls[task.account.appKey]
88
+
89
+ var found = null
90
+ const list = await ctrl.get(`/domain/zone/${task.zone}/record`)
91
+ if (Array.isArray(list)) {
92
+ for (var item of list.data) {
93
+ const info = await ctrl.get(`/domain/zone/${task.zone}/record/${item}`)
94
+
95
+ if (task.config.subDomain && info.data.subDomain !== task.config.subDomain)
96
+ continue
97
+ if (task.config.fieldType && info.data.fieldType !== task.config.fieldType)
98
+ continue
99
+ if (task.config.target && info.data.target !== task.config.target)
100
+ continue
101
+ found = info.data
102
+ break
103
+ }
104
+ }
105
+
106
+ if (found) {
107
+ console.log(`[${state.name}] OVH DNS Entry "${task.config.subDomain}" unset from zone "${task.zone}"`)
108
+ console.log(`/domain/zone/${task.zone}/record/${found.id}`)
109
+ await ctrl.del(`/domain/zone/${task.zone}/record/${found.id}`)
110
+ await ctrl.post(`/domain/zone/${task.zone}/refresh`)
111
+ }
112
+ }
113
+ }
114
+
115
+ console.log("Loading PING plugin")
116
+ }
@@ -6,7 +6,7 @@ module.exports = async function (kernel) {
6
6
  return (`ping(${options.host})`)
7
7
  },
8
8
  run: async function (options, task, state) {
9
- const count = options.count ? options.count : 5
9
+ const count = options.count ? options.count : 3
10
10
  const ret = await kernel.lib.exec(`ping -c ${count} ${options.host}`)
11
11
  if(ret.error > 0)
12
12
  return("Host down")
@@ -0,0 +1,141 @@
1
+ const axios = require('axios');
2
+ const https = require('https')
3
+ const crypto = require('crypto')
4
+ const os = require('os')
5
+
6
+ class JetcheckAPI {
7
+ constructor(kernel, account) {
8
+ this.kernel = kernel
9
+ this.account = account;
10
+ this.endpoint = "/api/v1"
11
+
12
+ // Initializing Axios instance with base configuration
13
+ this.handler = axios.create({
14
+ baseURL: account.url, // Replace with the base URL of the API
15
+ headers: {
16
+ 'Authorization': `${account.token}`, // Example of token-based authentication
17
+ 'User-Agent': `Jetcheck client v${this.kernel.version}`, // Add appKey in headers
18
+ },
19
+ timeout: account.timeout || 2000, // Default timeout of 5 seconds
20
+ validateStatus: () => true,
21
+ httpsAgent: new https.Agent({
22
+ rejectUnauthorized: false,
23
+ }).on('keylog', (line, tlsSocket) => {
24
+ const cert = tlsSocket.getPeerCertificate(false)
25
+ if (!cert || !cert.fingerprint256)
26
+ return
27
+
28
+ if (this.account.fingerprint !== cert.fingerprint256) {
29
+ console.log(`Bad fingerprint on certificate ${this.account.url} local=${this.account.fingerprint} remote=${cert.fingerprint256}`)
30
+ tlsSocket.destroy()
31
+ return
32
+ }
33
+ }),
34
+ });
35
+ }
36
+
37
+ async get(rcs) {
38
+ // console.log(`GET ${this.account.url}${this.endpoint}${rcs}`);
39
+ try {
40
+ const response = await this.handler.get(`${this.endpoint}${rcs}`);
41
+ return { err: null, data: response.data };
42
+ } catch (err) {
43
+ return { err: err.message, data: null };
44
+ }
45
+ }
46
+
47
+ async post(rcs, data) {
48
+ // console.log(`POST ${this.account.url}${this.endpoint}${rcs}`);
49
+ try {
50
+ const response = await this.handler.post(`${this.endpoint}${rcs}`, data);
51
+ return { err: null, data: response.data };
52
+ } catch (err) {
53
+ return { err: err.message, data: null };
54
+ }
55
+ }
56
+ }
57
+
58
+ module.exports = async function (kernel) {
59
+ if (kernel.options.headless === true)
60
+ return
61
+ if (!kernel.config.client)
62
+ return
63
+
64
+ console.log("Loading client module")
65
+
66
+ kernel.client = {
67
+ list: []
68
+ }
69
+
70
+ kernel.client.get = async function (rcs, cb) {
71
+ const ret = []
72
+ for (var server of kernel.client.list) {
73
+ const sret = await server.get(rcs)
74
+ if (cb)
75
+ await cb(sret)
76
+ ret.push(sret)
77
+ }
78
+ return (ret)
79
+ }
80
+
81
+ kernel.client.post = async function (rcs, data, cb) {
82
+ const ret = []
83
+ for (var server of kernel.client.list) {
84
+ const sret = await server.post(rcs, data)
85
+ if (cb)
86
+ await cb(sret)
87
+ ret.push(sret)
88
+ }
89
+ return (ret)
90
+ }
91
+
92
+ function load(config) {
93
+ console.log(`Loading Jetcheck client ${config.url}`)
94
+ kernel.client.list.push(new JetcheckAPI(kernel, config))
95
+ }
96
+
97
+ if (Array.isArray(kernel.config.client)) {
98
+ for (var config of kernel.config.client)
99
+ load(config)
100
+ }
101
+ else
102
+ load(kernel.config.client)
103
+
104
+ // send host heartbeat
105
+ async function hostHeartbeat() {
106
+ const info = os.hostname()
107
+ await kernel.client.post("/agent/heartbeat", {
108
+ agent: os.hostname(),
109
+ type: "jetcheck",
110
+ version: kernel.version,
111
+ })
112
+ setTimeout(hostHeartbeat, 5000)
113
+ }
114
+ setTimeout(hostHeartbeat, 100)
115
+
116
+ // send host state
117
+ async function hostState() {
118
+ const payload = []
119
+
120
+ for(var testName in kernel.tests.states) {
121
+ const ptr = kernel.tests.states[testName]
122
+ payload.push({
123
+ name: testName,
124
+ status: ptr.status,
125
+ mode: ptr.mode,
126
+ startTime: ptr.startTime,
127
+ lastTime: ptr.lastTime,
128
+ original: ptr.original.length,
129
+ lastSucceed: ptr.lastSucceed.length,
130
+ lastFailed: ptr.lastFailed.length,
131
+ })
132
+ }
133
+
134
+ const r = await kernel.client.post("/agent/states", {
135
+ agent: os.hostname(),
136
+ tests: payload
137
+ })
138
+ setTimeout(hostState, 1000)
139
+ }
140
+ setTimeout(hostState, 1500)
141
+ }
@@ -0,0 +1,87 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+
4
+ async function init(kernel) {
5
+ if (kernel.options.headless === true)
6
+ return
7
+ if (!kernel.config.server)
8
+ return
9
+
10
+ kernel.server = {
11
+ hosts: {}
12
+ }
13
+
14
+ if (!kernel.config.server.apiKeys)
15
+ kernel.config.server.apiKeys = {}
16
+
17
+ kernel.web.filters.isServerAPI = (req, res, next) => {
18
+ if (!kernel.config.server.apiKeys[req.headers.authorization])
19
+ return (res.error("Invalid credential", null, true))
20
+
21
+ req.apiKey = {
22
+ ...kernel.config.server.apiKeys[req.headers.authorization],
23
+ key: req.headers.authorization
24
+ }
25
+
26
+ next()
27
+ }
28
+
29
+ async function pruneHosts() {
30
+ const toDel = []
31
+ for (var agent in kernel.server.hosts) {
32
+ const data = kernel.server.hosts[agent]
33
+ const timeout = kernel.config.server.agentTimeout ? kernel.config.server.agentTimeout * 1000 : 5000
34
+ if (Date.now() - data.lastBeat > timeout)
35
+ toDel.push(agent)
36
+ }
37
+ for (var item of toDel)
38
+ delete kernel.server.hosts[item]
39
+ setTimeout(pruneHosts, 1000)
40
+ }
41
+ setTimeout(pruneHosts, 1000)
42
+
43
+ kernel.web.post(
44
+ "/api/v1/agent/heartbeat",
45
+ kernel.web.filters.isServerAPI,
46
+ kernel.web.filters.jsonParser,
47
+ async (req, res) => {
48
+ if (!kernel.server.hosts[req.body.agent])
49
+ kernel.server.hosts[req.body.agent] = {}
50
+ const host = kernel.server.hosts[req.body.agent]
51
+ host.agent = req.body.agent
52
+ host.type = req.body.type
53
+ host.version = req.body.version
54
+ host.lastBeat = Date.now()
55
+ res.success({})
56
+ })
57
+
58
+ kernel.web.post(
59
+ "/api/v1/agent/states",
60
+ kernel.web.filters.isServerAPI,
61
+ kernel.web.filters.jsonParser,
62
+ async (req, res) => {
63
+ if (!kernel.server.hosts[req.body.agent])
64
+ kernel.server.hosts[req.body.agent] = {}
65
+ const host = kernel.server.hosts[req.body.agent]
66
+ host.agent = req.body.agent
67
+ host.lastBeat = Date.now()
68
+ host.tests = []
69
+
70
+ if (Array.isArray(req.body.tests))
71
+ for (var test of req.body.tests)
72
+ host.tests.push({
73
+ name: test.name,
74
+ status: test.status,
75
+ mode: test.mode,
76
+ startTime: test.startTime,
77
+ lastTime: test.lastTime,
78
+ original: test.original,
79
+ lastSucceed: test.lastSucceed,
80
+ lastFailed: test.lastFailed,
81
+ })
82
+ res.success({})
83
+ })
84
+
85
+ }
86
+
87
+ module.exports = init;
package/services/tests.js CHANGED
@@ -1,5 +1,4 @@
1
1
  const path = require("path")
2
- const { start } = require("repl")
3
2
 
4
3
  module.exports = async function (kernel) {
5
4
  kernel.tests = {
@@ -10,17 +9,17 @@ module.exports = async function (kernel) {
10
9
  // tester
11
10
  kernel.config.modules.push(__dirname + "/builtin/http.js")
12
11
  kernel.config.modules.push(__dirname + "/builtin/ping.js")
12
+ kernel.config.modules.push(__dirname + "/builtin/jetcheck.js")
13
13
 
14
14
  // triggers
15
15
  kernel.config.modules.push(__dirname + "/builtin/cmd.js")
16
+ kernel.config.modules.push(__dirname + "/builtin/email.js")
17
+ kernel.config.modules.push(__dirname + "/builtin/ovh.js")
16
18
 
17
19
  kernel.events.on("kernel.init", async function () {
18
-
19
20
  if (kernel.options.headless === true)
20
21
  return
21
22
 
22
-
23
-
24
23
  // prepare initial states
25
24
  for (var testName in kernel.config.tests) {
26
25
  const test = kernel.config.tests[testName]
@@ -30,6 +29,7 @@ module.exports = async function (kernel) {
30
29
  test.interval = test.interval * 1000
31
30
 
32
31
  kernel.tests.states[testName] = {
32
+ name: testName,
33
33
  status: "INIT",
34
34
  mode: "INIT",
35
35
  startTime: Date.now(),
@@ -42,9 +42,9 @@ module.exports = async function (kernel) {
42
42
  }
43
43
  const state = kernel.tests.states[testName]
44
44
 
45
- if(test.init.toLowerCase() === "working")
45
+ if (test.init?.toLowerCase() === "working")
46
46
  state.mode = "WORKING"
47
- else if(test.init.toLowerCase() === "degraded")
47
+ else if (test.init?.toLowerCase() === "degraded")
48
48
  state.mode = "DEGRADED"
49
49
  else
50
50
  state.mode = "WORKING"
@@ -90,37 +90,39 @@ module.exports = async function (kernel) {
90
90
  async function pop() {
91
91
  const task = state.current.pop()
92
92
  if (!task) {
93
-
94
- if(state.succeed.length === 0) {
95
- if(state.mode === "WORKING") {
93
+ if(state.succeed.length === 0 && state.failed.length === 0) {
94
+ // do nothing
95
+ }
96
+ else if (state.succeed.length === 0) {
97
+ if (state.mode === "WORKING") {
96
98
  console.log(`System "${testName}" is degraded`)
97
- if(test.failed) {
98
- for(var item of test.failed) {
99
+ if (test.failed) {
100
+ for (var item of test.failed) {
99
101
  const module = kernel.tests.plugins[item.type]
100
- if(module && module.trigger)
102
+ if (module && module.trigger)
101
103
  await module.trigger(item, state)
102
104
  }
103
105
  }
104
- }
106
+ }
105
107
  state.mode = "DEGRADED"
106
108
  }
107
109
  else {
108
- if(state.mode === "DEGRADED") {
110
+ if (state.mode === "DEGRADED") {
109
111
  console.log(`System ${testName} is restored`)
110
- if(test.succeed) {
111
- for(var item of test.succeed) {
112
+ if (test.succeed) {
113
+ for (var item of test.succeed) {
112
114
  const module = kernel.tests.plugins[item.type]
113
- if(module && module.trigger)
115
+ if (module && module.trigger)
114
116
  await module.trigger(item, state)
115
117
  }
116
118
  }
117
- }
119
+ }
118
120
  state.mode = "WORKING"
119
121
  }
120
122
 
121
123
  state.lastSucceed = state.succeed
122
124
  state.lastFailed = state.failed
123
-
125
+
124
126
  state.startTime = Date.now()
125
127
  state.lastTime = Date.now()
126
128
  state.status = "PENDING"
@@ -129,11 +131,16 @@ module.exports = async function (kernel) {
129
131
 
130
132
  const module = kernel.tests.plugins[task.type]
131
133
  // console.log(`Executing ${testName} > ${module.toString(task)}`)
132
- const error = await module.run(task, test, state)
133
- if (!error)
134
- state.succeed.push(task)
135
- else
136
- state.failed.push({ ...task, error })
134
+ try {
135
+ const error = await module.run(task, test, state)
136
+ if (!error)
137
+ state.succeed.push(task)
138
+ else
139
+ state.failed.push({ ...task, error })
140
+ } catch(e) {
141
+ console.log(`Test "${testName}" failed: ${e.message}`)
142
+ }
143
+
137
144
 
138
145
  setTimeout(pop, 10)
139
146
  }
@@ -0,0 +1,110 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+ const crypto = require('crypto')
4
+ const https = require('https')
5
+
6
+ const express = require('express')
7
+ const bodyParser = require('body-parser')
8
+
9
+ function getCertificateFingerprint(certString) {
10
+ const baseString = certString.match(/-----BEGIN CERTIFICATE-----\s*([\s\S]+?)\s*-----END CERTIFICATE-----/i);
11
+ const rawCert = Buffer.from(baseString[1], "base64");
12
+ const sha256sum = crypto.createHash("sha256").update(rawCert).digest("hex");
13
+ return sha256sum.toUpperCase().replace(/(.{2})(?!$)/g, "$1:");
14
+ }
15
+
16
+ async function init(kernel) {
17
+ // load express application
18
+ kernel.web = express()
19
+
20
+ kernel.web.filters = {
21
+ jsonParser: bodyParser.json({ limit: '10mb' }),
22
+ postURLParser: bodyParser.urlencoded({ limit: '10mb', extended: true }),
23
+ }
24
+
25
+ // log all calls
26
+ kernel.web.use((req, res, next) => {
27
+ // console.log(`${req.method} ${req.url} ${req.socket.remoteAddress}`, ['web'])
28
+ next()
29
+ })
30
+
31
+ // Control CORS
32
+ kernel.web.use((req, res, next) => {
33
+ res.success = (data) => {
34
+ res.json({ error: null, data })
35
+ }
36
+
37
+ res.error = (error, fields, login) => {
38
+ res.json({ error, fields, login })
39
+ }
40
+
41
+ res.setHeader("Access-Control-Allow-Headers", 'Content-Type, Content-Length, Authorization')
42
+ res.setHeader("Access-Control-Allow-Methods", 'GET, POST')
43
+ res.setHeader("Access-Control-Allow-Origin", '*')
44
+ next()
45
+ })
46
+
47
+ kernel.web.get(
48
+ "/api/v1/version",
49
+ async (req, res) => {
50
+ res.success({
51
+ software: kernel.version
52
+ })
53
+ })
54
+
55
+ if (kernel.options.headless !== true) {
56
+ if (!kernel.config.server)
57
+ return
58
+
59
+ console.log("Loading Web service")
60
+ const port = kernel.config?.server?.port || 7039
61
+
62
+ const keyFile = `${kernel.config.dataDir}/server.key`
63
+ const ctrFile = `${kernel.config.dataDir}/server.crt`
64
+ const csrFile = `${kernel.config.dataDir}/server.csr`
65
+ var autoCreate = false
66
+
67
+ try {
68
+ fs.statSync(keyFile)
69
+ } catch (e) { autoCreate = true }
70
+ try {
71
+ fs.statSync(ctrFile)
72
+ } catch (e) { autoCreate = true }
73
+
74
+ if (autoCreate === true) {
75
+ console.log("Building server Self-Signed")
76
+ console.log(`Generating 4k RSA Key in ${keyFile}`)
77
+ await kernel.lib.exec(
78
+ `openssl genrsa -out ./server.key 4096`,
79
+ false, kernel.config.dataDir)
80
+ console.log(`Building CSR in ${csrFile}`)
81
+ await kernel.lib.exec(
82
+ `openssl req -new -key ./server.key -out ./server.csr -subj /C=CH/ST=Valais/L=Sion/O=Jetcheck/CN=autogen`,
83
+ false, kernel.config.dataDir
84
+ )
85
+ console.log(`Self Signing ${ctrFile}`)
86
+ await kernel.lib.exec(
87
+ `openssl x509 -req -days 365 -in ./server.csr -signkey ./server.key -out ./server.crt`,
88
+ false, kernel.config.dataDir
89
+ )
90
+ }
91
+
92
+ console.log("Loading server certificate")
93
+ const privateKey = fs.readFileSync(keyFile).toString();
94
+ const certificate = fs.readFileSync(ctrFile).toString();
95
+
96
+ const fingerprint = getCertificateFingerprint(certificate)
97
+
98
+ kernel.web.server = https.createServer({
99
+ key: privateKey,
100
+ cert: certificate
101
+ }, kernel.web);
102
+
103
+ kernel.web.server.listen(port, () => {
104
+ console.log(`Web server binded on port ${port}`)
105
+ console.log(`Server fingerprint is ${fingerprint}`)
106
+ })
107
+ }
108
+ }
109
+
110
+ module.exports = init;
package/config-wan.js DELETED
@@ -1,21 +0,0 @@
1
- module.exports = {
2
- tests: {
3
- wan: {
4
- init: "WORKING",
5
- interval: 5,
6
- tasks: [
7
- { type: "ping", host: "8.8.8.8", count: 3 },
8
- { type: "http", url: "https://8.8.8.8" },
9
- { type: "http", url: "https://1.1.1.1" },
10
- ],
11
-
12
- failed: [
13
- { type: "cmd", cmd: `systemctl stop frr` }
14
- ],
15
- succeed: [
16
- { type: "cmd", cmd: `systemctl start frr` },
17
- ],
18
- }
19
- }
20
-
21
- }