Skip to content

Commit 5f2c871

Browse files
committed
Adds health check of prediqt
1 parent be29964 commit 5f2c871

5 files changed

Lines changed: 111 additions & 5 deletions

File tree

.env.sample

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ PROD_API_URL=
88
DEV_URL=
99
DEV_API_URL=
1010

11+
PREDIQT_API_URL=
12+
1113
META_URL=
1214

1315
IQ_ADDRESS=

global.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ declare namespace NodeJS {
1717
TWITTER_API_SECRET: string
1818
TWITTER_ACCESS_TOKEN: string
1919
TWITTER_ACCESS_SECRET: string
20+
PREDIQT_API_URL: string
2021
}
2122
}

src/events/common.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ export class AppDiscord {
206206

207207
const [extractedProdLinks, extractedDevLinks] = await Promise.all([
208208
this.revalidate.extractLinks(this.PROD_URL),
209-
this.revalidate.extractLinks(this.DEV_API_URL),
209+
this.revalidate.extractLinks(this.DEV_URL),
210210
])
211211

212212
await Promise.all([

src/services/revalidate.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export default class RevalidateService {
1111
}
1212

1313
url(path: string) {
14-
return path.replace('/wiki/', '/api/')
14+
return path.replace('/wiki/', '/')
1515
}
1616

1717
async revalidateWikiPage(path: string, id?: string) {
@@ -37,7 +37,7 @@ export default class RevalidateService {
3737
async extractLinks(link: string) {
3838
let wikis
3939
try {
40-
const url = `${this.url(link)}sitemap.xml`
40+
const url = `${this.url(link)}/sitemap.xml`
4141

4242
const res = await axios.get(url, {
4343
responseType: 'text',

src/services/wikiUpdates.ts

Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import NodeCache from 'node-cache'
44
import { gql, request } from 'graphql-request'
55
import { EmbedBuilder, WebhookClient } from 'discord.js'
66
import { client } from '../main.js'
7+
import axios from 'axios'
78

89
interface 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

Comments
 (0)