mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 01:45:10 +03:00
## Summary * Adds `/api/status-page` (Next route + TanStack wrapper), backed by the [incident.io](<http://incident.io>) Widget API, annotating each item with `visible`, `show_banner`, and (for scheduled maintenances) `banner_lead_days`. * Deployment-mode visibility is driven by a new `status_page:visibility_field_ids` custom-content key. * Widget array parsing is fault-tolerant: a malformed item in one array is dropped and logged rather than failing the whole response, so one bad item can't hide a real ongoing incident. * 429s from [incident.io](<http://incident.io>) are retried with equal-jitter exponential backoff, respecting `Retry-After`, up to 2 retries. * Nothing consumes this endpoint yet — it replaces no existing behavior and changes nothing user-visible. Later PRs (this is PR 1 of a stack) wire up consumers behind the `incidentIoStatusPage` ConfigCat flag. Part of [FE-4057](https://linear.app/supabase/issue/FE-4057/frontend-bannerbot-reconfigured) — see Linear for full design context. ## Test plan - [X] `pnpm --filter studio run typecheck` - [X] `pnpm --filter studio run lint:ratchet` - [X] `pnpm knip --workspace apps/studio` - [X] `pnpm test:prettier` - [X] `pnpm --filter studio exec vitest run status-page` — 44 tests passing, including a regression test built from a real production [incident.io](<http://incident.io>) payload that initially failed to parse, and a compile-time type-safety regression test for the array-parsing helper Co-authored-by: Claude Code [charis@supabase.io](<mailto:charis@supabase.io>) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a status page that displays ongoing incidents and maintenance, with visibility and banner settings based on linked incident details. * Status page data is available through a new API endpoint, with caching for successful responses and degraded results. * **Bug Fixes** * Status page data can still display when some linked incident details are unavailable; affected results are marked as degraded. * Improved handling of invalid widget entries so they don’t prevent valid items from being processed. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Code <charis@supabase.io>
66 lines
2.0 KiB
TypeScript
66 lines
2.0 KiB
TypeScript
import { IS_PLATFORM } from 'common'
|
|
import { NextResponse } from 'next/server'
|
|
|
|
import { InternalServerError } from '@/lib/api/apiHelpers'
|
|
import { getStatusPage } from '@/lib/api/status-page'
|
|
|
|
const CACHE_CONTROL_SUCCESS = 'public, s-maxage=300, stale-while-revalidate=300'
|
|
const CACHE_CONTROL_DEGRADED = 'public, s-maxage=60, stale-while-revalidate=60'
|
|
const CACHE_CONTROL_ERROR = 'no-store'
|
|
|
|
export async function OPTIONS() {
|
|
if (!IS_PLATFORM) return new Response(null, { status: 404 })
|
|
return new Response(null, {
|
|
status: 204,
|
|
headers: { Allow: 'GET, HEAD, OPTIONS' },
|
|
})
|
|
}
|
|
|
|
export async function HEAD() {
|
|
if (!IS_PLATFORM) return new Response(null, { status: 404 })
|
|
return new Response(null, {
|
|
status: 200,
|
|
headers: { 'Cache-Control': CACHE_CONTROL_SUCCESS },
|
|
})
|
|
}
|
|
|
|
export async function GET() {
|
|
if (!IS_PLATFORM) return new Response(null, { status: 404 })
|
|
|
|
try {
|
|
const { data, isDegraded } = await getStatusPage()
|
|
return NextResponse.json(data, {
|
|
headers: { 'Cache-Control': isDegraded ? CACHE_CONTROL_DEGRADED : CACHE_CONTROL_SUCCESS },
|
|
})
|
|
} catch (error) {
|
|
let errorCode = 500
|
|
const headers = new Headers({ 'Cache-Control': CACHE_CONTROL_ERROR })
|
|
|
|
if (error instanceof InternalServerError) {
|
|
if (typeof error.details?.status === 'number') errorCode = error.details.status
|
|
if (errorCode === 420) errorCode = 429
|
|
if (errorCode === 429 && typeof error.details?.retryAfter === 'string') {
|
|
headers.set('Retry-After', error.details.retryAfter)
|
|
}
|
|
console.error(
|
|
'Failed to fetch incident.io status page: %s',
|
|
JSON.stringify(
|
|
{
|
|
message: error.message,
|
|
details: error.details,
|
|
},
|
|
null,
|
|
2
|
|
)
|
|
)
|
|
} else {
|
|
console.error('Unexpected error fetching incident.io status page:', error)
|
|
}
|
|
|
|
return NextResponse.json(
|
|
{ error: { message: 'Internal server error' } },
|
|
{ status: errorCode, headers }
|
|
)
|
|
}
|
|
}
|