2021-07-30 13:18:26 +02:00
const https = require ( "https" ) ;
2021-06-25 15:55:49 +02:00
const dayjs = require ( "dayjs" ) ;
2021-09-17 08:42:19 +02:00
const utc = require ( "dayjs/plugin/utc" ) ;
let timezone = require ( "dayjs/plugin/timezone" ) ;
dayjs . extend ( utc ) ;
dayjs . extend ( timezone ) ;
2021-06-27 10:10:55 +02:00
const axios = require ( "axios" ) ;
2021-07-30 13:18:26 +02:00
const { Prometheus } = require ( "../prometheus" ) ;
2022-04-30 13:40:34 +02:00
const { log , UP , DOWN , PENDING , MAINTENANCE , flipStatus , TimeLogger } = require ( "../../src/util" ) ;
2021-11-04 03:46:43 +02:00
const { tcping , ping , dnsResolve , checkCertificate , checkStatusCode , getTotalClientInRoom , setting , errorLog , mqttAsync } = require ( "../util-server" ) ;
2021-07-30 13:18:26 +02:00
const { R } = require ( "redbean-node" ) ;
const { BeanModel } = require ( "redbean-node/dist/bean-model" ) ;
2021-09-17 08:42:19 +02:00
const { Notification } = require ( "../notification" ) ;
2021-11-04 13:32:16 +02:00
const { Proxy } = require ( "../proxy" ) ;
2021-10-15 18:57:26 +02:00
const { demoMode } = require ( "../config" ) ;
2021-08-12 18:13:46 +02:00
const version = require ( "../../package.json" ) . version ;
2021-10-09 11:04:51 +02:00
const apicache = require ( "../modules/apicache" ) ;
2021-06-27 10:10:55 +02:00
/ * *
* status :
* 0 = DOWN
* 1 = UP
2021-07-27 19:53:59 +02:00
* 2 = PENDING
2022-01-23 16:22:00 +02:00
* 3 = MAINTENANCE
2021-06-27 10:10:55 +02:00
* /
2021-06-25 15:55:49 +02:00
class Monitor extends BeanModel {
2021-09-12 20:26:45 +02:00
/ * *
2022-03-18 11:56:46 +02:00
* Return an object that ready to parse to JSON for public
2021-09-12 20:26:45 +02:00
* Only show necessary data to public
2022-04-16 22:11:45 +02:00
* @ returns { Object }
2021-09-12 20:26:45 +02:00
* /
2022-03-18 11:56:46 +02:00
async toPublicJSON ( showTags = false ) {
let obj = {
2021-09-19 13:04:51 +02:00
id : this . id ,
name : this . name ,
2022-01-25 20:07:27 +02:00
maintenance : await Monitor . isUnderMaintenance ( this . id ) ,
2021-09-19 13:04:51 +02:00
} ;
2022-03-18 11:56:46 +02:00
if ( showTags ) {
obj . tags = await this . getTags ( ) ;
}
return obj ;
2021-09-12 20:26:45 +02:00
}
/ * *
2022-04-22 20:10:13 +02:00
* Return an object that ready to parse to JSON
2022-04-16 22:11:45 +02:00
* @ returns { Object }
2021-09-12 20:26:45 +02:00
* /
2022-04-17 13:30:58 +02:00
async toJSON ( includeSensitiveData = true ) {
2021-07-09 11:55:48 +02:00
let notificationIDList = { } ;
let list = await R . find ( "monitor_notification" , " monitor_id = ? " , [
2021-07-30 13:18:26 +02:00
this . id ,
2021-09-17 08:42:19 +02:00
] ) ;
2021-07-09 11:55:48 +02:00
for ( let bean of list ) {
notificationIDList [ bean . notification _id ] = true ;
}
2022-03-18 11:56:46 +02:00
const tags = await this . getTags ( ) ;
2021-08-26 12:55:19 +02:00
2022-04-17 13:30:58 +02:00
let data = {
2021-06-25 15:55:49 +02:00
id : this . id ,
name : this . name ,
url : this . url ,
2021-10-02 16:48:27 +02:00
method : this . method ,
2021-07-01 08:03:06 +02:00
hostname : this . hostname ,
port : this . port ,
2021-07-19 18:23:06 +02:00
maxretries : this . maxretries ,
2021-07-01 07:11:16 +02:00
weight : this . weight ,
2021-06-25 15:55:49 +02:00
active : this . active ,
type : this . type ,
interval : this . interval ,
2021-09-11 18:54:55 +02:00
retryInterval : this . retryInterval ,
2021-07-01 11:19:28 +02:00
keyword : this . keyword ,
2022-04-05 15:27:50 +02:00
expiryNotification : this . isEnabledExpiryNotification ( ) ,
2021-07-30 16:11:14 +02:00
ignoreTls : this . getIgnoreTls ( ) ,
2021-07-30 18:01:04 +02:00
upsideDown : this . isUpsideDown ( ) ,
2021-08-05 13:04:38 +02:00
maxredirects : this . maxredirects ,
accepted _statuscodes : this . getAcceptedStatuscodes ( ) ,
2021-08-23 00:05:48 +02:00
dns _resolve _type : this . dns _resolve _type ,
dns _resolve _server : this . dns _resolve _server ,
2021-08-28 21:20:25 +02:00
dns _last _result : this . dns _last _result ,
2021-10-30 19:37:15 +02:00
proxyId : this . proxy _id ,
2021-07-30 13:18:26 +02:00
notificationIDList ,
2021-08-26 12:55:19 +02:00
tags : tags ,
2022-01-25 20:07:27 +02:00
maintenance : await Monitor . isUnderMaintenance ( this . id ) ,
2021-12-18 23:35:18 +02:00
mqttUsername : this . mqttUsername ,
2022-04-16 19:06:47 +02:00
mqttPassword : this . mqttPassword ,
2021-12-18 23:35:18 +02:00
mqttTopic : this . mqttTopic ,
mqttSuccessMessage : this . mqttSuccessMessage
2021-06-25 15:55:49 +02:00
} ;
2022-04-17 13:30:58 +02:00
if ( includeSensitiveData ) {
data = {
... data ,
headers : this . headers ,
body : this . body ,
basic _auth _user : this . basic _auth _user ,
basic _auth _pass : this . basic _auth _pass ,
pushToken : this . pushToken ,
} ;
}
return data ;
2021-06-25 15:55:49 +02:00
}
2022-04-21 19:30:04 +02:00
/ * *
* Get all tags applied to this monitor
* @ returns { Promise < LooseObject < any > [ ] > }
* /
2022-03-18 11:56:46 +02:00
async getTags ( ) {
2022-04-17 09:27:35 +02:00
return await R . getAll ( "SELECT mt.*, tag.name, tag.color FROM monitor_tag mt JOIN tag ON mt.tag_id = tag.id WHERE mt.monitor_id = ?" , [ this . id ] ) ;
2021-06-25 15:55:49 +02:00
}
2021-11-02 13:30:44 +02:00
/ * *
* Encode user and password to Base64 encoding
* for HTTP "basic" auth , as per RFC - 7617
* @ returns { string }
* /
2021-11-23 06:59:48 +02:00
encodeBase64 ( user , pass ) {
return Buffer . from ( user + ":" + pass ) . toString ( "base64" ) ;
2021-11-02 14:11:33 +02:00
}
2021-11-02 13:30:44 +02:00
2022-04-21 19:30:04 +02:00
/ * *
* Is the TLS expiry notification enabled ?
* @ returns { boolean }
* /
2022-04-05 15:27:50 +02:00
isEnabledExpiryNotification ( ) {
return Boolean ( this . expiryNotification ) ;
}
2021-07-30 16:11:14 +02:00
/ * *
* Parse to boolean
* @ returns { boolean }
* /
getIgnoreTls ( ) {
2021-09-17 08:42:19 +02:00
return Boolean ( this . ignoreTls ) ;
2021-07-30 16:11:14 +02:00
}
/ * *
* Parse to boolean
* @ returns { boolean }
* /
2021-07-30 18:01:04 +02:00
isUpsideDown ( ) {
2021-07-30 16:11:14 +02:00
return Boolean ( this . upsideDown ) ;
}
2022-04-16 22:11:45 +02:00
/ * *
2022-04-22 19:42:47 +02:00
* Get accepted status codes
2022-04-16 22:11:45 +02:00
* @ returns { Object }
* /
2021-08-05 13:04:38 +02:00
getAcceptedStatuscodes ( ) {
return JSON . parse ( this . accepted _statuscodes _json ) ;
}
2022-04-16 22:11:45 +02:00
/ * *
* Start monitor
* @ param { Server } io Socket server instance
* /
2021-06-25 15:55:49 +02:00
start ( io ) {
2021-06-29 10:06:20 +02:00
let previousBeat = null ;
2021-07-19 18:23:06 +02:00
let retries = 0 ;
2021-06-29 10:06:20 +02:00
2021-07-27 18:52:31 +02:00
let prometheus = new Prometheus ( this ) ;
2021-07-22 17:00:11 +02:00
2021-06-27 10:10:55 +02:00
const beat = async ( ) => {
2021-09-08 13:54:37 +02:00
2022-02-24 09:11:17 +02:00
let beatInterval = this . interval ;
if ( ! beatInterval ) {
beatInterval = 1 ;
}
if ( demoMode ) {
if ( beatInterval < 20 ) {
console . log ( "beat interval too low, reset to 20s" ) ;
beatInterval = 20 ;
}
}
2021-08-10 11:51:30 +02:00
// Expose here for prometheus update
// undefined if not https
let tlsInfo = undefined ;
2021-11-04 03:46:43 +02:00
if ( ! previousBeat ) {
2021-06-29 10:06:20 +02:00
previousBeat = await R . findOne ( "heartbeat" , " monitor_id = ? ORDER BY time DESC" , [
2021-07-30 13:18:26 +02:00
this . id ,
2021-09-17 08:42:19 +02:00
] ) ;
2021-06-29 10:06:20 +02:00
}
2021-07-24 05:42:14 +02:00
const isFirstBeat = ! previousBeat ;
2021-09-17 08:42:19 +02:00
let bean = R . dispense ( "heartbeat" ) ;
2021-06-27 10:10:55 +02:00
bean . monitor _id = this . id ;
bean . time = R . isoDateTime ( dayjs . utc ( ) ) ;
2021-07-24 05:42:14 +02:00
bean . status = DOWN ;
2021-06-27 10:10:55 +02:00
2021-07-30 18:01:04 +02:00
if ( this . isUpsideDown ( ) ) {
bean . status = flipStatus ( bean . status ) ;
}
2021-06-30 20:02:54 +02:00
// Duration
2021-11-04 03:46:43 +02:00
if ( ! isFirstBeat ) {
2021-07-30 13:18:26 +02:00
bean . duration = dayjs ( bean . time ) . diff ( dayjs ( previousBeat . time ) , "second" ) ;
2021-06-30 20:02:54 +02:00
} else {
bean . duration = 0 ;
}
2021-06-27 10:10:55 +02:00
try {
2022-01-25 20:07:27 +02:00
if ( await Monitor . isUnderMaintenance ( this . id ) ) {
2022-01-23 16:22:00 +02:00
bean . msg = "Monitor under maintenance" ;
bean . status = MAINTENANCE ;
2022-04-30 14:57:08 +02:00
} else if ( this . type === "http" || this . type === "keyword" ) {
2021-08-23 12:52:24 +02:00
// Do not do any queries/high loading things before the "bean.ping"
2021-06-27 10:10:55 +02:00
let startTime = dayjs ( ) . valueOf ( ) ;
2021-07-30 16:11:14 +02:00
2021-11-02 13:30:44 +02:00
// HTTP basic auth
2021-11-04 11:12:06 +02:00
let basicAuthHeader = { } ;
if ( this . basic _auth _user ) {
basicAuthHeader = {
2021-11-23 06:59:48 +02:00
"Authorization" : "Basic " + this . encodeBase64 ( this . basic _auth _user , this . basic _auth _pass ) ,
2021-11-02 14:11:33 +02:00
} ;
2021-11-02 13:30:44 +02:00
}
2021-10-30 19:37:15 +02:00
const httpsAgentOptions = {
maxCachedSessions : 0 , // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
rejectUnauthorized : ! this . getIgnoreTls ( ) ,
} ;
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , ` [ ${ this . name } ] Prepare Options for axios ` ) ;
2021-11-07 18:15:36 +02:00
2021-10-02 16:48:27 +02:00
const options = {
url : this . url ,
method : ( this . method || "get" ) . toLowerCase ( ) ,
... ( this . body ? { data : JSON . parse ( this . body ) } : { } ) ,
2021-08-11 17:12:38 +02:00
timeout : this . interval * 1000 * 0.8 ,
2021-07-30 13:18:26 +02:00
headers : {
2021-12-16 09:09:10 +02:00
"Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" ,
2021-08-11 19:31:07 +02:00
"User-Agent" : "Uptime-Kuma/" + version ,
2021-10-09 21:51:24 +02:00
... ( this . headers ? JSON . parse ( this . headers ) : { } ) ,
2021-11-04 11:12:06 +02:00
... ( basicAuthHeader ) ,
2021-07-30 13:18:26 +02:00
} ,
2021-08-05 13:04:38 +02:00
maxRedirects : this . maxredirects ,
validateStatus : ( status ) => {
return checkStatusCode ( status , this . getAcceptedStatuscodes ( ) ) ;
} ,
2021-10-02 16:48:27 +02:00
} ;
2021-11-07 15:00:47 +02:00
2021-10-30 19:37:15 +02:00
if ( this . proxy _id ) {
const proxy = await R . load ( "proxy" , this . proxy _id ) ;
if ( proxy && proxy . active ) {
2021-11-04 13:32:16 +02:00
const { httpAgent , httpsAgent } = Proxy . createAgents ( proxy , {
httpsAgentOptions : httpsAgentOptions ,
} ) ;
2021-10-30 19:37:15 +02:00
options . proxy = false ;
2021-11-04 13:32:16 +02:00
options . httpAgent = httpAgent ;
options . httpsAgent = httpsAgent ;
2021-10-30 19:37:15 +02:00
}
}
if ( ! options . httpsAgent ) {
options . httpsAgent = new https . Agent ( httpsAgentOptions ) ;
}
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , ` [ ${ this . name } ] Axios Options: ${ JSON . stringify ( options ) } ` ) ;
log . debug ( "monitor" , ` [ ${ this . name } ] Axios Request ` ) ;
2021-10-30 19:37:15 +02:00
2021-10-02 16:48:27 +02:00
let res = await axios . request ( options ) ;
2021-09-17 08:42:19 +02:00
bean . msg = ` ${ res . status } - ${ res . statusText } ` ;
2021-06-27 10:10:55 +02:00
bean . ping = dayjs ( ) . valueOf ( ) - startTime ;
2021-07-01 11:19:28 +02:00
2021-07-22 10:04:32 +02:00
// Check certificate if https is used
2021-07-23 06:58:05 +02:00
let certInfoStartTime = dayjs ( ) . valueOf ( ) ;
2021-07-22 10:13:58 +02:00
if ( this . getUrl ( ) ? . protocol === "https:" ) {
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , ` [ ${ this . name } ] Check cert ` ) ;
2021-07-23 06:58:05 +02:00
try {
2021-10-27 09:33:15 +02:00
let tlsInfoObject = checkCertificate ( res ) ;
tlsInfo = await this . updateTlsInfo ( tlsInfoObject ) ;
2022-04-05 15:27:50 +02:00
if ( ! this . getIgnoreTls ( ) && this . isEnabledExpiryNotification ( ) ) {
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , ` [ ${ this . name } ] call sendCertNotification ` ) ;
2021-10-27 09:33:15 +02:00
await this . sendCertNotification ( tlsInfoObject ) ;
}
2021-07-23 06:58:05 +02:00
} catch ( e ) {
2021-08-08 07:47:29 +02:00
if ( e . message !== "No TLS certificate in response" ) {
2022-04-13 17:33:37 +02:00
log . error ( "monitor" , "Caught error" ) ;
log . error ( "monitor" , e . message ) ;
2021-08-08 07:47:29 +02:00
}
2021-07-23 06:58:05 +02:00
}
2021-07-21 06:09:09 +02:00
}
2021-07-01 11:19:28 +02:00
2021-10-07 11:39:58 +02:00
if ( process . env . TIMELOGGER === "1" ) {
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , "Cert Info Query Time: " + ( dayjs ( ) . valueOf ( ) - certInfoStartTime ) + "ms" ) ;
2021-10-07 11:39:58 +02:00
}
2021-10-15 12:36:40 +02:00
2022-04-17 09:43:03 +02:00
if ( process . env . UPTIME _KUMA _LOG _RESPONSE _BODY _MONITOR _ID === this . id ) {
2022-04-13 17:33:37 +02:00
log . info ( "monitor" , res . data ) ;
2021-10-15 12:36:40 +02:00
}
2021-07-23 06:58:05 +02:00
2021-07-01 11:19:28 +02:00
if ( this . type === "http" ) {
2021-07-24 05:42:14 +02:00
bean . status = UP ;
2021-07-01 11:19:28 +02:00
} else {
2021-07-12 04:52:41 +02:00
let data = res . data ;
// Convert to string for object/array
if ( typeof data !== "string" ) {
2021-09-17 08:42:19 +02:00
data = JSON . stringify ( data ) ;
2021-07-12 04:52:41 +02:00
}
if ( data . includes ( this . keyword ) ) {
2021-09-17 08:42:19 +02:00
bean . msg += ", keyword is found" ;
2021-07-24 05:42:14 +02:00
bean . status = UP ;
2021-07-01 11:19:28 +02:00
} else {
2021-09-17 08:42:19 +02:00
throw new Error ( bean . msg + ", but keyword is not found" ) ;
2021-07-01 11:19:28 +02:00
}
}
2021-07-01 08:03:06 +02:00
} else if ( this . type === "port" ) {
bean . ping = await tcping ( this . hostname , this . port ) ;
2021-09-17 08:42:19 +02:00
bean . msg = "" ;
2021-07-24 05:42:14 +02:00
bean . status = UP ;
2021-07-01 11:00:23 +02:00
} else if ( this . type === "ping" ) {
bean . ping = await ping ( this . hostname ) ;
2021-09-17 08:42:19 +02:00
bean . msg = "" ;
2021-07-24 05:42:14 +02:00
bean . status = UP ;
2021-08-23 00:05:48 +02:00
} else if ( this . type === "dns" ) {
let startTime = dayjs ( ) . valueOf ( ) ;
2021-08-23 00:05:48 +02:00
let dnsMessage = "" ;
2021-08-23 00:05:48 +02:00
2021-08-23 00:05:48 +02:00
let dnsRes = await dnsResolve ( this . hostname , this . dns _resolve _server , this . dns _resolve _type ) ;
bean . ping = dayjs ( ) . valueOf ( ) - startTime ;
2021-08-23 00:05:48 +02:00
2022-04-17 09:43:03 +02:00
if ( this . dns _resolve _type === "A" || this . dns _resolve _type === "AAAA" || this . dns _resolve _type === "TXT" ) {
2021-08-24 11:47:12 +02:00
dnsMessage += "Records: " ;
2021-08-25 09:31:42 +02:00
dnsMessage += dnsRes . join ( " | " ) ;
2022-04-17 09:43:03 +02:00
} else if ( this . dns _resolve _type === "CNAME" || this . dns _resolve _type === "PTR" ) {
2021-08-23 00:05:48 +02:00
dnsMessage = dnsRes [ 0 ] ;
2022-04-17 09:43:03 +02:00
} else if ( this . dns _resolve _type === "CAA" ) {
2021-08-23 00:05:48 +02:00
dnsMessage = dnsRes [ 0 ] . issue ;
2022-04-17 09:43:03 +02:00
} else if ( this . dns _resolve _type === "MX" ) {
2021-08-23 00:05:48 +02:00
dnsRes . forEach ( record => {
2021-08-24 11:47:12 +02:00
dnsMessage += ` Hostname: ${ record . exchange } - Priority: ${ record . priority } | ` ;
2021-08-23 00:05:48 +02:00
} ) ;
2021-09-17 08:42:19 +02:00
dnsMessage = dnsMessage . slice ( 0 , - 2 ) ;
2022-04-17 09:43:03 +02:00
} else if ( this . dns _resolve _type === "NS" ) {
2021-08-24 11:47:12 +02:00
dnsMessage += "Servers: " ;
2021-08-25 09:31:42 +02:00
dnsMessage += dnsRes . join ( " | " ) ;
2022-04-17 09:43:03 +02:00
} else if ( this . dns _resolve _type === "SOA" ) {
2021-08-23 00:05:48 +02:00
dnsMessage += ` NS-Name: ${ dnsRes . nsname } | Hostmaster: ${ dnsRes . hostmaster } | Serial: ${ dnsRes . serial } | Refresh: ${ dnsRes . refresh } | Retry: ${ dnsRes . retry } | Expire: ${ dnsRes . expire } | MinTTL: ${ dnsRes . minttl } ` ;
2022-04-17 09:43:03 +02:00
} else if ( this . dns _resolve _type === "SRV" ) {
2021-08-23 00:05:48 +02:00
dnsRes . forEach ( record => {
dnsMessage += ` Name: ${ record . name } | Port: ${ record . port } | Priority: ${ record . priority } | Weight: ${ record . weight } | ` ;
} ) ;
2021-09-17 08:42:19 +02:00
dnsMessage = dnsMessage . slice ( 0 , - 2 ) ;
2021-08-23 00:05:48 +02:00
}
2021-08-28 21:29:24 +02:00
if ( this . dnsLastResult !== dnsMessage ) {
2021-08-28 21:20:25 +02:00
R . exec ( "UPDATE `monitor` SET dns_last_result = ? WHERE id = ? " , [
dnsMessage ,
this . id
] ) ;
}
2021-08-23 00:05:48 +02:00
bean . msg = dnsMessage ;
bean . status = UP ;
2021-09-30 18:09:43 +02:00
} else if ( this . type === "push" ) { // Type: Push
const time = R . isoDateTime ( dayjs . utc ( ) . subtract ( this . interval , "second" ) ) ;
let heartbeatCount = await R . count ( "heartbeat" , " monitor_id = ? AND time > ? " , [
this . id ,
time
] ) ;
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , "heartbeatCount" + heartbeatCount + " " + time ) ;
2021-09-30 18:09:43 +02:00
if ( heartbeatCount <= 0 ) {
2021-12-08 08:59:59 +02:00
// Fix #922, since previous heartbeat could be inserted by api, it should get from database
previousBeat = await Monitor . getPreviousHeartbeat ( this . id ) ;
2021-09-30 18:09:43 +02:00
throw new Error ( "No heartbeat in the time window" ) ;
} else {
// No need to insert successful heartbeat for push type, so end here
retries = 0 ;
2022-02-24 09:11:17 +02:00
this . heartbeatInterval = setTimeout ( beat , beatInterval * 1000 ) ;
2021-09-30 18:09:43 +02:00
return ;
}
2021-09-27 11:17:57 +02:00
} else if ( this . type === "steam" ) {
const steamApiUrl = "https://api.steampowered.com/IGameServersService/GetServerList/v1/" ;
2021-10-18 11:11:41 +02:00
const steamAPIKey = await setting ( "steamAPIKey" ) ;
2021-09-27 11:17:57 +02:00
const filter = ` addr \\ ${ this . hostname } : ${ this . port } ` ;
2021-10-18 11:11:41 +02:00
if ( ! steamAPIKey ) {
throw new Error ( "Steam API Key not found" ) ;
}
2021-09-27 11:17:57 +02:00
let res = await axios . get ( steamApiUrl , {
timeout : this . interval * 1000 * 0.8 ,
headers : {
"Accept" : "*/*" ,
"User-Agent" : "Uptime-Kuma/" + version ,
} ,
httpsAgent : new https . Agent ( {
maxCachedSessions : 0 , // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
2021-11-04 03:46:43 +02:00
rejectUnauthorized : ! this . getIgnoreTls ( ) ,
2021-09-27 11:17:57 +02:00
} ) ,
maxRedirects : this . maxredirects ,
validateStatus : ( status ) => {
return checkStatusCode ( status , this . getAcceptedStatuscodes ( ) ) ;
} ,
params : {
filter : filter ,
2021-10-18 11:11:41 +02:00
key : steamAPIKey ,
2021-09-27 11:17:57 +02:00
}
} ) ;
2021-10-18 11:02:05 +02:00
if ( res . data . response && res . data . response . servers && res . data . response . servers . length > 0 ) {
bean . status = UP ;
bean . msg = res . data . response . servers [ 0 ] . name ;
2021-09-27 11:17:57 +02:00
2021-10-18 11:02:05 +02:00
try {
bean . ping = await ping ( this . hostname ) ;
} catch ( _ ) { }
2021-09-27 11:17:57 +02:00
} else {
2021-10-18 11:02:05 +02:00
throw new Error ( "Server not found on Steam" ) ;
2021-09-27 11:17:57 +02:00
}
2021-11-04 03:46:43 +02:00
} else if ( this . type === "mqtt" ) {
2022-04-16 19:06:47 +02:00
bean . msg = await mqttAsync ( this . hostname , this . mqttTopic , this . mqttSuccessMessage , {
port : this . port ,
username : this . mqttUsername ,
password : this . mqttPassword ,
interval : this . interval ,
} ) ;
bean . status = UP ;
2021-09-30 18:09:43 +02:00
} else {
bean . msg = "Unknown Monitor Type" ;
bean . status = PENDING ;
2021-06-27 10:10:55 +02:00
}
2021-07-30 18:01:04 +02:00
if ( this . isUpsideDown ( ) ) {
bean . status = flipStatus ( bean . status ) ;
if ( bean . status === DOWN ) {
throw new Error ( "Flip UP to DOWN" ) ;
}
}
2021-07-19 18:23:06 +02:00
retries = 0 ;
2021-06-27 10:10:55 +02:00
} catch ( error ) {
2021-07-30 18:01:04 +02:00
bean . msg = error . message ;
// If UP come in here, it must be upside down mode
// Just reset the retries
if ( this . isUpsideDown ( ) && bean . status === UP ) {
retries = 0 ;
} else if ( ( this . maxretries > 0 ) && ( retries < this . maxretries ) ) {
2021-07-19 18:23:06 +02:00
retries ++ ;
2021-07-24 05:42:14 +02:00
bean . status = PENDING ;
2021-07-19 18:23:06 +02:00
}
2021-06-27 10:10:55 +02:00
}
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , ` [ ${ this . name } ] Check isImportant ` ) ;
2021-10-16 11:28:03 +02:00
let isImportant = Monitor . isImportantBeat ( isFirstBeat , previousBeat ? . status , bean . status ) ;
2021-07-24 05:42:14 +02:00
2021-07-20 11:50:33 +02:00
// Mark as important if status changed, ignore pending pings,
// Don't notify if disrupted changes to up
2021-07-24 05:42:14 +02:00
if ( isImportant ) {
2021-06-29 10:06:20 +02:00
bean . important = true ;
2021-11-07 15:00:47 +02:00
2022-01-23 16:22:00 +02:00
if ( Monitor . isImportantForNotification ( isFirstBeat , previousBeat ? . status , bean . status ) ) {
2022-04-30 13:40:34 +02:00
log . debug ( "monitor" , ` [ ${ this . name } ] sendNotification ` ) ;
2022-01-23 16:22:00 +02:00
await Monitor . sendNotification ( isFirstBeat , this , bean ) ;
2022-04-30 14:57:08 +02:00
} else {
2022-04-30 13:40:34 +02:00
log . debug ( "monitor" , ` [ ${ this . name } ] will not sendNotification because it is (or was) under maintenance ` ) ;
2022-01-23 16:22:00 +02:00
}
2021-10-27 05:39:46 +02:00
// Clear Status Page Cache
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , ` [ ${ this . name } ] apicache clear ` ) ;
2021-10-27 05:39:46 +02:00
apicache . clear ( ) ;
2021-06-29 10:06:20 +02:00
} else {
bean . important = false ;
}
2021-07-24 05:42:14 +02:00
if ( bean . status === UP ) {
2022-04-13 17:33:37 +02:00
log . info ( "monitor" , ` Monitor # ${ this . id } ' ${ this . name } ': Successful Response: ${ bean . ping } ms | Interval: ${ beatInterval } seconds | Type: ${ this . type } ` ) ;
2021-07-24 05:42:14 +02:00
} else if ( bean . status === PENDING ) {
2021-09-29 11:20:35 +02:00
if ( this . retryInterval > 0 ) {
2021-09-11 18:54:55 +02:00
beatInterval = this . retryInterval ;
}
2022-04-13 17:33:37 +02:00
log . warn ( "monitor" , ` Monitor # ${ this . id } ' ${ this . name } ': Pending: ${ bean . msg } | Max retries: ${ this . maxretries } | Retry: ${ retries } | Retry Interval: ${ beatInterval } seconds | Type: ${ this . type } ` ) ;
2022-01-23 16:22:00 +02:00
} else if ( bean . status === MAINTENANCE ) {
2022-04-30 13:40:34 +02:00
log . warn ( "monitor" , ` Monitor # ${ this . id } ' ${ this . name } ': Under Maintenance | Type: ${ this . type } ` ) ;
2021-07-21 00:41:38 +02:00
} else {
2022-04-13 17:33:37 +02:00
log . warn ( "monitor" , ` Monitor # ${ this . id } ' ${ this . name } ': Failing: ${ bean . msg } | Interval: ${ beatInterval } seconds | Type: ${ this . type } ` ) ;
2021-07-21 00:41:38 +02:00
}
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , ` [ ${ this . name } ] Send to socket ` ) ;
2021-06-29 10:06:20 +02:00
io . to ( this . user _id ) . emit ( "heartbeat" , bean . toJSON ( ) ) ;
2021-09-17 08:42:19 +02:00
Monitor . sendStats ( io , this . id , this . user _id ) ;
2021-06-29 10:06:20 +02:00
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , ` [ ${ this . name } ] Store ` ) ;
2021-08-23 12:52:24 +02:00
await R . store ( bean ) ;
2021-11-07 15:00:47 +02:00
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , ` [ ${ this . name } ] prometheus.update ` ) ;
2021-08-23 12:52:24 +02:00
prometheus . update ( bean , tlsInfo ) ;
2021-06-29 10:06:20 +02:00
previousBeat = bean ;
2021-08-23 12:52:24 +02:00
2021-09-08 14:00:16 +02:00
if ( ! this . isStop ) {
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , ` [ ${ this . name } ] SetTimeout for next check. ` ) ;
2021-10-27 08:08:44 +02:00
this . heartbeatInterval = setTimeout ( safeBeat , beatInterval * 1000 ) ;
2021-11-07 15:00:47 +02:00
} else {
2022-04-13 17:33:37 +02:00
log . info ( "monitor" , ` [ ${ this . name } ] isStop = true, no next check. ` ) ;
2021-09-08 14:00:16 +02:00
}
2021-09-17 08:42:19 +02:00
} ;
2021-06-25 15:55:49 +02:00
2022-04-21 19:30:04 +02:00
/** Get a heartbeat and handle errors */
2021-10-27 08:08:44 +02:00
const safeBeat = async ( ) => {
try {
await beat ( ) ;
} catch ( e ) {
console . trace ( e ) ;
2021-10-29 12:24:47 +02:00
errorLog ( e , false ) ;
2022-04-13 17:33:37 +02:00
log . error ( "monitor" , "Please report to https://github.com/louislam/uptime-kuma/issues" ) ;
2021-10-27 08:08:44 +02:00
if ( ! this . isStop ) {
2022-04-13 17:33:37 +02:00
log . info ( "monitor" , "Try to restart the monitor" ) ;
2021-10-27 08:08:44 +02:00
this . heartbeatInterval = setTimeout ( safeBeat , this . interval * 1000 ) ;
}
}
} ;
2021-09-30 18:09:43 +02:00
// Delay Push Type
if ( this . type === "push" ) {
setTimeout ( ( ) => {
2021-10-27 08:08:44 +02:00
safeBeat ( ) ;
2021-09-30 18:09:43 +02:00
} , this . interval * 1000 ) ;
} else {
2021-10-27 08:08:44 +02:00
safeBeat ( ) ;
2021-09-30 18:09:43 +02:00
}
2021-06-25 15:55:49 +02:00
}
2022-04-16 22:11:45 +02:00
/** Stop monitor */
2021-06-25 15:55:49 +02:00
stop ( ) {
2021-08-23 12:52:24 +02:00
clearTimeout ( this . heartbeatInterval ) ;
2021-09-08 14:00:16 +02:00
this . isStop = true ;
2022-01-07 06:26:26 +02:00
this . prometheus ( ) . remove ( ) ;
2021-06-25 15:55:49 +02:00
}
2021-06-30 15:04:58 +02:00
2022-04-21 19:30:04 +02:00
/ * *
* Get a new prometheus instance
* @ returns { Prometheus }
* /
2022-01-07 06:26:26 +02:00
prometheus ( ) {
return new Prometheus ( this ) ;
2021-06-25 15:55:49 +02:00
}
2021-06-30 15:04:58 +02:00
2021-07-30 05:23:04 +02:00
/ * *
* Helper Method :
* returns URL object for further usage
* returns null if url is invalid
2022-04-16 22:11:45 +02:00
* @ returns { ( null | URL ) }
2021-07-30 05:23:04 +02:00
* /
2021-07-22 10:04:32 +02:00
getUrl ( ) {
try {
return new URL ( this . url ) ;
} catch ( _ ) {
return null ;
}
}
2021-07-30 05:23:04 +02:00
/ * *
* Store TLS info to database
* @ param checkCertificateResult
2022-04-16 22:11:45 +02:00
* @ returns { Promise < Object > }
2021-07-30 05:23:04 +02:00
* /
2021-07-22 10:04:32 +02:00
async updateTlsInfo ( checkCertificateResult ) {
2022-04-13 18:30:32 +02:00
let tlsInfoBean = await R . findOne ( "monitor_tls_info" , "monitor_id = ?" , [
2021-07-30 13:18:26 +02:00
this . id ,
2021-07-22 10:04:32 +02:00
] ) ;
2021-10-27 10:12:18 +02:00
2022-04-13 18:30:32 +02:00
if ( tlsInfoBean == null ) {
tlsInfoBean = R . dispense ( "monitor_tls_info" ) ;
tlsInfoBean . monitor _id = this . id ;
2021-10-27 10:12:18 +02:00
} else {
2021-10-27 10:03:16 +02:00
2021-10-27 10:12:18 +02:00
// Clear sent history if the cert changed.
try {
2022-04-13 18:30:32 +02:00
let oldCertInfo = JSON . parse ( tlsInfoBean . info _json ) ;
2021-10-27 10:03:16 +02:00
2021-10-27 10:12:18 +02:00
let isValidObjects = oldCertInfo && oldCertInfo . certInfo && checkCertificateResult && checkCertificateResult . certInfo ;
if ( isValidObjects ) {
if ( oldCertInfo . certInfo . fingerprint256 !== checkCertificateResult . certInfo . fingerprint256 ) {
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , "Resetting sent_history" ) ;
2021-10-27 10:12:18 +02:00
await R . exec ( "DELETE FROM notification_sent_history WHERE type = 'certificate' AND monitor_id = ?" , [
this . id
] ) ;
} else {
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , "No need to reset sent_history" ) ;
log . debug ( "monitor" , oldCertInfo . certInfo . fingerprint256 ) ;
log . debug ( "monitor" , checkCertificateResult . certInfo . fingerprint256 ) ;
2021-10-27 10:12:18 +02:00
}
} else {
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , "Not valid object" ) ;
2021-10-27 10:12:18 +02:00
}
} catch ( e ) { }
2021-10-27 10:03:16 +02:00
}
2022-04-13 18:30:32 +02:00
tlsInfoBean . info _json = JSON . stringify ( checkCertificateResult ) ;
await R . store ( tlsInfoBean ) ;
2021-08-10 11:51:30 +02:00
return checkCertificateResult ;
2021-07-22 10:04:32 +02:00
}
2022-04-16 22:11:45 +02:00
/ * *
* Send statistics to clients
* @ param { Server } io Socket server instance
* @ param { number } monitorID ID of monitor to send
* @ param { number } userID ID of user to send to
* /
2021-06-30 15:04:58 +02:00
static async sendStats ( io , monitorID , userID ) {
2021-08-30 08:55:33 +02:00
const hasClients = getTotalClientInRoom ( io , userID ) > 0 ;
if ( hasClients ) {
await Monitor . sendAvgPing ( 24 , io , monitorID , userID ) ;
await Monitor . sendUptime ( 24 , io , monitorID , userID ) ;
await Monitor . sendUptime ( 24 * 30 , io , monitorID , userID ) ;
await Monitor . sendCertInfo ( io , monitorID , userID ) ;
} else {
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , "No clients in the room, no need to send stats" ) ;
2021-08-30 08:55:33 +02:00
}
2021-06-30 15:04:58 +02:00
}
2021-07-01 07:11:16 +02:00
/ * *
2022-04-16 22:11:45 +02:00
* Send the average ping to user
* @ param { number } duration Hours
2021-07-01 07:11:16 +02:00
* /
2021-06-30 15:04:58 +02:00
static async sendAvgPing ( duration , io , monitorID , userID ) {
2021-08-16 20:09:40 +02:00
const timeLogger = new TimeLogger ( ) ;
2021-06-30 15:04:58 +02:00
let avgPing = parseInt ( await R . getCell ( `
SELECT AVG ( ping )
FROM heartbeat
2021-07-10 06:04:40 +02:00
WHERE time > DATETIME ( 'now' , ? || ' hours' )
2021-07-01 07:11:16 +02:00
AND ping IS NOT NULL
2021-06-30 15:04:58 +02:00
AND monitor _id = ? ` , [
- duration ,
2021-07-30 13:18:26 +02:00
monitorID ,
2021-06-30 15:04:58 +02:00
] ) ) ;
2021-08-16 20:09:40 +02:00
timeLogger . print ( ` [Monitor: ${ monitorID } ] avgPing ` ) ;
2021-06-30 15:04:58 +02:00
io . to ( userID ) . emit ( "avgPing" , monitorID , avgPing ) ;
}
2022-04-16 22:11:45 +02:00
/ * *
2022-04-22 19:42:47 +02:00
* Send certificate information to client
2022-04-16 22:11:45 +02:00
* @ param { Server } io Socket server instance
* @ param { number } monitorID ID of monitor to send
* @ param { number } userID ID of user to send to
* /
2021-07-22 10:04:32 +02:00
static async sendCertInfo ( io , monitorID , userID ) {
2022-04-16 19:39:49 +02:00
let tlsInfo = await R . findOne ( "monitor_tls_info" , "monitor_id = ?" , [
2021-07-30 13:18:26 +02:00
monitorID ,
2021-07-22 10:04:32 +02:00
] ) ;
2022-04-16 19:39:49 +02:00
if ( tlsInfo != null ) {
io . to ( userID ) . emit ( "certInfo" , monitorID , tlsInfo . info _json ) ;
2021-07-22 10:04:32 +02:00
}
2021-07-21 06:09:09 +02:00
}
2021-07-01 07:11:16 +02:00
/ * *
2021-07-09 08:14:03 +02:00
* Uptime with calculation
* Calculation based on :
* https : //www.uptrends.com/support/kb/reporting/calculation-of-uptime-and-downtime
2022-04-16 22:11:45 +02:00
* @ param { number } duration Hours
* @ param { number } monitorID ID of monitor to calculate
2021-07-01 07:11:16 +02:00
* /
2021-09-22 09:10:08 +02:00
static async calcUptime ( duration , monitorID ) {
2021-08-16 20:09:40 +02:00
const timeLogger = new TimeLogger ( ) ;
2021-09-09 09:46:28 +02:00
const startTime = R . isoDateTime ( dayjs . utc ( ) . subtract ( duration , "hour" ) ) ;
// Handle if heartbeat duration longer than the target duration
// e.g. If the last beat's duration is bigger that the 24hrs window, it will use the duration between the (beat time - window margin) (THEN case in SQL)
let result = await R . getRow ( `
SELECT
-- SUM all duration , also trim off the beat out of time window
SUM (
CASE
WHEN ( JULIANDAY ( \ ` time \` ) - JULIANDAY(?)) * 86400 < duration
THEN ( JULIANDAY ( \ ` time \` ) - JULIANDAY(?)) * 86400
ELSE duration
END
) AS total _duration ,
-- SUM all uptime duration , also trim off the beat out of time window
SUM (
CASE
2022-01-23 16:22:00 +02:00
WHEN ( status = 1 OR status = 3 )
2021-09-09 09:46:28 +02:00
THEN
CASE
WHEN ( JULIANDAY ( \ ` time \` ) - JULIANDAY(?)) * 86400 < duration
THEN ( JULIANDAY ( \ ` time \` ) - JULIANDAY(?)) * 86400
ELSE duration
END
END
) AS uptime _duration
2021-07-01 07:11:16 +02:00
FROM heartbeat
2021-09-09 09:46:28 +02:00
WHERE time > ?
AND monitor _id = ?
` , [
startTime , startTime , startTime , startTime , startTime ,
2021-07-30 13:18:26 +02:00
monitorID ,
2021-07-01 11:00:23 +02:00
] ) ;
2021-08-16 20:09:40 +02:00
timeLogger . print ( ` [Monitor: ${ monitorID } ][ ${ duration } ] sendUptime ` ) ;
2021-09-09 09:46:28 +02:00
let totalDuration = result . total _duration ;
let uptimeDuration = result . uptime _duration ;
2021-09-09 09:55:34 +02:00
let uptime = 0 ;
2021-07-01 07:11:16 +02:00
2021-09-09 09:55:34 +02:00
if ( totalDuration > 0 ) {
uptime = uptimeDuration / totalDuration ;
if ( uptime < 0 ) {
uptime = 0 ;
}
2021-07-11 14:07:03 +02:00
2021-09-09 09:55:34 +02:00
} else {
// Handle new monitor with only one beat, because the beat's duration = 0
let status = parseInt ( await R . getCell ( "SELECT `status` FROM heartbeat WHERE monitor_id = ?" , [ monitorID ] ) ) ;
2021-09-17 08:42:19 +02:00
2021-09-09 09:55:34 +02:00
if ( status === UP ) {
uptime = 1 ;
}
2021-07-01 11:00:23 +02:00
}
2021-09-22 09:10:08 +02:00
return uptime ;
}
/ * *
* Send Uptime
2022-04-16 22:11:45 +02:00
* @ param { number } duration Hours
* @ param { Server } io Socket server instance
* @ param { number } monitorID ID of monitor to send
* @ param { number } userID ID of user to send to
2021-09-22 09:10:08 +02:00
* /
static async sendUptime ( duration , io , monitorID , userID ) {
const uptime = await this . calcUptime ( duration , monitorID ) ;
2021-07-01 07:11:16 +02:00
io . to ( userID ) . emit ( "uptime" , monitorID , duration , uptime ) ;
2021-06-30 15:04:58 +02:00
}
2021-10-07 11:39:58 +02:00
2022-04-16 22:11:45 +02:00
/ * *
* Has status of monitor changed since last beat ?
* @ param { boolean } isFirstBeat Is this the first beat of this monitor ?
* @ param { const } previousBeatStatus Status of the previous beat
* @ param { const } currentBeatStatus Status of the current beat
* @ returns { boolean } True if is an important beat else false
* /
2021-10-14 16:32:15 +02:00
static isImportantBeat ( isFirstBeat , previousBeatStatus , currentBeatStatus ) {
// * ? -> ANY STATUS = important [isFirstBeat]
// UP -> PENDING = not important
// * UP -> DOWN = important
// UP -> UP = not important
// PENDING -> PENDING = not important
// * PENDING -> DOWN = important
// PENDING -> UP = not important
// DOWN -> PENDING = this case not exists
// DOWN -> DOWN = not important
// * DOWN -> UP = important
2022-01-23 16:22:00 +02:00
// MAINTENANCE -> MAINTENANCE = not important
// * MAINTENANCE -> UP = important
// * MAINTENANCE -> DOWN = important
// * DOWN -> MAINTENANCE = important
// * UP -> MAINTENANCE = important
return isFirstBeat ||
( previousBeatStatus === DOWN && currentBeatStatus === MAINTENANCE ) ||
( previousBeatStatus === UP && currentBeatStatus === MAINTENANCE ) ||
( previousBeatStatus === MAINTENANCE && currentBeatStatus === DOWN ) ||
( previousBeatStatus === MAINTENANCE && currentBeatStatus === UP ) ||
( previousBeatStatus === UP && currentBeatStatus === DOWN ) ||
( previousBeatStatus === DOWN && currentBeatStatus === UP ) ||
( previousBeatStatus === PENDING && currentBeatStatus === DOWN ) ;
}
static isImportantForNotification ( isFirstBeat , previousBeatStatus , currentBeatStatus ) {
// * ? -> ANY STATUS = important [isFirstBeat]
// UP -> PENDING = not important
// * UP -> DOWN = important
// UP -> UP = not important
// PENDING -> PENDING = not important
// * PENDING -> DOWN = important
// PENDING -> UP = not important
// DOWN -> PENDING = this case not exists
// DOWN -> DOWN = not important
// * DOWN -> UP = important
// MAINTENANCE -> MAINTENANCE = not important
// MAINTENANCE -> UP = not important
// * MAINTENANCE -> DOWN = important
// DOWN -> MAINTENANCE = not important
// UP -> MAINTENANCE = not important
return isFirstBeat ||
( previousBeatStatus === MAINTENANCE && currentBeatStatus === DOWN ) ||
2021-10-14 16:32:15 +02:00
( previousBeatStatus === UP && currentBeatStatus === DOWN ) ||
( previousBeatStatus === DOWN && currentBeatStatus === UP ) ||
( previousBeatStatus === PENDING && currentBeatStatus === DOWN ) ;
}
2022-04-16 22:11:45 +02:00
/ * *
* Send a notification about a monitor
* @ param { boolean } isFirstBeat Is this beat the first of this monitor ?
* @ param { Monitor } monitor The monitor to send a notificaton about
* @ param { Bean } bean Status information about monitor
* /
2021-10-14 16:32:15 +02:00
static async sendNotification ( isFirstBeat , monitor , bean ) {
if ( ! isFirstBeat || bean . status === DOWN ) {
2021-10-27 09:33:15 +02:00
const notificationList = await Monitor . getNotificationList ( monitor ) ;
2021-10-14 16:32:15 +02:00
let text ;
if ( bean . status === UP ) {
text = "✅ Up" ;
} else {
text = "🔴 Down" ;
}
let msg = ` [ ${ monitor . name } ] [ ${ text } ] ${ bean . msg } ` ;
for ( let notification of notificationList ) {
try {
2022-04-17 13:30:58 +02:00
await Notification . send ( JSON . parse ( notification . config ) , msg , await monitor . toJSON ( false ) , bean . toJSON ( ) ) ;
2021-10-14 16:32:15 +02:00
} catch ( e ) {
2022-04-13 17:33:37 +02:00
log . error ( "monitor" , "Cannot send notification to " + notification . name ) ;
log . error ( "monitor" , e ) ;
2021-10-14 16:32:15 +02:00
}
}
}
}
2022-04-16 22:11:45 +02:00
/ * *
* Get list of notification providers for a given monitor
* @ param { Monitor } monitor Monitor to get notification providers for
* @ returns { Promise < LooseObject < any > [ ] > }
* /
2021-10-27 09:33:15 +02:00
static async getNotificationList ( monitor ) {
let notificationList = await R . getAll ( "SELECT notification.* FROM notification, monitor_notification WHERE monitor_id = ? AND monitor_notification.notification_id = notification.id " , [
monitor . id ,
] ) ;
return notificationList ;
}
2022-04-16 22:11:45 +02:00
/ * *
* Send notification about a certificate
* @ param { Object } tlsInfoObject Information about certificate
* /
2021-10-27 09:33:15 +02:00
async sendCertNotification ( tlsInfoObject ) {
if ( tlsInfoObject && tlsInfoObject . certInfo && tlsInfoObject . certInfo . daysRemaining ) {
const notificationList = await Monitor . getNotificationList ( this ) ;
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , "call sendCertNotificationByTargetDays" ) ;
2021-10-27 09:33:15 +02:00
await this . sendCertNotificationByTargetDays ( tlsInfoObject . certInfo . daysRemaining , 21 , notificationList ) ;
await this . sendCertNotificationByTargetDays ( tlsInfoObject . certInfo . daysRemaining , 14 , notificationList ) ;
await this . sendCertNotificationByTargetDays ( tlsInfoObject . certInfo . daysRemaining , 7 , notificationList ) ;
}
}
2022-04-16 22:11:45 +02:00
/ * *
* Send a certificate notification when certificate expires in less
* than target days
* @ param { number } daysRemaining Number of days remaining on certifcate
* @ param { number } targetDays Number of days to alert after
2022-04-21 21:02:18 +02:00
* @ param { LooseObject < any > [ ] } notificationList List of notification providers
2022-04-16 22:11:45 +02:00
* @ returns { Promise < void > }
* /
2021-10-27 09:33:15 +02:00
async sendCertNotificationByTargetDays ( daysRemaining , targetDays , notificationList ) {
if ( daysRemaining > targetDays ) {
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , ` No need to send cert notification. ${ daysRemaining } > ${ targetDays } ` ) ;
2021-10-27 09:33:15 +02:00
return ;
}
if ( notificationList . length > 0 ) {
let row = await R . getRow ( "SELECT * FROM notification_sent_history WHERE type = ? AND monitor_id = ? AND days = ?" , [
"certificate" ,
this . id ,
targetDays ,
] ) ;
// Sent already, no need to send again
if ( row ) {
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , "Sent already, no need to send again" ) ;
2021-10-27 09:33:15 +02:00
return ;
}
let sent = false ;
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , "Send certificate notification" ) ;
2021-10-27 09:33:15 +02:00
for ( let notification of notificationList ) {
try {
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , "Sending to " + notification . name ) ;
2021-10-27 10:12:18 +02:00
await Notification . send ( JSON . parse ( notification . config ) , ` [ ${ this . name } ][ ${ this . url } ] Certificate will be expired in ${ daysRemaining } days ` ) ;
2021-10-27 09:33:15 +02:00
sent = true ;
} catch ( e ) {
2022-04-13 17:33:37 +02:00
log . error ( "monitor" , "Cannot send cert notification to " + notification . name ) ;
log . error ( "monitor" , e ) ;
2021-10-27 09:33:15 +02:00
}
}
if ( sent ) {
await R . exec ( "INSERT INTO notification_sent_history (type, monitor_id, days) VALUES(?, ?, ?)" , [
"certificate" ,
this . id ,
targetDays ,
] ) ;
}
} else {
2022-04-13 17:33:37 +02:00
log . debug ( "monitor" , "No notification, no need to send cert notification" ) ;
2021-10-27 09:33:15 +02:00
}
}
2021-12-08 08:59:59 +02:00
2022-04-16 22:11:45 +02:00
/ * *
* Get the status of the previous heartbeat
* @ param { number } monitorID ID of monitor to check
* @ returns { Promise < LooseObject < any >> }
* /
2021-12-08 08:59:59 +02:00
static async getPreviousHeartbeat ( monitorID ) {
return await R . getRow ( `
SELECT status , time FROM heartbeat
WHERE id = ( select MAX ( id ) from heartbeat where monitor _id = ? )
` , [
monitorID
] ) ;
}
2022-01-25 20:07:27 +02:00
static async isUnderMaintenance ( monitorID ) {
2022-04-30 14:57:08 +02:00
const maintenance = await R . getRow ( "SELECT COUNT(*) AS count FROM monitor_maintenance mm JOIN maintenance ON mm.maintenance_id = maintenance.id WHERE mm.monitor_id = ? AND datetime(maintenance.start_date) <= datetime('now') AND datetime(maintenance.end_date) >= datetime('now') LIMIT 1" , [ monitorID ] ) ;
2022-01-25 20:07:27 +02:00
return maintenance . count !== 0 ;
}
2021-06-25 15:55:49 +02:00
}
module . exports = Monitor ;