@ -3,10 +3,10 @@ const dayjs = require("dayjs");
const axios = require ( "axios" ) ;
const axios = require ( "axios" ) ;
const { Prometheus } = require ( "../prometheus" ) ;
const { Prometheus } = require ( "../prometheus" ) ;
const { log , UP , DOWN , PENDING , MAINTENANCE , flipStatus , MAX _INTERVAL _SECOND , MIN _INTERVAL _SECOND ,
const { log , UP , DOWN , PENDING , MAINTENANCE , flipStatus , MAX _INTERVAL _SECOND , MIN _INTERVAL _SECOND ,
SQL _DATETIME _FORMAT
SQL _DATETIME _FORMAT , isDev , sleep , getRandomInt
} = require ( "../../src/util" ) ;
} = require ( "../../src/util" ) ;
const { tcping , ping , checkCertificate , checkStatusCode , getTotalClientInRoom , setting , mssqlQuery , postgresQuery , mysqlQuery , mqttAsync , setSetting , httpNtlm , radius , grpcQuery ,
const { tcping , ping , checkCertificate , checkStatusCode , getTotalClientInRoom , setting , mssqlQuery , postgresQuery , mysqlQuery , mqttAsync , setSetting , httpNtlm , radius , grpcQuery ,
redisPingAsync , mongodbPing , kafkaProducerAsync , getOidcTokenClientCredentials , axiosAbortSignal
redisPingAsync , mongodbPing , kafkaProducerAsync , getOidcTokenClientCredentials , rootCertificatesFingerprints, axiosAbortSignal
} = require ( "../util-server" ) ;
} = require ( "../util-server" ) ;
const { R } = require ( "redbean-node" ) ;
const { R } = require ( "redbean-node" ) ;
const { BeanModel } = require ( "redbean-node/dist/bean-model" ) ;
const { BeanModel } = require ( "redbean-node/dist/bean-model" ) ;
@ -23,6 +23,8 @@ const jsonata = require("jsonata");
const jwt = require ( "jsonwebtoken" ) ;
const jwt = require ( "jsonwebtoken" ) ;
const { UptimeCalculator } = require ( "../uptime-calculator" ) ;
const { UptimeCalculator } = require ( "../uptime-calculator" ) ;
const rootCertificates = rootCertificatesFingerprints ( ) ;
/ * *
/ * *
* status :
* status :
* 0 = DOWN
* 0 = DOWN
@ -146,8 +148,8 @@ class Monitor extends BeanModel {
expectedValue : this . expectedValue ,
expectedValue : this . expectedValue ,
kafkaProducerTopic : this . kafkaProducerTopic ,
kafkaProducerTopic : this . kafkaProducerTopic ,
kafkaProducerBrokers : JSON . parse ( this . kafkaProducerBrokers ) ,
kafkaProducerBrokers : JSON . parse ( this . kafkaProducerBrokers ) ,
kafkaProducerSsl : this . kafkaProducerSsl === "1" && true || false ,
kafkaProducerSsl : this . getKafkaProducerSsl( ) ,
kafkaProducerAllowAutoTopicCreation : this . kafkaProducerAllowAutoTopicCreation === "1" && true || false ,
kafkaProducerAllowAutoTopicCreation : this . getKafkaProducerAllowAutoTopicCreation( ) ,
kafkaProducerMessage : this . kafkaProducerMessage ,
kafkaProducerMessage : this . kafkaProducerMessage ,
screenshot ,
screenshot ,
} ;
} ;
@ -298,6 +300,22 @@ class Monitor extends BeanModel {
return Boolean ( this . gamedigGivenPortOnly ) ;
return Boolean ( this . gamedigGivenPortOnly ) ;
}
}
/ * *
* Parse to boolean
* @ returns { boolean } Kafka Producer Ssl enabled ?
* /
getKafkaProducerSsl ( ) {
return Boolean ( this . kafkaProducerSsl ) ;
}
/ * *
* Parse to boolean
* @ returns { boolean } Kafka Producer Allow Auto Topic Creation Enabled ?
* /
getKafkaProducerAllowAutoTopicCreation ( ) {
return Boolean ( this . kafkaProducerAllowAutoTopicCreation ) ;
}
/ * *
/ * *
* Start monitor
* Start monitor
* @ param { Server } io Socket server instance
* @ param { Server } io Socket server instance
@ -324,6 +342,16 @@ class Monitor extends BeanModel {
}
}
}
}
// Evil
if ( isDev ) {
if ( process . env . EVIL _RANDOM _MONITOR _SLEEP === "SURE" ) {
if ( getRandomInt ( 0 , 100 ) === 0 ) {
log . debug ( "evil" , ` [ ${ this . name } ] Evil mode: Random sleep: ` + beatInterval * 10000 ) ;
await sleep ( beatInterval * 10000 ) ;
}
}
}
// Expose here for prometheus update
// Expose here for prometheus update
// undefined if not https
// undefined if not https
let tlsInfo = undefined ;
let tlsInfo = undefined ;
@ -346,6 +374,12 @@ class Monitor extends BeanModel {
bean . status = flipStatus ( bean . status ) ;
bean . status = flipStatus ( bean . status ) ;
}
}
// Runtime patch timeout if it is 0
// See https://github.com/louislam/uptime-kuma/pull/3961#issuecomment-1804149144
if ( this . timeout <= 0 ) {
this . timeout = this . interval * 1000 * 0.8 ;
}
try {
try {
if ( await Monitor . isUnderMaintenance ( this . id ) ) {
if ( await Monitor . isUnderMaintenance ( this . id ) ) {
bean . msg = "Monitor under maintenance" ;
bean . msg = "Monitor under maintenance" ;
@ -728,7 +762,7 @@ class Monitor extends BeanModel {
} else if ( this . type === "sqlserver" ) {
} else if ( this . type === "sqlserver" ) {
let startTime = dayjs ( ) . valueOf ( ) ;
let startTime = dayjs ( ) . valueOf ( ) ;
await mssqlQuery ( this . databaseConnectionString , this . databaseQuery ) ;
await mssqlQuery ( this . databaseConnectionString , this . databaseQuery || "SELECT 1" ) ;
bean . msg = "" ;
bean . msg = "" ;
bean . status = UP ;
bean . status = UP ;
@ -767,7 +801,7 @@ class Monitor extends BeanModel {
} else if ( this . type === "postgres" ) {
} else if ( this . type === "postgres" ) {
let startTime = dayjs ( ) . valueOf ( ) ;
let startTime = dayjs ( ) . valueOf ( ) ;
await postgresQuery ( this . databaseConnectionString , this . databaseQuery ) ;
await postgresQuery ( this . databaseConnectionString , this . databaseQuery || "SELECT 1" ) ;
bean . msg = "" ;
bean . msg = "" ;
bean . status = UP ;
bean . status = UP ;
@ -775,7 +809,11 @@ class Monitor extends BeanModel {
} else if ( this . type === "mysql" ) {
} else if ( this . type === "mysql" ) {
let startTime = dayjs ( ) . valueOf ( ) ;
let startTime = dayjs ( ) . valueOf ( ) ;
bean . msg = await mysqlQuery ( this . databaseConnectionString , this . databaseQuery ) ;
// Use `radius_password` as `password` field, since there are too many unnecessary fields
// TODO: rename `radius_password` to `password` later for general use
let mysqlPassword = this . radiusPassword ;
bean . msg = await mysqlQuery ( this . databaseConnectionString , this . databaseQuery || "SELECT 1" , mysqlPassword ) ;
bean . status = UP ;
bean . status = UP ;
bean . ping = dayjs ( ) . valueOf ( ) - startTime ;
bean . ping = dayjs ( ) . valueOf ( ) - startTime ;
} else if ( this . type === "mongodb" ) {
} else if ( this . type === "mongodb" ) {
@ -959,6 +997,7 @@ class Monitor extends BeanModel {
log . debug ( "monitor" , ` [ ${ this . name } ] Next heartbeat in: ${ intervalRemainingMs } ms ` ) ;
log . debug ( "monitor" , ` [ ${ this . name } ] Next heartbeat in: ${ intervalRemainingMs } ms ` ) ;
this . heartbeatInterval = setTimeout ( safeBeat , intervalRemainingMs ) ;
this . heartbeatInterval = setTimeout ( safeBeat , intervalRemainingMs ) ;
this . lastScheduleBeatTime = dayjs ( ) ;
} else {
} else {
log . info ( "monitor" , ` [ ${ this . name } ] isStop = true, no next check. ` ) ;
log . info ( "monitor" , ` [ ${ this . name } ] isStop = true, no next check. ` ) ;
}
}
@ -971,7 +1010,9 @@ class Monitor extends BeanModel {
* /
* /
const safeBeat = async ( ) => {
const safeBeat = async ( ) => {
try {
try {
this . lastStartBeatTime = dayjs ( ) ;
await beat ( ) ;
await beat ( ) ;
this . lastEndBeatTime = dayjs ( ) ;
} catch ( e ) {
} catch ( e ) {
console . trace ( e ) ;
console . trace ( e ) ;
UptimeKumaServer . errorLog ( e , false ) ;
UptimeKumaServer . errorLog ( e , false ) ;
@ -980,6 +1021,9 @@ class Monitor extends BeanModel {
if ( ! this . isStop ) {
if ( ! this . isStop ) {
log . info ( "monitor" , "Try to restart the monitor" ) ;
log . info ( "monitor" , "Try to restart the monitor" ) ;
this . heartbeatInterval = setTimeout ( safeBeat , this . interval * 1000 ) ;
this . heartbeatInterval = setTimeout ( safeBeat , this . interval * 1000 ) ;
this . lastScheduleBeatTime = dayjs ( ) ;
} else {
log . info ( "monitor" , "isStop = true, no next check." ) ;
}
}
}
}
} ;
} ;
@ -1320,7 +1364,10 @@ class Monitor extends BeanModel {
let certInfo = tlsInfoObject . certInfo ;
let certInfo = tlsInfoObject . certInfo ;
while ( certInfo ) {
while ( certInfo ) {
let subjectCN = certInfo . subject [ "CN" ] ;
let subjectCN = certInfo . subject [ "CN" ] ;
if ( certInfo . daysRemaining > targetDays ) {
if ( rootCertificates . has ( certInfo . fingerprint256 ) ) {
log . debug ( "monitor" , ` Known root cert: ${ certInfo . certType } certificate " ${ subjectCN } " ( ${ certInfo . daysRemaining } days valid) on ${ targetDays } deadline. ` ) ;
break ;
} else if ( certInfo . daysRemaining > targetDays ) {
log . debug ( "monitor" , ` No need to send cert notification for ${ certInfo . certType } certificate " ${ subjectCN } " ( ${ certInfo . daysRemaining } days valid) on ${ targetDays } deadline. ` ) ;
log . debug ( "monitor" , ` No need to send cert notification for ${ certInfo . certType } certificate " ${ subjectCN } " ( ${ certInfo . daysRemaining } days valid) on ${ targetDays } deadline. ` ) ;
} else {
} else {
log . debug ( "monitor" , ` call sendCertNotificationByTargetDays for ${ targetDays } deadline on certificate ${ subjectCN } . ` ) ;
log . debug ( "monitor" , ` call sendCertNotificationByTargetDays for ${ targetDays } deadline on certificate ${ subjectCN } . ` ) ;