@@ -4,6 +4,7 @@ import NodeCache from 'node-cache'
44import { gql , request } from 'graphql-request'
55import { EmbedBuilder , WebhookClient } from 'discord.js'
66import { client } from '../main.js'
7+ import axios from 'axios'
78
89interface ApiResponse {
910 activities : wikiActivities [ ]
@@ -21,17 +22,19 @@ export default class WikiUpdates {
2122 DEV_CHANNEL_ID : string
2223 PROD_CHANNEL_ID : string
2324 REVALIDATE_SECRET : string
25+ PREDIQT_API_URL : string
2426 DEV_WIKI_WEBHOOK : string
2527 PROD_ALARMS_WEBHOOK : string
2628
2729 private apiHealthStatus = new Map <
28- ChannelTypes ,
30+ ChannelTypes | 'PREDIQT' ,
2931 { isHealthy : boolean ; lastCheck : number ; alertSent : boolean }
3032 > ( )
3133
3234 constructor ( ) {
3335 this . DEV_API_URL = process . env . DEV_API_URL
3436 this . PROD_API_URL = process . env . PROD_API_URL
37+ this . PREDIQT_API_URL = process . env . PREDIQT_API_URL
3538 this . CHANNEL_IDS = JSON . parse ( process . env . CHANNELS )
3639 this . DEV_CHANNEL_ID = this . CHANNEL_IDS . DEV . WIKI
3740 this . PROD_CHANNEL_ID = this . CHANNEL_IDS . PROD . WIKI
@@ -337,6 +340,48 @@ export default class WikiUpdates {
337340 return false
338341 }
339342
343+ async checkPrediqtApiHealth ( ) : Promise < boolean > {
344+ const maxRetries = 3
345+ const retryDelay = 10000
346+
347+ let lastError : any
348+
349+ for ( let attempt = 1 ; attempt <= maxRetries ; attempt ++ ) {
350+ try {
351+ await axios . get ( this . PREDIQT_API_URL , { timeout : 30000 } )
352+
353+ this . apiHealthStatus . set ( 'PREDIQT' , {
354+ isHealthy : true ,
355+ lastCheck : Date . now ( ) ,
356+ alertSent : false ,
357+ } )
358+
359+ if ( attempt > 1 ) {
360+ console . log ( `✅ PrediQT API health check succeeded on attempt ${ attempt } /${ maxRetries } ` )
361+ }
362+
363+ return true
364+ } catch ( error ) {
365+ lastError = error
366+ console . error ( `❌ PrediQT API Health Check Failed (attempt ${ attempt } /${ maxRetries } ):` , error )
367+
368+ if ( attempt < maxRetries ) {
369+ console . log ( `⏳ Retrying PrediQT API health check in 10 seconds...` )
370+ await this . sleep ( retryDelay )
371+ }
372+ }
373+ }
374+
375+ const currentStatus = this . apiHealthStatus . get ( 'PREDIQT' )
376+ this . apiHealthStatus . set ( 'PREDIQT' , {
377+ isHealthy : false ,
378+ lastCheck : Date . now ( ) ,
379+ alertSent : currentStatus ?. alertSent || false ,
380+ } )
381+ console . error ( `❌ PrediQT API Health Check Failed after ${ maxRetries } attempts` )
382+ return false
383+ }
384+
340385 async startApiHealthMonitoring ( ) : Promise < void > {
341386 const checkInterval = 120000 // 2 minutes
342387
@@ -352,10 +397,68 @@ export default class WikiUpdates {
352397 lastCheck : 0 ,
353398 alertSent : false ,
354399 } )
400+ this . apiHealthStatus . set ( 'PREDIQT' , {
401+ isHealthy : true ,
402+ lastCheck : 0 ,
403+ alertSent : false ,
404+ } )
355405
356406 setInterval ( async ( ) => {
357407 console . log ( '🏥 Running API health checks...' )
358408
409+ // Check PrediQT API health
410+ {
411+ const previousStatus = this . apiHealthStatus . get ( 'PREDIQT' )
412+ const isHealthy = await this . checkPrediqtApiHealth ( )
413+ const currentStatus = this . apiHealthStatus . get ( 'PREDIQT' )
414+
415+ if ( ! isHealthy ) {
416+ console . warn (
417+ `⚠️ PrediQT API is unresponsive at ${ new Date ( ) . toISOString ( ) } ` ,
418+ )
419+
420+ if ( ! currentStatus ?. alertSent ) {
421+ console . log ( `🚨 Sending initial error alert for PrediQT` )
422+ await this . notifyError (
423+ 1 ,
424+ ChannelTypes . PROD ,
425+ this . PREDIQT_API_URL ,
426+ 'HEALTH_CHECK_FAILED' ,
427+ )
428+
429+ this . apiHealthStatus . set ( 'PREDIQT' , {
430+ isHealthy : false ,
431+ lastCheck : Date . now ( ) ,
432+ alertSent : true ,
433+ } )
434+ } else {
435+ console . log ( `⏳ PrediQT API still down, continuing to monitor silently...` )
436+ }
437+ } else {
438+ console . log (
439+ `✅ PrediQT API is healthy at ${ new Date ( ) . toISOString ( ) } ` ,
440+ )
441+
442+ if ( previousStatus && ! previousStatus . isHealthy && previousStatus . alertSent ) {
443+ console . log ( `🎉 PrediQT API has recovered!` )
444+ const webhookUrl = this . PROD_ALARMS_WEBHOOK
445+ if ( webhookUrl ) {
446+ const webhook = new WebhookClient ( { url : webhookUrl } )
447+ await webhook . send (
448+ `✅ **RECOVERY** - PrediQT API is back online! 🎉` ,
449+ )
450+ webhook . destroy ( )
451+ }
452+
453+ this . apiHealthStatus . set ( 'PREDIQT' , {
454+ isHealthy : true ,
455+ lastCheck : Date . now ( ) ,
456+ alertSent : false ,
457+ } )
458+ }
459+ }
460+ }
461+
359462 for ( const channelType of [ ChannelTypes . DEV , ChannelTypes . PROD ] ) {
360463 const previousStatus = this . apiHealthStatus . get ( channelType )
361464 const isHealthy = await this . checkApiHealth ( channelType )
@@ -442,7 +545,7 @@ export default class WikiUpdates {
442545 }
443546
444547 getApiHealthStatus ( ) : Map <
445- ChannelTypes ,
548+ ChannelTypes | 'PREDIQT' ,
446549 { isHealthy : boolean ; lastCheck : number ; alertSent : boolean }
447550 > {
448551 return new Map ( this . apiHealthStatus )
0 commit comments