diff --git a/apps/studio/components/interfaces/Home/ServiceStatus.tsx b/apps/studio/components/interfaces/Home/ServiceStatus.tsx
index a2e768ac0fe..69a57ca1a8d 100644
--- a/apps/studio/components/interfaces/Home/ServiceStatus.tsx
+++ b/apps/studio/components/interfaces/Home/ServiceStatus.tsx
@@ -8,7 +8,8 @@ import { InlineLink } from 'components/ui/InlineLink'
import { useBranchesQuery } from 'data/branches/branches-query'
import { useEdgeFunctionServiceStatusQuery } from 'data/service-status/edge-functions-status-query'
import {
- ProjectServiceStatus,
+ ProjectServiceStatus as APIProjectServiceStatus,
+ ServiceHealthResponse,
useProjectServiceStatusQuery,
} from 'data/service-status/service-status-query'
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
@@ -24,22 +25,23 @@ import {
const SERVICE_STATUS_THRESHOLD = 5 // minutes
-const StatusMessage = ({
+export type ProjectServiceStatus = APIProjectServiceStatus | 'DISABLED'
+
+export const StatusMessage = ({
status,
isLoading,
- isHealthy,
isProjectNew,
}: {
isLoading: boolean
- isHealthy: boolean
isProjectNew: boolean
status?: ProjectServiceStatus
}) => {
- if (isHealthy) return 'Healthy'
if (isLoading) return 'Checking status'
+ if (status === 'DISABLED') return 'Disabled'
if (status === 'UNHEALTHY') return 'Unhealthy'
if (status === 'COMING_UP') return 'Coming up...'
if (status === 'ACTIVE_HEALTHY') return 'Healthy'
+ // isProjectNew has to be after all other statuses
if (isProjectNew) return 'Coming up...'
if (status) return status
return 'Unable to connect'
@@ -53,26 +55,36 @@ const LoaderIcon = () =>
const AlertIcon = () =>
const CheckIcon = () =>
-const StatusIcon = ({
+export const StatusIcon = ({
isLoading,
- isHealthy,
isProjectNew,
projectStatus,
}: {
isLoading: boolean
- isHealthy: boolean
isProjectNew: boolean
projectStatus?: ProjectServiceStatus
}) => {
- if (isHealthy) return
- if (isLoading) return
- if (projectStatus === 'UNHEALTHY') return
- if (projectStatus === 'COMING_UP') return
+ //
if (projectStatus === 'ACTIVE_HEALTHY') return
+ if (projectStatus === 'DISABLED') return
+ if (projectStatus === 'COMING_UP') return
+ if (isLoading) return
+ // isProjectNew has to be above UNHEALTHY because in the first few minutes, some services might be starting up and show as UNHEALTHY
if (isProjectNew) return
+ if (projectStatus === 'UNHEALTHY') return
return
}
+/*
+ * Extract the db_schema from the response.info object
+ */
+export const extractDbSchema = (response: ServiceHealthResponse | undefined) => {
+ if (response?.info && 'db_schema' in response.info) {
+ return response.info.db_schema
+ }
+ return undefined
+}
+
export const ServiceStatus = () => {
const { ref } = useParams()
const { data: project } = useSelectedProjectQuery()
@@ -126,7 +138,18 @@ export const ServiceStatus = () => {
{
refetchInterval: (query) => {
const data = query.state.data
- return data?.some((service) => !service.healthy) ? 5000 : false
+ const isServiceUnhealthy = data?.some((service) => {
+ // if the postgrest service has an empty schema, the user chose to turn off postgrest during project creation
+ if (service.name === 'rest' && extractDbSchema(service) === '') {
+ return false
+ }
+ if (service.status === 'ACTIVE_HEALTHY') {
+ return false
+ }
+ return true
+ })
+
+ return isServiceUnhealthy ? 5000 : false
},
}
)
@@ -155,7 +178,6 @@ export const ServiceStatus = () => {
error?: string
docsUrl?: string
isLoading: boolean
- isHealthy: boolean
status: ProjectServiceStatus
logsUrl: string
}[] = [
@@ -164,7 +186,6 @@ export const ServiceStatus = () => {
error: undefined,
docsUrl: undefined,
isLoading: isLoading,
- isHealthy: !!dbStatus?.healthy,
status: dbStatus?.status ?? 'UNHEALTHY',
logsUrl: '/logs/postgres-logs',
},
@@ -173,8 +194,8 @@ export const ServiceStatus = () => {
error: restStatus?.error,
docsUrl: undefined,
isLoading,
- isHealthy: !!restStatus?.healthy,
- status: restStatus?.status ?? 'UNHEALTHY',
+ // If PostgREST has an empty schema, it means it's been disabled
+ status: extractDbSchema(restStatus) === '' ? 'DISABLED' : restStatus?.status ?? 'UNHEALTHY',
logsUrl: '/logs/postgrest-logs',
},
...(authEnabled
@@ -184,7 +205,6 @@ export const ServiceStatus = () => {
error: authStatus?.error,
docsUrl: undefined,
isLoading,
- isHealthy: !!authStatus?.healthy,
status: authStatus?.status ?? 'UNHEALTHY',
logsUrl: '/logs/auth-logs',
},
@@ -197,7 +217,6 @@ export const ServiceStatus = () => {
error: realtimeStatus?.error,
docsUrl: undefined,
isLoading,
- isHealthy: !!realtimeStatus?.healthy,
status: realtimeStatus?.status ?? 'UNHEALTHY',
logsUrl: '/logs/realtime-logs',
},
@@ -210,7 +229,6 @@ export const ServiceStatus = () => {
error: storageStatus?.error,
docsUrl: undefined,
isLoading,
- isHealthy: !!storageStatus?.healthy,
status: storageStatus?.status ?? 'UNHEALTHY',
logsUrl: '/logs/storage-logs',
},
@@ -223,7 +241,6 @@ export const ServiceStatus = () => {
error: undefined,
docsUrl: `${DOCS_URL}/guides/functions/troubleshooting`,
isLoading,
- isHealthy: !!edgeFunctionsStatus?.healthy,
status: edgeFunctionsStatus?.healthy
? 'ACTIVE_HEALTHY'
: isLoading
@@ -240,7 +257,6 @@ export const ServiceStatus = () => {
error: undefined,
docsUrl: undefined,
isLoading: isBranchesLoading,
- isHealthy: currentBranch?.status === 'FUNCTIONS_DEPLOYED',
status: (currentBranch?.status === 'FUNCTIONS_DEPLOYED'
? 'ACTIVE_HEALTHY'
: currentBranch?.status === 'FUNCTIONS_FAILED' ||
@@ -258,7 +274,10 @@ export const ServiceStatus = () => {
currentBranch?.status === 'CREATING_PROJECT' ||
currentBranch?.status === 'RUNNING_MIGRATIONS'
const isLoadingChecks = services.some((service) => service.isLoading)
- const allServicesOperational = services.every((service) => service.isHealthy)
+ // We consider a service operational if it's healthy or intentionally disabled
+ const allServicesOperational = services.every(
+ (service) => service.status === 'ACTIVE_HEALTHY' || service.status === 'DISABLED'
+ )
// 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 =
@@ -316,7 +335,6 @@ export const ServiceStatus = () => {
@@ -325,7 +343,6 @@ export const ServiceStatus = () => {
diff --git a/apps/studio/components/interfaces/HomeNew/ServiceStatus.tsx b/apps/studio/components/interfaces/HomeNew/ServiceStatus.tsx
index 7b8dd1706c9..810f3eb0d07 100644
--- a/apps/studio/components/interfaces/HomeNew/ServiceStatus.tsx
+++ b/apps/studio/components/interfaces/HomeNew/ServiceStatus.tsx
@@ -1,5 +1,5 @@
import dayjs from 'dayjs'
-import { AlertTriangle, CheckCircle2, ChevronRight, Loader2 } from 'lucide-react'
+import { ChevronRight, Loader2 } from 'lucide-react'
import Link from 'next/link'
import { useParams } from 'common'
@@ -7,14 +7,17 @@ import { InlineLink } from 'components/ui/InlineLink'
import { SingleStat } from 'components/ui/SingleStat'
import { useBranchesQuery } from 'data/branches/branches-query'
import { useEdgeFunctionServiceStatusQuery } from 'data/service-status/edge-functions-status-query'
-import {
- ProjectServiceStatus,
- useProjectServiceStatusQuery,
-} from 'data/service-status/service-status-query'
+import { useProjectServiceStatusQuery } from 'data/service-status/service-status-query'
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { DOCS_URL } from 'lib/constants'
import { InfoIcon, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_, Popover_Shadcn_, cn } from 'ui'
+import {
+ ProjectServiceStatus,
+ StatusIcon,
+ StatusMessage,
+ extractDbSchema,
+} from '../Home/ServiceStatus'
const SERVICE_STATUS_THRESHOLD = 5 // minutes
@@ -36,49 +39,6 @@ const SERVICE_STATUS_THRESHOLD = 5 // minutes
* minutes of when it was created). Might be related to decoupling "ready" state vs "health checks"
*/
-const StatusMessage = ({
- status,
- isLoading,
- isHealthy,
- isProjectNew,
-}: {
- isLoading: boolean
- isHealthy: boolean
- isProjectNew: boolean
- status?: ProjectServiceStatus
-}) => {
- if (isHealthy || status === 'ACTIVE_HEALTHY') return 'Healthy'
- if (isLoading) return 'Checking status'
- if (status === 'UNHEALTHY') return 'Unhealthy'
- if (isProjectNew || status === 'COMING_UP') return 'Coming up...'
- if (status) return status
- return 'Unable to connect'
-}
-
-const iconProps = {
- size: 18,
- strokeWidth: 1.5,
-}
-const LoaderIcon = () =>
-const AlertIcon = () =>
-const CheckIcon = () =>
-
-const StatusIcon = ({
- isLoading,
- isHealthy,
- isProjectNew,
- projectStatus,
-}: {
- isLoading: boolean
- isHealthy: boolean
- isProjectNew: boolean
- projectStatus?: ProjectServiceStatus
-}) => {
- if (isHealthy || projectStatus === 'ACTIVE_HEALTHY') return
- if (isLoading || isProjectNew || projectStatus === 'COMING_UP') return
- return
-}
-
export const ServiceStatus = () => {
const { ref } = useParams()
const { data: project } = useSelectedProjectQuery()
@@ -113,8 +73,21 @@ export const ServiceStatus = () => {
const { data: status, isPending: isLoading } = useProjectServiceStatusQuery(
{ projectRef: ref },
{
- refetchInterval: (query) =>
- query.state.data?.some((service) => !service.healthy) ? 5000 : false,
+ refetchInterval: (query) => {
+ const data = query.state.data
+ const isServiceUnhealthy = data?.some((service) => {
+ // if the postgrest service has an empty schema, postgrest has been disabled
+ if (service.name === 'rest' && extractDbSchema(service) === '') {
+ return false
+ }
+ if (service.status === 'ACTIVE_HEALTHY') {
+ return false
+ }
+ return true
+ })
+
+ return isServiceUnhealthy ? 5000 : false
+ },
}
)
const { data: edgeFunctionsStatus } = useEdgeFunctionServiceStatusQuery(
@@ -141,7 +114,6 @@ export const ServiceStatus = () => {
error?: string
docsUrl?: string
isLoading: boolean
- isHealthy: boolean
status: ProjectServiceStatus
logsUrl: string
}[] = [
@@ -150,7 +122,6 @@ export const ServiceStatus = () => {
error: undefined,
docsUrl: undefined,
isLoading: isLoading,
- isHealthy: !!dbStatus?.healthy,
status: dbStatus?.status ?? 'UNHEALTHY',
logsUrl: '/logs/postgres-logs',
},
@@ -159,8 +130,8 @@ export const ServiceStatus = () => {
error: restStatus?.error,
docsUrl: undefined,
isLoading,
- isHealthy: !!restStatus?.healthy,
- status: restStatus?.status ?? 'UNHEALTHY',
+ // If PostgREST has an empty schema, it means it's been disabled
+ status: extractDbSchema(restStatus) === '' ? 'DISABLED' : restStatus?.status ?? 'UNHEALTHY',
logsUrl: '/logs/postgrest-logs',
},
...(authEnabled
@@ -170,7 +141,6 @@ export const ServiceStatus = () => {
error: authStatus?.error,
docsUrl: undefined,
isLoading,
- isHealthy: !!authStatus?.healthy,
status: authStatus?.status ?? 'UNHEALTHY',
logsUrl: '/logs/auth-logs',
},
@@ -183,7 +153,6 @@ export const ServiceStatus = () => {
error: realtimeStatus?.error,
docsUrl: undefined,
isLoading,
- isHealthy: !!realtimeStatus?.healthy,
status: realtimeStatus?.status ?? 'UNHEALTHY',
logsUrl: '/logs/realtime-logs',
},
@@ -196,7 +165,6 @@ export const ServiceStatus = () => {
error: storageStatus?.error,
docsUrl: undefined,
isLoading,
- isHealthy: !!storageStatus?.healthy,
status: storageStatus?.status ?? 'UNHEALTHY',
logsUrl: '/logs/storage-logs',
},
@@ -209,12 +177,11 @@ export const ServiceStatus = () => {
error: undefined,
docsUrl: `${DOCS_URL}/guides/functions/troubleshooting`,
isLoading,
- isHealthy: !!edgeFunctionsStatus?.healthy,
status: edgeFunctionsStatus?.healthy
- ? 'ACTIVE_HEALTHY'
+ ? ('ACTIVE_HEALTHY' as const)
: isLoading
- ? 'COMING_UP'
- : ('UNHEALTHY' as ProjectServiceStatus),
+ ? ('COMING_UP' as const)
+ : ('UNHEALTHY' as const),
logsUrl: '/logs/edge-functions-logs',
},
]
@@ -226,19 +193,16 @@ export const ServiceStatus = () => {
error: undefined,
docsUrl: undefined,
isLoading: isBranchesLoading,
- isHealthy: isBranch
+ status: isBranch
? currentBranch?.status === 'FUNCTIONS_DEPLOYED'
- : !isMigrationLoading,
- status: (isBranch
- ? currentBranch?.status === 'FUNCTIONS_DEPLOYED'
- ? 'ACTIVE_HEALTHY'
+ ? ('ACTIVE_HEALTHY' as const)
: currentBranch?.status === 'FUNCTIONS_FAILED' ||
currentBranch?.status === 'MIGRATIONS_FAILED'
- ? 'UNHEALTHY'
- : 'COMING_UP'
+ ? ('UNHEALTHY' as const)
+ : ('COMING_UP' as const)
: isMigrationLoading
? 'COMING_UP'
- : 'ACTIVE_HEALTHY') as ProjectServiceStatus,
+ : 'ACTIVE_HEALTHY',
logsUrl: isBranch ? '/branches' : '/logs/database-logs',
},
]
@@ -246,7 +210,10 @@ export const ServiceStatus = () => {
]
const isLoadingChecks = services.some((service) => service.isLoading)
- const allServicesOperational = services.every((service) => service.isHealthy)
+ // We consider a service operational if it's healthy or intentionally disabled
+ const allServicesOperational = services.every(
+ (service) => service.status === 'ACTIVE_HEALTHY' || service.status === 'DISABLED'
+ )
// Check if project or branch is in a startup state
const isProjectNew =
@@ -257,9 +224,7 @@ export const ServiceStatus = () => {
currentBranch?.status === 'RUNNING_MIGRATIONS' ||
isMigrationLoading))
- const anyUnhealthy = services.some(
- (service) => !service.isHealthy && service.status !== 'COMING_UP'
- )
+ const anyUnhealthy = services.some((service) => service.status === 'UNHEALTHY')
const anyComingUp = services.some((service) => service.status === 'COMING_UP')
// Spinner only while the overall project is in COMING_UP; otherwise show 6-dot grid
const showSpinnerIcon = project?.status === 'COMING_UP'
@@ -289,9 +254,9 @@ export const ServiceStatus = () => {
'w-1.5 h-1.5 rounded-full',
service.isLoading ||
service.status === 'COMING_UP' ||
- (isProjectNew && !service.isHealthy)
+ (isProjectNew && service.status !== 'ACTIVE_HEALTHY')
? 'bg-foreground-lighter animate-pulse'
- : service.isHealthy
+ : service.status === 'ACTIVE_HEALTHY'
? 'bg-brand'
: 'bg-selection'
)}
@@ -314,7 +279,6 @@ export const ServiceStatus = () => {
@@ -323,7 +287,6 @@ export const ServiceStatus = () => {
diff --git a/apps/studio/data/service-status/service-status-query.ts b/apps/studio/data/service-status/service-status-query.ts
index 38d481bbdd6..4b8f2d3c422 100644
--- a/apps/studio/data/service-status/service-status-query.ts
+++ b/apps/studio/data/service-status/service-status-query.ts
@@ -1,5 +1,6 @@
import { useQuery } from '@tanstack/react-query'
+import { components } from 'api-types'
import { get, handleError } from 'data/fetchers'
import type { ResponseError, UseCustomQueryOptions } from 'types'
import { serviceStatusKeys } from './keys'
@@ -8,6 +9,13 @@ export type ProjectServiceStatusVariables = {
projectRef?: string
}
+// Omit the 'healthy' field as it's equivalent to status = 'ACTIVE_HEALTHY'
+export type ServiceHealthResponse = Omit<
+ components['schemas']['V1ServiceHealthResponse'],
+ 'healthy'
+>
+export type ProjectServiceStatus = ServiceHealthResponse['status']
+
export async function getProjectServiceStatus(
{ projectRef }: ProjectServiceStatusVariables,
signal?: AbortSignal
@@ -25,11 +33,11 @@ export async function getProjectServiceStatus(
})
if (error) handleError(error)
- return data
+
+ return data as ServiceHealthResponse[]
}
export type ProjectServiceStatusData = Awaited>
-export type ProjectServiceStatus = ProjectServiceStatusData[0]['status']
export type ProjectServiceStatusError = ResponseError
export const useProjectServiceStatusQuery = (