-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve cache update reliability and monitoring
- Add retry mechanism and rate limit handling to GitHub API calls - Create monitoring system for cache health - Add detailed logging and status tracking - Implement safer cache building process - Add status endpoint for monitoring
- Loading branch information
1 parent
65ee8f2
commit e0456a4
Showing
5 changed files
with
207 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,91 @@ | ||
import { exec } from 'child_process'; | ||
import { promisify } from 'util'; | ||
|
||
const execAsync = promisify(exec); | ||
import { fetchBotActivities } from '../scripts/src/github-api'; | ||
|
||
export default async function handler(req, res) { | ||
console.log('Cron job started:', new Date().toISOString()); | ||
|
||
try { | ||
if (req.method === 'POST') { | ||
const { authorization } = req.headers; | ||
if (req.method !== 'POST') { | ||
console.log('Invalid method:', req.method); | ||
return res.status(405).json({ error: 'Method not allowed' }); | ||
} | ||
|
||
const { authorization } = req.headers; | ||
if (authorization !== `Bearer ${process.env.CRON_SECRET}`) { | ||
console.warn('Unauthorized access attempt'); | ||
return res.status(401).json({ error: 'Unauthorized' }); | ||
} | ||
|
||
if (authorization !== `Bearer ${process.env.CRON_SECRET}`) { | ||
return res.status(401).json({ error: 'Unauthorized' }); | ||
} | ||
console.log('Fetching bot activities...'); | ||
const activities = await fetchBotActivities(); | ||
|
||
// Store the cache in public/cache directory | ||
const cacheData = { | ||
activities, | ||
lastUpdated: new Date().toISOString() | ||
}; | ||
|
||
await execAsync('npm run build:cache'); | ||
return res.status(200).json({ success: true }); | ||
// Write to a status file to track last successful update | ||
const statusData = { | ||
lastSuccessfulUpdate: new Date().toISOString(), | ||
activitiesCount: activities.length, | ||
status: 'success' | ||
}; | ||
|
||
try { | ||
const { writeFile } = await import('fs/promises'); | ||
const { join } = await import('path'); | ||
|
||
// Ensure directories exist | ||
const { mkdir } = await import('fs/promises'); | ||
await mkdir('public/cache', { recursive: true }); | ||
|
||
// Write cache and status files | ||
await writeFile( | ||
join('public/cache/bot-activities.json'), | ||
JSON.stringify(cacheData, null, 2) | ||
); | ||
|
||
await writeFile( | ||
join('public/cache/status.json'), | ||
JSON.stringify(statusData, null, 2) | ||
); | ||
|
||
console.log('Cache files written successfully'); | ||
} catch (fsError) { | ||
console.error('Failed to write cache files:', fsError); | ||
throw new Error('Failed to write cache files: ' + fsError.message); | ||
} | ||
|
||
return res.status(405).json({ error: 'Method not allowed' }); | ||
console.log('Cron job completed successfully'); | ||
return res.status(200).json({ | ||
success: true, | ||
timestamp: new Date().toISOString(), | ||
activitiesCount: activities.length | ||
}); | ||
} catch (error) { | ||
return res.status(500).json({ error: error.message }); | ||
console.error('Cron job failed:', error); | ||
|
||
// Write error status | ||
try { | ||
const { writeFile } = await import('fs/promises'); | ||
const { join } = await import('path'); | ||
|
||
await writeFile( | ||
join('public/cache/status.json'), | ||
JSON.stringify({ | ||
lastAttempt: new Date().toISOString(), | ||
status: 'error', | ||
error: error.message | ||
}, null, 2) | ||
); | ||
} catch (fsError) { | ||
console.error('Failed to write error status:', fsError); | ||
} | ||
|
||
return res.status(500).json({ | ||
error: error.message, | ||
timestamp: new Date().toISOString(), | ||
stack: process.env.NODE_ENV === 'development' ? error.stack : undefined | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
export default async function handler(req, res) { | ||
if (req.method !== 'GET') { | ||
return res.status(405).json({ error: 'Method not allowed' }); | ||
} | ||
|
||
try { | ||
const { readFile } = await import('fs/promises'); | ||
const { join } = await import('path'); | ||
|
||
const statusPath = join('public/cache/status.json'); | ||
const status = JSON.parse(await readFile(statusPath, 'utf8')); | ||
|
||
return res.status(200).json(status); | ||
} catch (error) { | ||
console.error('Failed to read status:', error); | ||
return res.status(500).json({ | ||
error: 'Failed to read cache status', | ||
details: error.message | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
interface CacheStatus { | ||
lastSuccessfulUpdate?: string; | ||
lastAttempt?: string; | ||
status: 'success' | 'error'; | ||
activitiesCount?: number; | ||
error?: string; | ||
} | ||
|
||
async function checkCacheStatus(): Promise<void> { | ||
try { | ||
const fs = await import('fs/promises'); | ||
const path = await import('path'); | ||
|
||
const statusPath = path.join(process.cwd(), 'public/cache/status.json'); | ||
const cacheDataPath = path.join(process.cwd(), 'public/cache/bot-activities.json'); | ||
|
||
// Read status file | ||
const statusData = JSON.parse(await fs.readFile(statusPath, 'utf8')) as CacheStatus; | ||
|
||
// Read cache file | ||
const cacheData = JSON.parse(await fs.readFile(cacheDataPath, 'utf8')); | ||
|
||
// Check cache freshness | ||
const lastUpdate = new Date(statusData.lastSuccessfulUpdate || 0); | ||
const now = new Date(); | ||
const hoursSinceUpdate = (now.getTime() - lastUpdate.getTime()) / (1000 * 60 * 60); | ||
|
||
console.log('Cache Status Report:'); | ||
console.log('-------------------'); | ||
console.log(`Status: ${statusData.status}`); | ||
console.log(`Last Successful Update: ${statusData.lastSuccessfulUpdate || 'Never'}`); | ||
console.log(`Hours Since Last Update: ${hoursSinceUpdate.toFixed(2)}`); | ||
console.log(`Activities in Cache: ${cacheData.activities?.length || 0}`); | ||
|
||
if (statusData.error) { | ||
console.error('Last Error:', statusData.error); | ||
} | ||
|
||
// Alert if cache is stale | ||
if (hoursSinceUpdate > 12) { | ||
throw new Error(`Cache is stale! Last update was ${hoursSinceUpdate.toFixed(2)} hours ago`); | ||
} | ||
|
||
// Alert if no activities | ||
if (!cacheData.activities?.length) { | ||
throw new Error('Cache contains no activities!'); | ||
} | ||
|
||
console.log('\nCache status is healthy ✓'); | ||
|
||
} catch (error) { | ||
console.error('\nCache Health Check Failed!'); | ||
console.error(error instanceof Error ? error.message : String(error)); | ||
process.exit(1); | ||
} | ||
} | ||
|
||
// Run the check if this file is being run directly | ||
if (require.main === module) { | ||
checkCacheStatus(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters