@ -5,6 +5,7 @@ var timezone = require('dayjs/plugin/timezone') 
			
		
	
		
			
				
					dayjs . extend ( utc )  
			
		
	
		
			
				
					dayjs . extend ( timezone )  
			
		
	
		
			
				
					const  axios  =  require ( "axios" ) ;  
			
		
	
		
			
				
					const  { UP ,  DOWN ,  PENDING }  =  require ( "../util" ) ;  
			
		
	
		
			
				
					const  { tcping ,  ping }  =  require ( "../util-server" ) ;  
			
		
	
		
			
				
					const  { R }  =  require ( "redbean-node" ) ;  
			
		
	
		
			
				
					const  { BeanModel }  =  require ( "redbean-node/dist/bean-model" ) ;  
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -49,19 +50,22 @@ class Monitor extends BeanModel { 
			
		
	
		
			
				
					        let  retries  =  0 ;  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					        const  beat  =  async  ( )  =>  {  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					            if  ( !  previousBeat )  {  
			
		
	
		
			
				
					                previousBeat  =  await  R . findOne ( "heartbeat" ,  " monitor_id = ? ORDER BY time DESC" ,  [  
			
		
	
		
			
				
					                    this . id  
			
		
	
		
			
				
					                ] )  
			
		
	
		
			
				
					            }  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					            const  isFirstBeat  =  ! previousBeat ;  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					            let  bean  =  R . dispense ( "heartbeat" )  
			
		
	
		
			
				
					            bean . monitor_id  =  this . id ;  
			
		
	
		
			
				
					            bean . time  =  R . isoDateTime ( dayjs . utc ( ) ) ;  
			
		
	
		
			
				
					            bean . status  =  0 ;  
			
		
	
		
			
				
					            bean . status  =  DOWN ;  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					            // Duration
  
			
		
	
		
			
				
					            if  ( previous Beat)  {  
			
		
	
		
			
				
					            if  ( !  isFirst Beat)  {  
			
		
	
		
			
				
					                bean . duration  =  dayjs ( bean . time ) . diff ( dayjs ( previousBeat . time ) ,  'second' ) ;  
			
		
	
		
			
				
					            }  else  {  
			
		
	
		
			
				
					                bean . duration  =  0 ;  
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -77,7 +81,7 @@ class Monitor extends BeanModel { 
			
		
	
		
			
				
					                    bean . ping  =  dayjs ( ) . valueOf ( )  -  startTime ;  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					                    if  ( this . type  ===  "http" )  {  
			
		
	
		
			
				
					                        bean . status  =  1 ;  
			
		
	
		
			
				
					                        bean . status  =  UP ;  
			
		
	
		
			
				
					                    }  else  {  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					                        let  data  =  res . data ;  
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -89,7 +93,7 @@ class Monitor extends BeanModel { 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					                        if  ( data . includes ( this . keyword ) )  {  
			
		
	
		
			
				
					                            bean . msg  +=  ", keyword is found"  
			
		
	
		
			
				
					                            bean . status  =  1 ;  
			
		
	
		
			
				
					                            bean . status  =  UP ;  
			
		
	
		
			
				
					                        }  else  {  
			
		
	
		
			
				
					                            throw  new  Error ( bean . msg  +  ", but keyword is not found" )  
			
		
	
		
			
				
					                        }  
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -100,12 +104,12 @@ class Monitor extends BeanModel { 
			
		
	
		
			
				
					                }  else  if  ( this . type  ===  "port" )  {  
			
		
	
		
			
				
					                    bean . ping  =  await  tcping ( this . hostname ,  this . port ) ;  
			
		
	
		
			
				
					                    bean . msg  =  ""  
			
		
	
		
			
				
					                    bean . status  =  1 ;  
			
		
	
		
			
				
					                    bean . status  =  UP ;  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					                }  else  if  ( this . type  ===  "ping" )  {  
			
		
	
		
			
				
					                    bean . ping  =  await  ping ( this . hostname ) ;  
			
		
	
		
			
				
					                    bean . msg  =  ""  
			
		
	
		
			
				
					                    bean . status  =  1 ;  
			
		
	
		
			
				
					                    bean . status  =  UP ;  
			
		
	
		
			
				
					                }  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					                retries  =  0 ;  
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -113,24 +117,39 @@ class Monitor extends BeanModel { 
			
		
	
		
			
				
					            }  catch  ( error )  {  
			
		
	
		
			
				
					                if  ( ( this . maxretries  >  0 )  &&  ( retries  <  this . maxretries ) )  {  
			
		
	
		
			
				
					                    retries ++ ;  
			
		
	
		
			
				
					                    bean . status  =  2 ;  
			
		
	
		
			
				
					                    bean . status  =  PENDING ;  
			
		
	
		
			
				
					                }  
			
		
	
		
			
				
					                bean . msg  =  error . message ;  
			
		
	
		
			
				
					            }  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					            // * ? -> 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
  
			
		
	
		
			
				
					            let  isImportant  =  isFirstBeat  ||  
			
		
	
		
			
				
					                ( previousBeat . status  ===  UP  &&  bean . status  ===  DOWN )  ||  
			
		
	
		
			
				
					                ( previousBeat . status  ===  DOWN  &&  bean . status  ===  UP )  ||  
			
		
	
		
			
				
					                ( previousBeat . status  ===  PENDING  &&  bean . status  ===  DOWN ) ;  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					            // Mark as important if status changed, ignore pending pings,
  
			
		
	
		
			
				
					            // Don't notify if disrupted changes to up
  
			
		
	
		
			
				
					            if  ( ( ! previousBeat )  ||  ( ( previousBeat . status  !==  bean . status )  &&  bean . status  !==  2  &&  ! ( previousBeat . status  ===  2  &&  bean . status  !==  0 ) ) )  {  
			
		
	
		
			
				
					            if  ( isImportant )  {  
			
		
	
		
			
				
					                bean . important  =  true ;  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					                // Do not send if first beat is UP
  
			
		
	
		
			
				
					                if  ( previousBeat  ||  bean . status  !==  1 )  {  
			
		
	
		
			
				
					                // Send only if the first beat is DOWN 
  
			
		
	
		
			
				
					                if  ( ! isFirst Beat||  bean . status  ===  DOWN )  {  
			
		
	
		
			
				
					                    let  notificationList  =  await  R . getAll ( ` SELECT notification.* FROM notification, monitor_notification WHERE monitor_id = ? AND monitor_notification.notification_id = notification.id  ` ,  [  
			
		
	
		
			
				
					                        this . id  
			
		
	
		
			
				
					                    ] )  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					                    let  text ;  
			
		
	
		
			
				
					                    if  ( bean . status  ===  1 )  {  
			
		
	
		
			
				
					                    if  ( bean . status  ===  UP )  {  
			
		
	
		
			
				
					                        text  =  "✅ Up"  
			
		
	
		
			
				
					                    }  else  {  
			
		
	
		
			
				
					                        text  =  "🔴 Down"  
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -151,8 +170,10 @@ class Monitor extends BeanModel { 
			
		
	
		
			
				
					                bean . important  =  false ;  
			
		
	
		
			
				
					            }  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					            if  ( bean . status  ===  1 )  {  
			
		
	
		
			
				
					            if  ( bean . status  ===  UP )  {  
			
		
	
		
			
				
					                console . info ( ` Monitor # ${ this . id }  ' ${ this . name } ': Successful Response:  ${ bean . ping }  ms | Interval:  ${ this . interval }  seconds | Type:  ${ this . type } ` )  
			
		
	
		
			
				
					            }  else  if  ( bean . status  ===  PENDING )  {  
			
		
	
		
			
				
					                console . warn ( ` Monitor # ${ this . id }  ' ${ this . name } ': Pending:  ${ bean . msg }  | Type:  ${ this . type } ` )  
			
		
	
		
			
				
					            }  else  {  
			
		
	
		
			
				
					                console . warn ( ` Monitor # ${ this . id }  ' ${ this . name } ': Failing:  ${ bean . msg }  | Type:  ${ this . type } ` )  
			
		
	
		
			
				
					            }