route.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import { IS_PLATFORM } from 'common'
  2. import { NextResponse } from 'next/server'
  3. import { InternalServerError } from '@/lib/api/apiHelpers'
  4. import { getActiveIncidents, type IncidentCache } from '@/lib/api/incident-status'
  5. import { createAdminClient } from '@/lib/api/briven-admin'
  6. /**
  7. * Cache on CDN for 5 minutes
  8. * Allow serving stale content for 1 minute while revalidating
  9. */
  10. const CACHE_CONTROL_SETTINGS = 'public, s-maxage=300, stale-while-revalidate=60'
  11. async function fetchIncidentCache(incidentIds: Array<string>): Promise<Map<string, IncidentCache>> {
  12. const cacheMap = new Map<string, IncidentCache>()
  13. if (incidentIds.length === 0) return cacheMap
  14. const briven = createAdminClient()
  15. try {
  16. const { data, error } = await briven
  17. .from('incident_status_cache')
  18. .select('incident_id, affected_regions, affects_project_creation')
  19. .in('incident_id', incidentIds)
  20. if (error) {
  21. console.error('Failed to fetch incident_status_cache: %O', error)
  22. return cacheMap
  23. }
  24. for (const row of data ?? []) {
  25. cacheMap.set(row.incident_id, {
  26. affected_regions: row.affected_regions ?? null,
  27. affects_project_creation: row.affects_project_creation,
  28. })
  29. }
  30. } catch (error) {
  31. console.error('Unexpected error fetching incident_status_cache: %O', error)
  32. }
  33. return cacheMap
  34. }
  35. export async function OPTIONS() {
  36. if (!IS_PLATFORM) return new Response(null, { status: 404 })
  37. return new Response(null, {
  38. status: 204,
  39. headers: {
  40. Allow: 'GET, HEAD, OPTIONS',
  41. },
  42. })
  43. }
  44. export async function HEAD() {
  45. if (!IS_PLATFORM) return new Response(null, { status: 404 })
  46. return new Response(null, {
  47. status: 200,
  48. headers: { 'Cache-Control': CACHE_CONTROL_SETTINGS },
  49. })
  50. }
  51. export async function GET() {
  52. if (!IS_PLATFORM) return new Response(null, { status: 404 })
  53. try {
  54. const allIncidents = await getActiveIncidents()
  55. const bannerIncidents = allIncidents.filter(
  56. (incident) =>
  57. incident.impact !== 'maintenance' &&
  58. incident.metadata?.dashboard_metadata?.show_banner === true
  59. )
  60. const cacheMap = await fetchIncidentCache(bannerIncidents.map((i) => i.id))
  61. const enrichedIncidents = bannerIncidents.map((incident) => ({
  62. ...incident,
  63. cache: cacheMap.get(incident.id) ?? null,
  64. }))
  65. return NextResponse.json(enrichedIncidents, {
  66. headers: { 'Cache-Control': CACHE_CONTROL_SETTINGS },
  67. })
  68. } catch (error) {
  69. let errorCode = 500
  70. const headers = new Headers()
  71. if (error instanceof InternalServerError) {
  72. if (typeof error.details?.status === 'number') errorCode = error.details.status
  73. if (errorCode === 420) errorCode = 429
  74. if (errorCode === 429 && typeof error.details?.retryAfter === 'string') {
  75. headers.set('Retry-After', error.details.retryAfter)
  76. }
  77. console.error('Failed to fetch active StatusPage incidents: %O', {
  78. message: error.message,
  79. details: error.details,
  80. })
  81. } else {
  82. console.error('Unexpected error fetching active StatusPage incidents: %O', error)
  83. }
  84. return NextResponse.json(
  85. { error: 'Unable to fetch incidents at this time' },
  86. { status: errorCode, headers }
  87. )
  88. }
  89. }