2020-10-11 16:54:58 -07:00
import opts from 'config'
import Notifier from '@uci-utils/notify'
import Route53 from '@uci-utils/route53'
import pIp from 'public-ip'
import { lookup as clookup } from 'dns'
import { promisify } from 'util'
const lookup = promisify ( clookup )
import { CronJob } from 'cron'
const rname = process . argv [ 2 ] || opts . domain
if ( ! rname ) {
2020-10-13 08:19:38 -07:00
console . log ( 'exiting, must supply (sub)domain record name to check' )
console . log ( JSON . stringify ( opts , null , 3 ) )
2020-10-11 16:54:58 -07:00
process . exit ( )
}
2020-10-13 08:19:38 -07:00
const verbose = ( opts . verbose || '' ) . length
const interval = ( opts . lookup || { } ) . interval || 10 // seconds
const timeout = ( opts . lookup || { } ) . timeout || 5 // minutes
if ( verbose ) console . log ( JSON . stringify ( opts , null , 3 ) )
2020-10-11 16:54:58 -07:00
let notify = ( ) => { } // noop
const route53 = new Route53 ( opts . aws ? opts . aws : { profile : 'default' } )
;
( async ( ) => {
if ( opts . notify ) {
const notifier = await Notifier . create ( opts . notify )
notify = notifier . send . bind ( notifier )
}
2020-10-13 08:19:38 -07:00
if ( opts . cron && ! opts . debug ) {
2020-10-11 16:54:58 -07:00
console . log ( 'setting up check cron job every' , opts . cron , 'minutes' )
const cronstr = ` 0 */ ${ opts . cron } * * * * `
const job = new CronJob ( cronstr , checkIP )
console . log ( 'cron job starting with:' , cronstr )
job . start ( )
} else {
console . log ( 'running check a single time' )
2020-10-13 08:19:38 -07:00
checkIP ( )
2020-10-11 16:54:58 -07:00
}
} ) ( ) . catch ( err => {
console . log ( 'FATAL: IP Updater! \n' , err )
process . exitCode = 1
process . kill ( process . pid , 'SIGINT' ) // this will restart via systemd unit
} )
async function checkIP ( ) {
let curr = await pIp . v4 ( )
if ( opts . dev ) {
let ip = curr . split ( '.' )
ip [ 0 ] = ip [ 0 ] - Math . floor ( Math . random ( ) * 10 )
curr = ip . join ( '.' )
}
const saved = await route53 . getZoneRecordValue ( rname )
2020-10-13 08:19:38 -07:00
if ( opts . debug ) notify ( 'checking for change in public ip' , timestamp ( ) )
2020-10-11 16:54:58 -07:00
if ( curr !== saved ) {
2020-10-13 08:19:38 -07:00
let msg = ` ${ timestamp ( ) } Network: ${ opts . network } , a new public ip has been detected for ${ rname } from ${ saved } to ${ curr } `
2020-10-11 16:54:58 -07:00
console . log ( msg )
notify ( msg )
2020-10-13 08:19:38 -07:00
const res = JSON . stringify ( await route53 . updateZoneRecordValue ( rname , curr ) , null , 3 )
console . log ( res )
notify ( res )
const fail = setTimeout ( ( ) => {
clearInterval ( check )
msg = ` ${ timestamp ( ) } , ${ opts . network } : FATAL the pending change of ip address was not realized in ${ timeout * ( opts . debug ? 10 : 60 ) } seconds. Human attention required `
console . log ( msg )
notify ( msg )
} , timeout * 1000 * ( opts . debug ? 10 : 60 ) )
2020-10-11 16:54:58 -07:00
const check = setInterval ( async ( ) => {
if ( curr === ( await lookup ( rname ) ) . address ) {
msg = ` Update Succeeded, lookup of ${ rname } is now ${ curr } `
clearInterval ( check )
2020-10-13 08:19:38 -07:00
clearTimeout ( fail )
console . log ( msg )
notify ( msg )
} else {
msg = ` Update Pending, will check again in ${ interval } seconds `
if ( verbose || opts . debug ) {
notify ( msg )
console . log ( msg )
}
}
2020-10-11 16:54:58 -07:00
}
2020-10-13 08:19:38 -07:00
, interval * 1000 )
2020-10-11 16:54:58 -07:00
} else console . log ( 'Public IP has not changed, nothing to do' , saved )
}
2020-10-13 08:19:38 -07:00
function timestamp ( ) {
return new Date ( ) . toLocaleString ( ) . replace ( 'T' , ' ' ) . substring ( 0 , 19 )
}