Project Status improvements (#29669)

* add logs links, set status as coming up when project is less than 10 minutes old

* make whole dorpdown item clickable

* Small tweak to UI

* Switch to 5 mins

* Small tweak

* Add useeffect to automatically refresh service status

* Add useeffect to automatically refresh service status

* Address feedback

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
Jordi EnricandJoshen Lim authored and GitHub committed 2024-10-07 16:41:51 +08:00
1 parent 0d5715c00d
commit 6e1e5db7f7
2 files changed
+113 -35

No files matched your search

@@ -1,19 +1,18 @@
import { CheckCircle2, ChevronRight, Loader2 } from 'lucide-react'
import Link from 'next/link'
import { useState } from 'react'
import { useParams } from 'common'
import { useProjectLintsQuery } from 'data/lint/lint-query'
import {
Button,
PopoverContent_Shadcn_,
PopoverSeparator_Shadcn_,
PopoverTrigger_Shadcn_,
Popover_Shadcn_,
PopoverSeparator_Shadcn_,
cn,
} from 'ui'
import { useProjectLintsQuery } from 'data/lint/lint-query'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
import Link from 'next/link'
import { LINTER_LEVELS, LINT_TABS } from '../Linter/Linter.constants'
import { useParams } from 'common'
export const SecurityStatus = () => {
const { ref } = useParams()
@@ -1,5 +1,7 @@
import { AlertTriangle, CheckCircle2, Loader2 } from 'lucide-react'
import { useState } from 'react'
import dayjs from 'dayjs'
import { AlertTriangle, CheckCircle2, ChevronRight, Loader2 } from 'lucide-react'
import Link from 'next/link'
import { useEffect, useState } from 'react'
import { useParams } from 'common'
import { useEdgeFunctionServiceStatusQuery } from 'data/service-status/edge-functions-status-query'
@@ -9,6 +11,37 @@ import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
import { Button, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_, Popover_Shadcn_ } from 'ui'
const SERVICE_STATUS_THRESHOLD = 5 // minutes
const StatusMessage = ({
isLoading,
isSuccess,
isProjectNew,
}: {
isLoading: boolean
isSuccess: boolean
isProjectNew: boolean
}) => {
if (isLoading) return 'Checking status'
if (isProjectNew) return 'Coming up...'
if (isSuccess) return 'No issues'
return 'Unable to connect'
}
const StatusIcon = ({
isLoading,
isSuccess,
isProjectNew,
}: {
isLoading: boolean
isSuccess: boolean
isProjectNew: boolean
}) => {
if (isLoading || isProjectNew) return <Loader2 size={14} className="animate-spin" />
if (isSuccess) return <CheckCircle2 className="text-brand" size={18} strokeWidth={1.5} />
return <AlertTriangle className="text-warning" size={18} strokeWidth={1.5} />
}
const ServiceStatus = () => {
const { ref } = useParams()
const project = useSelectedProject()
@@ -29,13 +62,21 @@ const ServiceStatus = () => {
const isBranch = project?.parentRef !== project?.ref
// [Joshen] Need pooler service check eventually
const { data: status, isLoading } = useProjectServiceStatusQuery({ projectRef: ref })
const { data: edgeFunctionsStatus } = useEdgeFunctionServiceStatusQuery({ projectRef: ref })
const { isLoading: isLoadingPostgres, isSuccess: isSuccessPostgres } =
usePostgresServiceStatusQuery({
projectRef: ref,
connectionString: project?.connectionString,
})
const {
data: status,
isLoading,
refetch: refetchServiceStatus,
} = useProjectServiceStatusQuery({ projectRef: ref })
const { data: edgeFunctionsStatus, refetch: refetchEdgeFunctionServiceStatus } =
useEdgeFunctionServiceStatusQuery({ projectRef: ref })
const {
isLoading: isLoadingPostgres,
isSuccess: isSuccessPostgres,
refetch: refetchPostgresServiceStatus,
} = usePostgresServiceStatusQuery({
projectRef: ref,
connectionString: project?.connectionString,
})
const authStatus = status?.find((service) => service.name === 'auth')
const restStatus = status?.find((service) => service.name === 'rest')
@@ -49,6 +90,7 @@ const ServiceStatus = () => {
docsUrl?: string
isLoading: boolean
isSuccess?: boolean
logsUrl: string
}[] = [
{
name: 'Database',
@@ -56,6 +98,7 @@ const ServiceStatus = () => {
docsUrl: undefined,
isLoading: isLoadingPostgres,
isSuccess: isSuccessPostgres,
logsUrl: '/logs/postgres-logs',
},
{
name: 'PostgREST',
@@ -63,6 +106,7 @@ const ServiceStatus = () => {
docsUrl: undefined,
isLoading,
isSuccess: restStatus?.healthy,
logsUrl: '/logs/postgrest-logs',
},
...(authEnabled
? [
@@ -72,6 +116,7 @@ const ServiceStatus = () => {
docsUrl: undefined,
isLoading,
isSuccess: authStatus?.healthy,
logsUrl: '/logs/auth-logs',
},
]
: []),
@@ -83,6 +128,7 @@ const ServiceStatus = () => {
docsUrl: undefined,
isLoading,
isSuccess: realtimeStatus?.healthy,
logsUrl: '/logs/realtime-logs',
},
]
: []),
@@ -94,6 +140,7 @@ const ServiceStatus = () => {
docsUrl: undefined,
isLoading,
isSuccess: storageStatus?.healthy,
logsUrl: '/logs/storage-logs',
},
]
: []),
@@ -105,6 +152,7 @@ const ServiceStatus = () => {
docsUrl: 'https://supabase.com/docs/guides/functions/troubleshooting',
isLoading,
isSuccess: edgeFunctionsStatus?.healthy,
logsUrl: '/logs/edge-functions-logs',
},
]
: []),
@@ -113,13 +161,39 @@ const ServiceStatus = () => {
const isLoadingChecks = services.some((service) => service.isLoading)
const allServicesOperational = services.every((service) => service.isSuccess)
// If the project is less than 5 minutes old, and status is not operational, then it's likely the service is still starting up
const isProjectNew =
dayjs.utc().diff(dayjs.utc(project?.inserted_at), 'minute') < SERVICE_STATUS_THRESHOLD
useEffect(() => {
let timer: any
if (isProjectNew) {
const secondsSinceProjectCreated = dayjs
.utc()
.diff(dayjs.utc(project?.inserted_at), 'seconds')
const remainingTimeTillNextCheck = SERVICE_STATUS_THRESHOLD * 60 - secondsSinceProjectCreated
timer = setTimeout(() => {
refetchServiceStatus()
refetchPostgresServiceStatus()
refetchEdgeFunctionServiceStatus()
}, remainingTimeTillNextCheck * 1000)
}
return () => {
clearTimeout(timer)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isProjectNew])
return (
<Popover_Shadcn_ modal={false} open={open} onOpenChange={setOpen}>
<PopoverTrigger_Shadcn_ asChild>
<Button
type="default"
icon={
isLoadingChecks ? (
isLoadingChecks || isProjectNew ? (
<Loader2 className="animate-spin" />
) : (
<div
@@ -135,28 +209,33 @@ const ServiceStatus = () => {
</PopoverTrigger_Shadcn_>
<PopoverContent_Shadcn_ className="p-0 w-56" side="bottom" align="center">
{services.map((service) => (
<div
<Link
href={`/project/${ref}${service.logsUrl}`}
key={service.name}
className="px-4 py-2 text-xs flex items-center justify-between border-b last:border-none"
className="transition px-3 py-2 text-xs flex items-center justify-between border-b last:border-none group relative hover:bg-surface-300"
>
<div>
<p>{service.name}</p>
<p className="text-foreground-light">
{service.isLoading
? 'Checking status'
: service.isSuccess
? 'No issues'
: 'Unable to connect'}
</p>
<div className="flex gap-x-2">
<StatusIcon
isLoading={service.isLoading}
isSuccess={!!service.isSuccess}
isProjectNew={isProjectNew}
/>
<div className="flex-1">
<p>{service.name}</p>
<p className="text-foreground-light flex items-center gap-1">
<StatusMessage
isLoading={service.isLoading}
isSuccess={!!service.isSuccess}
isProjectNew={isProjectNew}
/>
</p>
</div>
</div>
{service.isLoading ? (
<Loader2 className="animate-spin text-foreground-light" size={18} />
) : service.isSuccess ? (
<CheckCircle2 className="text-brand" size={18} strokeWidth={1.5} />
) : (
<AlertTriangle className="text-warning" size={18} strokeWidth={1.5} />
)}
</div>
<div className="flex items-center gap-x-1 transition opacity-0 group-hover:opacity-100">
<span className="text-xs text-foreground">View logs</span>
<ChevronRight size={14} className="text-foreground" />
</div>
</Link>
))}
</PopoverContent_Shadcn_>
</Popover_Shadcn_>