mirror of
https://github.com/supabase/supabase.git
synced 2026-05-06 22:18:00 +08:00
feat: Add a banner for clock skew in studio (#32866)
* Add a ClockSkewBanner on top of studio. * Fix the color of the RestrictionBanner. * Add an API endpoint which return the current UTC time on the server. * Fix the glob pattern for docs in turbo.json. * Force docs to build. * Revert unneeded changes. * Add feature flag for clock skew banner. * Add a comment.
This commit is contained in:
@@ -2,6 +2,7 @@ import { useMonaco } from '@monaco-editor/react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { PropsWithChildren, useMemo } from 'react'
|
||||
|
||||
import { ClockSkewBanner } from 'components/layouts/AppLayout/ClockSkewBanner'
|
||||
import IncidentBanner from 'components/layouts/AppLayout/IncidentBanner'
|
||||
import { NoticeBanner } from 'components/layouts/AppLayout/NoticeBanner'
|
||||
import { RestrictionBanner } from 'components/layouts/AppLayout/RestrictionBanner'
|
||||
@@ -16,6 +17,7 @@ const AppBannerWrapper = ({ children }: PropsWithChildren<{}>) => {
|
||||
|
||||
const ongoingIncident = useFlag('ongoingIncident')
|
||||
const showNoticeBanner = useFlag('showNoticeBanner')
|
||||
const clockSkewBanner = useFlag('clockSkewBanner')
|
||||
|
||||
// Define the supabase theme for Monaco before anything is rendered. Using useEffect would sometime load the theme
|
||||
// after the editor was loaded, so it looked off. useMemo will always be run before rendering
|
||||
@@ -32,6 +34,7 @@ const AppBannerWrapper = ({ children }: PropsWithChildren<{}>) => {
|
||||
{ongoingIncident && <IncidentBanner />}
|
||||
{showNoticeBanner && <NoticeBanner />}
|
||||
{profile !== undefined && <RestrictionBanner />}
|
||||
{clockSkewBanner && <ClockSkewBanner />}
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
58
apps/studio/components/layouts/AppLayout/ClockSkewBanner.tsx
Normal file
58
apps/studio/components/layouts/AppLayout/ClockSkewBanner.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import { BASE_PATH } from 'lib/constants'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { Button } from 'ui'
|
||||
|
||||
// Show the banner if the clock skew is greater than 2 minutes
|
||||
const CLOCK_SKEW_THRESHOLD = 2 * 60 * 1000
|
||||
// check every 5 minutes
|
||||
const CLOCK_SKEW_CHECK_INTERVAL = 30 * 60 * 1000
|
||||
|
||||
const isClockSkewed = async () => {
|
||||
try {
|
||||
const response = await fetch(`${BASE_PATH}/api/get-utc-time`)
|
||||
const data = await response.json()
|
||||
// The received time is in UTC timezone, add Z at the end to make JS understand that
|
||||
const serverTime = new Date(data.utcTime).getTime()
|
||||
const clientTime = new Date().getTime()
|
||||
const clockSkew = Math.abs(clientTime - serverTime)
|
||||
|
||||
return clockSkew > CLOCK_SKEW_THRESHOLD
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export const ClockSkewBanner = () => {
|
||||
const [clockSkew, setClockSkew] = useState(false)
|
||||
|
||||
const checkClockSkew = useCallback(async () => {
|
||||
const value = await isClockSkewed()
|
||||
setClockSkew(value)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
// check for clock skew every CLOCK_SKEW_CHECK_INTERVAL
|
||||
checkClockSkew()
|
||||
const interval = setInterval(checkClockSkew, CLOCK_SKEW_CHECK_INTERVAL)
|
||||
|
||||
return () => clearInterval(interval)
|
||||
}, [checkClockSkew])
|
||||
|
||||
if (!clockSkew) return null
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{ height: '44px' }}
|
||||
className="flex items-center justify-center gap-x-4 bg-destructive-400 py-3 transition text-foreground box-border border-b border-default"
|
||||
>
|
||||
<p className="text-sm">
|
||||
Your computer's clock appears to be inaccurate. This can cause issues with certain features.
|
||||
</p>
|
||||
<Button asChild type="default">
|
||||
<a href="https://supabase.com/docs" target="_blank">
|
||||
More information
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import Link from 'next/link'
|
||||
|
||||
import { useOrganizationsQuery } from 'data/organizations/organizations-query'
|
||||
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
|
||||
import { AlertTitle_Shadcn_, Alert_Shadcn_, Button, CriticalIcon, WarningIcon } from 'ui'
|
||||
import { AlertTitle_Shadcn_, Alert_Shadcn_, Button, CriticalIcon, WarningIcon, cn } from 'ui'
|
||||
|
||||
/**
|
||||
* Shown on projects in organization which are above their qouta
|
||||
@@ -18,7 +18,10 @@ export const RestrictionBanner = () => {
|
||||
return (
|
||||
<Alert_Shadcn_
|
||||
variant={currentOrg.restriction_status === 'restricted' ? 'destructive' : 'warning'}
|
||||
className="rounded-none border-l-0 border-r-0 h-[44px] p-0 flex items-center justify-center"
|
||||
className={cn(
|
||||
'rounded-none border-0 h-[44px] p-0 flex items-center justify-center',
|
||||
currentOrg.restriction_status === 'restricted' ? 'bg-destructive-400' : 'bg-warning-500'
|
||||
)}
|
||||
>
|
||||
<AlertTitle_Shadcn_ className="flex items-center gap-x-4">
|
||||
{currentOrg.restriction_status === 'restricted' ? <CriticalIcon /> : <WarningIcon />}
|
||||
|
||||
@@ -17,10 +17,10 @@ const HOSTED_SUPPORTED_API_URLS = [
|
||||
'/ai/sql/cron',
|
||||
'/ai/docs',
|
||||
'/get-ip-address',
|
||||
'/get-utc-time',
|
||||
]
|
||||
|
||||
export function middleware(request: NextRequest) {
|
||||
const url = request.url
|
||||
if (IS_PLATFORM && !HOSTED_SUPPORTED_API_URLS.some((url) => request.url.endsWith(url))) {
|
||||
return Response.json(
|
||||
{ success: false, message: 'Endpoint not supported on hosted' },
|
||||
|
||||
11
apps/studio/pages/api/get-utc-time.ts
Normal file
11
apps/studio/pages/api/get-utc-time.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
// Returns the current UTC time in ISO format. Used to check if the client and server time are skewed. Clock skew causes
|
||||
// issues with JWT verification.
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const utcTime = new Date().toISOString()
|
||||
|
||||
return res.status(200).json({ utcTime })
|
||||
}
|
||||
|
||||
export default handler
|
||||
Reference in New Issue
Block a user