mirror of
https://github.com/supabase/supabase.git
synced 2026-10-18 23:59:08 +03:00
Merge pull request #16557 from supabase/chore/convert-requests-to-RQ-4
Convert independent POST requests to RQ P2
This commit is contained in:
10 files changed
+403
-141
No files matched your search
@@ -2,14 +2,14 @@ import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import dayjs from 'dayjs'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useState } from 'react'
|
||||
import { Badge, Button, IconDownload } from 'ui'
|
||||
|
||||
import { confirmAlert } from 'components/to-be-cleaned/ModalsDeprecated/ConfirmModal'
|
||||
import { useBackupDownloadMutation } from 'data/database/backup-download-mutation'
|
||||
import { useBackupRestoreMutation } from 'data/database/backup-restore-mutation'
|
||||
import { setProjectStatus } from 'data/projects/projects-query'
|
||||
import { useCheckPermissions, useStore } from 'hooks'
|
||||
import { post } from 'lib/common/fetch'
|
||||
import { API_URL, PROJECT_STATUS } from 'lib/constants'
|
||||
import { Badge, Button, IconDownload } from 'ui'
|
||||
import { PROJECT_STATUS } from 'lib/constants'
|
||||
|
||||
interface BackupItemProps {
|
||||
projectRef: string
|
||||
@@ -18,48 +18,33 @@ interface BackupItemProps {
|
||||
}
|
||||
|
||||
const BackupItem = ({ projectRef, backup, index }: BackupItemProps) => {
|
||||
const queryClient = useQueryClient()
|
||||
const router = useRouter()
|
||||
const { ui } = useStore()
|
||||
|
||||
const [isDownloading, setDownloading] = useState<boolean>(false)
|
||||
const [isRestoring, setRestoring] = useState<boolean>(false)
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const canTriggerScheduledBackups = useCheckPermissions(
|
||||
PermissionAction.INFRA_EXECUTE,
|
||||
'queue_job.restore.prepare'
|
||||
)
|
||||
|
||||
async function restore(backup: any) {
|
||||
setRestoring(true)
|
||||
try {
|
||||
post(`${API_URL}/database/${projectRef}/backups/restore`, backup).then(() => {
|
||||
setTimeout(() => {
|
||||
setProjectStatus(queryClient, projectRef, PROJECT_STATUS.RESTORING)
|
||||
ui.setNotification({
|
||||
category: 'success',
|
||||
message: `Restoring database back to ${dayjs(backup.inserted_at).format(
|
||||
'DD MMM YYYY HH:mm:ss'
|
||||
)}`,
|
||||
})
|
||||
router.push(`/project/${projectRef}`)
|
||||
}, 3000)
|
||||
})
|
||||
} catch (error: any) {
|
||||
ui.setNotification({
|
||||
error,
|
||||
category: 'error',
|
||||
message: `Failed to restore from backup: ${error.message}`,
|
||||
})
|
||||
setRestoring(false)
|
||||
}
|
||||
}
|
||||
const { mutate: restoreFromBackup, isLoading: isRestoring } = useBackupRestoreMutation({
|
||||
onSuccess: () => {
|
||||
setTimeout(() => {
|
||||
setProjectStatus(queryClient, projectRef, PROJECT_STATUS.RESTORING)
|
||||
ui.setNotification({
|
||||
category: 'success',
|
||||
message: `Restoring database back to ${dayjs(backup.inserted_at).format(
|
||||
'DD MMM YYYY HH:mm:ss'
|
||||
)}`,
|
||||
})
|
||||
router.push(`/project/${projectRef}`)
|
||||
}, 3000)
|
||||
},
|
||||
})
|
||||
|
||||
async function download(backup: any) {
|
||||
setDownloading(true)
|
||||
try {
|
||||
const res = await post(`${API_URL}/database/${projectRef}/backups/download`, backup)
|
||||
const { fileUrl } = await res
|
||||
const { mutate: downloadBackup, isLoading: isDownloading } = useBackupDownloadMutation({
|
||||
onSuccess: (res) => {
|
||||
const { fileUrl } = res
|
||||
|
||||
// Trigger browser download by create,trigger and remove tempLink
|
||||
const tempLink = document.createElement('a')
|
||||
@@ -67,27 +52,16 @@ const BackupItem = ({ projectRef, backup, index }: BackupItemProps) => {
|
||||
document.body.appendChild(tempLink)
|
||||
tempLink.click()
|
||||
document.body.removeChild(tempLink)
|
||||
},
|
||||
})
|
||||
|
||||
setDownloading(false)
|
||||
} catch (error: any) {
|
||||
ui.setNotification({
|
||||
error,
|
||||
category: 'error',
|
||||
message: `Failed to download backup: ${error.message}`,
|
||||
})
|
||||
setDownloading(false)
|
||||
}
|
||||
}
|
||||
|
||||
function onRestoreClick() {
|
||||
const onRestoreClick = () => {
|
||||
confirmAlert({
|
||||
title: 'Confirm to restore',
|
||||
message: `Are you sure you want to restore from ${dayjs(backup.inserted_at).format(
|
||||
'DD MMM YYYY'
|
||||
)}? This will destroy any new data written since this backup was made.`,
|
||||
onAsyncConfirm: async () => {
|
||||
await restore(backup)
|
||||
},
|
||||
onAsyncConfirm: async () => restoreFromBackup({ ref: projectRef, backup }),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -108,7 +82,7 @@ const BackupItem = ({ projectRef, backup, index }: BackupItemProps) => {
|
||||
<Button
|
||||
type="default"
|
||||
disabled={!canTriggerScheduledBackups || isRestoring || isDownloading}
|
||||
onClick={() => download(backup)}
|
||||
onClick={() => downloadBackup({ ref: projectRef, backup })}
|
||||
loading={isDownloading}
|
||||
icon={<IconDownload />}
|
||||
>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as Tooltip from '@radix-ui/react-tooltip'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { useParams } from 'common'
|
||||
import { format } from 'date-fns'
|
||||
import dayjs from 'dayjs'
|
||||
import { useRouter } from 'next/router'
|
||||
@@ -7,13 +8,12 @@ import { useState } from 'react'
|
||||
import DatePicker from 'react-datepicker'
|
||||
import { Alert, Button, IconChevronLeft, IconChevronRight, IconHelpCircle, Modal } from 'ui'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { FormHeader, FormPanel } from 'components/ui/Forms'
|
||||
import InformationBox from 'components/ui/InformationBox'
|
||||
import { usePitrRestoreMutation } from 'data/database/pitr-restore-mutation'
|
||||
import { setProjectStatus } from 'data/projects/projects-query'
|
||||
import { useStore } from 'hooks'
|
||||
import { post } from 'lib/common/fetch'
|
||||
import { API_URL, PROJECT_STATUS } from 'lib/constants'
|
||||
import { PROJECT_STATUS } from 'lib/constants'
|
||||
import BackupsEmpty from '../BackupsEmpty'
|
||||
import { Timezone } from './PITR.types'
|
||||
import {
|
||||
@@ -27,14 +27,25 @@ import TimeInput from './TimeInput'
|
||||
import TimezoneSelection from './TimezoneSelection'
|
||||
|
||||
const PITRSelection = () => {
|
||||
const queryClient = useQueryClient()
|
||||
const router = useRouter()
|
||||
const { ref } = useParams()
|
||||
const { backups } = useStore()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const { ui, backups } = useStore()
|
||||
|
||||
const [showConfiguration, setShowConfiguration] = useState(false)
|
||||
const [showConfirmation, setShowConfirmation] = useState(false)
|
||||
const [selectedTimezone, setSelectedTimezone] = useState<Timezone>(getClientTimezone())
|
||||
|
||||
const { mutate: restoreFromPitr, isLoading: isRestoring } = usePitrRestoreMutation({
|
||||
onSuccess: (res, variables) => {
|
||||
setTimeout(() => {
|
||||
setShowConfirmation(false)
|
||||
setProjectStatus(queryClient, variables.ref, PROJECT_STATUS.RESTORING)
|
||||
router.push(`/project/${variables.ref}`)
|
||||
}, 3000)
|
||||
},
|
||||
})
|
||||
|
||||
const { earliestPhysicalBackupDateUnix, latestPhysicalBackupDateUnix } =
|
||||
backups?.configuration?.physicalBackupData ?? {}
|
||||
const hasNoBackupsAvailable = !earliestPhysicalBackupDateUnix || !latestPhysicalBackupDateUnix
|
||||
@@ -46,10 +57,6 @@ const PITRSelection = () => {
|
||||
const [selectedDateRaw, setSelectedDateRaw] = useState<Date>(latestAvailableBackup.toDate())
|
||||
const selectedDate = dayjs(selectedDateRaw).tz(selectedTimezone.utc[0], true) // true to keep local time and just change +whatever
|
||||
|
||||
const [showConfiguration, setShowConfiguration] = useState(false)
|
||||
const [showConfirmation, setShowConfirmation] = useState(false)
|
||||
const [isRestoring, setIsRestoring] = useState(false)
|
||||
|
||||
const isSelectedOnEarliestDay = selectedDate.isSame(earliestAvailableBackup, 'day')
|
||||
const isSelectedOnLatestDay = selectedDate.isSame(latestAvailableBackup, 'day')
|
||||
const availableDates = getDatesBetweenRange(earliestAvailableBackup, latestAvailableBackup)
|
||||
@@ -98,28 +105,9 @@ const PITRSelection = () => {
|
||||
}
|
||||
|
||||
const onConfirmRestore = async () => {
|
||||
if (!recoveryTimeTargetUnix) return
|
||||
|
||||
setIsRestoring(true)
|
||||
const projectRef = ref as string
|
||||
const { error } = await post(`${API_URL}/database/${projectRef}/backups/pitr`, {
|
||||
recovery_time_target_unix: recoveryTimeTargetUnix,
|
||||
})
|
||||
|
||||
if (error) {
|
||||
ui.setNotification({
|
||||
error,
|
||||
category: 'error',
|
||||
message: `Failed to start restoration: ${error.message}`,
|
||||
})
|
||||
setIsRestoring(false)
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
setShowConfirmation(false)
|
||||
setProjectStatus(queryClient, projectRef, PROJECT_STATUS.RESTORING)
|
||||
router.push(`/project/${projectRef}`)
|
||||
}, 3000)
|
||||
}
|
||||
if (!ref) return console.error('Project ref is required')
|
||||
if (!recoveryTimeTargetUnix) return console.error('Recovery time target unix is required')
|
||||
restoreFromPitr({ ref, recovery_time_target_unix: recoveryTimeTargetUnix })
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
+16
-29
@@ -3,17 +3,17 @@ import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useState } from 'react'
|
||||
import { Button, IconPause } from 'ui'
|
||||
|
||||
import {
|
||||
useIsProjectActive,
|
||||
useProjectContext,
|
||||
} from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import ConfirmModal from 'components/ui/Dialogs/ConfirmDialog'
|
||||
import { useProjectPauseMutation } from 'data/projects/project-pause-mutation'
|
||||
import { setProjectStatus } from 'data/projects/projects-query'
|
||||
import { useCheckPermissions, useStore } from 'hooks'
|
||||
import { post } from 'lib/common/fetch'
|
||||
import { API_URL, PROJECT_STATUS } from 'lib/constants'
|
||||
import { Button, IconPause } from 'ui'
|
||||
import { PROJECT_STATUS } from 'lib/constants'
|
||||
|
||||
const PauseProjectButton = () => {
|
||||
const { ui } = useStore()
|
||||
@@ -21,12 +21,8 @@ const PauseProjectButton = () => {
|
||||
const queryClient = useQueryClient()
|
||||
const { project } = useProjectContext()
|
||||
const isProjectActive = useIsProjectActive()
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [isModalOpen, setIsModalOpen] = useState(false)
|
||||
|
||||
const openModal = () => setIsModalOpen(true)
|
||||
const closeModal = () => setIsModalOpen(false)
|
||||
|
||||
const projectRef = project?.ref ?? ''
|
||||
const isPaused = project?.status === PROJECT_STATUS.INACTIVE
|
||||
const canPauseProject = useCheckPermissions(
|
||||
@@ -34,31 +30,22 @@ const PauseProjectButton = () => {
|
||||
'queue_jobs.projects.pause'
|
||||
)
|
||||
|
||||
const requestPauseProject = async () => {
|
||||
const { mutate: pauseProject, isLoading: isPausing } = useProjectPauseMutation({
|
||||
onSuccess: (res, variables) => {
|
||||
setProjectStatus(queryClient, variables.ref, PROJECT_STATUS.PAUSING)
|
||||
ui.setNotification({ category: 'success', message: 'Pausing project...' })
|
||||
router.push(`/project/${projectRef}`)
|
||||
},
|
||||
})
|
||||
|
||||
const requestPauseProject = () => {
|
||||
if (!canPauseProject) {
|
||||
return ui.setNotification({
|
||||
category: 'error',
|
||||
message: 'You do not have the required permissions to pause this project',
|
||||
})
|
||||
}
|
||||
|
||||
setLoading(true)
|
||||
const res = await post(`${API_URL}/projects/${projectRef}/pause`, {})
|
||||
|
||||
if (res.error) {
|
||||
ui.setNotification({
|
||||
error: res.error,
|
||||
category: 'error',
|
||||
message: `Failed to pause project: ${res.error.message}`,
|
||||
})
|
||||
setLoading(false)
|
||||
} else {
|
||||
setProjectStatus(queryClient, projectRef, PROJECT_STATUS.PAUSING)
|
||||
|
||||
ui.setNotification({ category: 'success', message: 'Pausing project' })
|
||||
router.push(`/project/${projectRef}`)
|
||||
}
|
||||
closeModal()
|
||||
pauseProject({ ref: projectRef })
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -68,8 +55,8 @@ const PauseProjectButton = () => {
|
||||
<Button
|
||||
type="default"
|
||||
icon={<IconPause />}
|
||||
onClick={openModal}
|
||||
loading={loading}
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
loading={isPausing}
|
||||
disabled={project === undefined || isPaused || !canPauseProject || !isProjectActive}
|
||||
>
|
||||
Pause Project
|
||||
@@ -106,7 +93,7 @@ const PauseProjectButton = () => {
|
||||
description="Are you sure you want to pause this project? It will not be accessible until you unpause it."
|
||||
buttonLabel="Pause project"
|
||||
buttonLoadingLabel="Pausing project"
|
||||
onSelectCancel={closeModal}
|
||||
onSelectCancel={() => setIsModalOpen(false)}
|
||||
onSelectConfirm={requestPauseProject}
|
||||
/>
|
||||
</>
|
||||
|
||||
+33
-27
@@ -6,41 +6,57 @@ import { useRouter } from 'next/router'
|
||||
import { useState } from 'react'
|
||||
import { Button, Dropdown, IconChevronDown } from 'ui'
|
||||
|
||||
import ConfirmModal from 'components/ui/Dialogs/ConfirmDialog'
|
||||
import { setProjectPostgrestStatus } from 'data/projects/projects-query'
|
||||
import { useCheckPermissions, useStore } from 'hooks'
|
||||
import { post } from 'lib/common/fetch'
|
||||
import { API_URL } from 'lib/constants'
|
||||
import {
|
||||
useIsProjectActive,
|
||||
useProjectContext,
|
||||
} from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import ConfirmModal from 'components/ui/Dialogs/ConfirmDialog'
|
||||
import { useProjectRestartMutation } from 'data/projects/project-restart-mutation'
|
||||
import { useProjectRestartServicesMutation } from 'data/projects/project-restart-services-mutation'
|
||||
import { setProjectPostgrestStatus } from 'data/projects/projects-query'
|
||||
import { useCheckPermissions, useStore } from 'hooks'
|
||||
|
||||
const RestartServerButton = () => {
|
||||
const queryClient = useQueryClient()
|
||||
const { project } = useProjectContext()
|
||||
const { ui } = useStore()
|
||||
const router = useRouter()
|
||||
const queryClient = useQueryClient()
|
||||
const { project } = useProjectContext()
|
||||
const isProjectActive = useIsProjectActive()
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [serviceToRestart, setServiceToRestart] = useState<'project' | 'database'>()
|
||||
|
||||
const projectRef = project?.ref ?? ''
|
||||
const projectRegion = project?.region
|
||||
const projectRegion = project?.region ?? ''
|
||||
|
||||
const canRestartProject = useCheckPermissions(PermissionAction.INFRA_EXECUTE, 'reboot')
|
||||
|
||||
const requestProjectRestart = async () => {
|
||||
const { mutate: restartProject, isLoading: isRestartingProject } = useProjectRestartMutation({
|
||||
onSuccess: () => {
|
||||
onRestartSuccess()
|
||||
},
|
||||
onError: (error) => {
|
||||
onRestartFailed(error, 'project')
|
||||
},
|
||||
})
|
||||
const { mutate: restartProjectServices, isLoading: isRestartingServices } =
|
||||
useProjectRestartServicesMutation({
|
||||
onSuccess: () => {
|
||||
onRestartSuccess()
|
||||
},
|
||||
onError: (error) => {
|
||||
onRestartFailed(error, 'database')
|
||||
},
|
||||
})
|
||||
|
||||
const isLoading = isRestartingProject || isRestartingServices
|
||||
|
||||
const requestProjectRestart = () => {
|
||||
if (!canRestartProject) {
|
||||
return ui.setNotification({
|
||||
category: 'error',
|
||||
message: 'You do not have the required permissions to restart this project',
|
||||
})
|
||||
}
|
||||
setLoading(true)
|
||||
const res = await post(`${API_URL}/projects/${projectRef}/restart`, {})
|
||||
if (res.error) onRestartFailed(res.error, 'project')
|
||||
else onRestartSuccess()
|
||||
restartProject({ ref: projectRef })
|
||||
}
|
||||
|
||||
const requestDatabaseRestart = async () => {
|
||||
@@ -50,15 +66,7 @@ const RestartServerButton = () => {
|
||||
message: 'You do not have the required permissions to restart this project',
|
||||
})
|
||||
}
|
||||
setLoading(true)
|
||||
const res = await post(`${API_URL}/projects/${projectRef}/restart-services`, {
|
||||
restartRequest: {
|
||||
region: projectRegion,
|
||||
services: ['postgresql'],
|
||||
},
|
||||
})
|
||||
if (res.error) onRestartFailed(res.error, 'database')
|
||||
else onRestartSuccess()
|
||||
restartProjectServices({ ref: projectRef, region: projectRegion, services: ['postgresql'] })
|
||||
}
|
||||
|
||||
const onRestartFailed = (error: any, type: string) => {
|
||||
@@ -67,15 +75,13 @@ const RestartServerButton = () => {
|
||||
category: 'error',
|
||||
message: `Unable to restart ${type}: ${error.message}`,
|
||||
})
|
||||
setLoading(false)
|
||||
setServiceToRestart(undefined)
|
||||
}
|
||||
|
||||
const onRestartSuccess = () => {
|
||||
setProjectPostgrestStatus(queryClient, projectRef, 'OFFLINE')
|
||||
ui.setNotification({ category: 'success', message: 'Restarting server' })
|
||||
ui.setNotification({ category: 'success', message: 'Restarting server...' })
|
||||
router.push(`/project/${projectRef}`)
|
||||
setLoading(false)
|
||||
setServiceToRestart(undefined)
|
||||
}
|
||||
|
||||
@@ -99,7 +105,7 @@ const RestartServerButton = () => {
|
||||
overlay={[
|
||||
<Dropdown.Item
|
||||
key="database"
|
||||
disabled={loading}
|
||||
disabled={isLoading}
|
||||
onClick={() => {
|
||||
setServiceToRestart('database')
|
||||
}}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query'
|
||||
import { toast } from 'react-hot-toast'
|
||||
|
||||
import { post } from 'data/fetchers'
|
||||
import { ResponseError } from 'types'
|
||||
import { Backup } from './backup-restore-mutation'
|
||||
|
||||
export type BackupDownloadVariables = {
|
||||
ref: string
|
||||
backup: Backup
|
||||
}
|
||||
|
||||
export async function downloadBackup({ ref, backup }: BackupDownloadVariables) {
|
||||
const { data, error } = await post('/platform/database/{ref}/backups/download', {
|
||||
params: { path: { ref } },
|
||||
body: { ...backup, status: backup.status as unknown as string },
|
||||
})
|
||||
if (error) throw error
|
||||
return data
|
||||
}
|
||||
|
||||
type BackupDownloadData = Awaited<ReturnType<typeof downloadBackup>>
|
||||
|
||||
export const useBackupDownloadMutation = ({
|
||||
onSuccess,
|
||||
onError,
|
||||
...options
|
||||
}: Omit<
|
||||
UseMutationOptions<BackupDownloadData, ResponseError, BackupDownloadVariables>,
|
||||
'mutationFn'
|
||||
> = {}) => {
|
||||
return useMutation<BackupDownloadData, ResponseError, BackupDownloadVariables>(
|
||||
(vars) => downloadBackup(vars),
|
||||
{
|
||||
async onSuccess(data, variables, context) {
|
||||
await onSuccess?.(data, variables, context)
|
||||
},
|
||||
async onError(data, variables, context) {
|
||||
if (onError === undefined) {
|
||||
toast.error(`Failed to download backup: ${data.message}`)
|
||||
} else {
|
||||
onError(data, variables, context)
|
||||
}
|
||||
},
|
||||
...options,
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query'
|
||||
import { toast } from 'react-hot-toast'
|
||||
|
||||
import { post } from 'data/fetchers'
|
||||
import { ResponseError } from 'types'
|
||||
import { components } from 'data/api'
|
||||
|
||||
export type BackupRestoreVariables = {
|
||||
ref: string
|
||||
backup: Backup
|
||||
}
|
||||
|
||||
// [Joshen] Shift to backups RQ query once created
|
||||
export type Backup = components['schemas']['Backup']
|
||||
|
||||
export async function restoreFromBackup({ ref, backup }: BackupRestoreVariables) {
|
||||
const { data, error } = await post('/platform/database/{ref}/backups/restore', {
|
||||
params: { path: { ref } },
|
||||
body: { ...backup, status: backup.status as unknown as string },
|
||||
})
|
||||
if (error) throw error
|
||||
return data
|
||||
}
|
||||
|
||||
type BackupRestoreData = Awaited<ReturnType<typeof restoreFromBackup>>
|
||||
|
||||
export const useBackupRestoreMutation = ({
|
||||
onSuccess,
|
||||
onError,
|
||||
...options
|
||||
}: Omit<
|
||||
UseMutationOptions<BackupRestoreData, ResponseError, BackupRestoreVariables>,
|
||||
'mutationFn'
|
||||
> = {}) => {
|
||||
return useMutation<BackupRestoreData, ResponseError, BackupRestoreVariables>(
|
||||
(vars) => restoreFromBackup(vars),
|
||||
{
|
||||
async onSuccess(data, variables, context) {
|
||||
await onSuccess?.(data, variables, context)
|
||||
},
|
||||
async onError(data, variables, context) {
|
||||
if (onError === undefined) {
|
||||
toast.error(`Failed to restore from backup: ${data.message}`)
|
||||
} else {
|
||||
onError(data, variables, context)
|
||||
}
|
||||
},
|
||||
...options,
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query'
|
||||
import { toast } from 'react-hot-toast'
|
||||
|
||||
import { post } from 'data/fetchers'
|
||||
import { ResponseError } from 'types'
|
||||
|
||||
export type PitrRestoreVariables = {
|
||||
ref: string
|
||||
recovery_time_target_unix: number
|
||||
}
|
||||
|
||||
export async function restoreFromPitr({ ref, recovery_time_target_unix }: PitrRestoreVariables) {
|
||||
const { data, error } = await post('/platform/database/{ref}/backups/pitr', {
|
||||
params: { path: { ref } },
|
||||
body: { recovery_time_target_unix },
|
||||
})
|
||||
if (error) throw error
|
||||
return data
|
||||
}
|
||||
|
||||
type PitrRestoreData = Awaited<ReturnType<typeof restoreFromPitr>>
|
||||
|
||||
export const usePitrRestoreMutation = ({
|
||||
onSuccess,
|
||||
onError,
|
||||
...options
|
||||
}: Omit<
|
||||
UseMutationOptions<PitrRestoreData, ResponseError, PitrRestoreVariables>,
|
||||
'mutationFn'
|
||||
> = {}) => {
|
||||
return useMutation<PitrRestoreData, ResponseError, PitrRestoreVariables>(
|
||||
(vars) => restoreFromPitr(vars),
|
||||
{
|
||||
async onSuccess(data, variables, context) {
|
||||
await onSuccess?.(data, variables, context)
|
||||
},
|
||||
async onError(data, variables, context) {
|
||||
if (onError === undefined) {
|
||||
toast.error(`Failed to start PITR restoration: ${data.message}`)
|
||||
} else {
|
||||
onError(data, variables, context)
|
||||
}
|
||||
},
|
||||
...options,
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query'
|
||||
import { toast } from 'react-hot-toast'
|
||||
|
||||
import { post } from 'data/fetchers'
|
||||
import { ResponseError } from 'types'
|
||||
|
||||
export type ProjectPauseVariables = {
|
||||
ref: string
|
||||
}
|
||||
|
||||
export async function pauseProject({ ref }: ProjectPauseVariables) {
|
||||
const { data, error } = await post('/platform/projects/{ref}/pause', {
|
||||
params: { path: { ref } },
|
||||
})
|
||||
if (error) throw error
|
||||
return data
|
||||
}
|
||||
|
||||
type ProjectPauseData = Awaited<ReturnType<typeof pauseProject>>
|
||||
|
||||
export const useProjectPauseMutation = ({
|
||||
onSuccess,
|
||||
onError,
|
||||
...options
|
||||
}: Omit<
|
||||
UseMutationOptions<ProjectPauseData, ResponseError, ProjectPauseVariables>,
|
||||
'mutationFn'
|
||||
> = {}) => {
|
||||
return useMutation<ProjectPauseData, ResponseError, ProjectPauseVariables>(
|
||||
(vars) => pauseProject(vars),
|
||||
{
|
||||
async onSuccess(data, variables, context) {
|
||||
await onSuccess?.(data, variables, context)
|
||||
},
|
||||
async onError(data, variables, context) {
|
||||
if (onError === undefined) {
|
||||
toast.error(`Failed to pause project: ${data.message}`)
|
||||
} else {
|
||||
onError(data, variables, context)
|
||||
}
|
||||
},
|
||||
...options,
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query'
|
||||
import { toast } from 'react-hot-toast'
|
||||
|
||||
import { post } from 'data/fetchers'
|
||||
import { ResponseError } from 'types'
|
||||
|
||||
export type ProjectRestartVariables = {
|
||||
ref: string
|
||||
}
|
||||
|
||||
export async function restartProject({ ref }: ProjectRestartVariables) {
|
||||
const { data, error } = await post('/platform/projects/{ref}/restart', {
|
||||
params: { path: { ref } },
|
||||
})
|
||||
if (error) throw error
|
||||
return data
|
||||
}
|
||||
|
||||
type ProjectRestartData = Awaited<ReturnType<typeof restartProject>>
|
||||
|
||||
export const useProjectRestartMutation = ({
|
||||
onSuccess,
|
||||
onError,
|
||||
...options
|
||||
}: Omit<
|
||||
UseMutationOptions<ProjectRestartData, ResponseError, ProjectRestartVariables>,
|
||||
'mutationFn'
|
||||
> = {}) => {
|
||||
return useMutation<ProjectRestartData, ResponseError, ProjectRestartVariables>(
|
||||
(vars) => restartProject(vars),
|
||||
{
|
||||
async onSuccess(data, variables, context) {
|
||||
await onSuccess?.(data, variables, context)
|
||||
},
|
||||
async onError(data, variables, context) {
|
||||
if (onError === undefined) {
|
||||
toast.error(`Failed to restart project: ${data.message}`)
|
||||
} else {
|
||||
onError(data, variables, context)
|
||||
}
|
||||
},
|
||||
...options,
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query'
|
||||
import { toast } from 'react-hot-toast'
|
||||
|
||||
import { post } from 'data/fetchers'
|
||||
import { ResponseError } from 'types'
|
||||
|
||||
export type ProjectRestartServicesVariables = {
|
||||
ref: string
|
||||
region: string
|
||||
services: (
|
||||
| 'postgresql'
|
||||
| 'adminapi'
|
||||
| 'api-gateway'
|
||||
| 'functions'
|
||||
| 'gotrue'
|
||||
| 'kong'
|
||||
| 'pgbouncer'
|
||||
| 'pgsodium'
|
||||
| 'postgrest'
|
||||
| 'realtime'
|
||||
| 'storage'
|
||||
| 'walg'
|
||||
| 'autoshutdown'
|
||||
)[]
|
||||
}
|
||||
|
||||
export async function restartProjectServices({
|
||||
ref,
|
||||
region,
|
||||
services = ['postgresql'],
|
||||
}: ProjectRestartServicesVariables) {
|
||||
const { data, error } = await post('/platform/projects/{ref}/restart-services', {
|
||||
params: { path: { ref } },
|
||||
body: {
|
||||
restartRequest: {
|
||||
region,
|
||||
services,
|
||||
},
|
||||
},
|
||||
})
|
||||
if (error) throw error
|
||||
return data
|
||||
}
|
||||
|
||||
type ProjectRestartServicesData = Awaited<ReturnType<typeof restartProjectServices>>
|
||||
|
||||
export const useProjectRestartServicesMutation = ({
|
||||
onSuccess,
|
||||
onError,
|
||||
...options
|
||||
}: Omit<
|
||||
UseMutationOptions<ProjectRestartServicesData, ResponseError, ProjectRestartServicesVariables>,
|
||||
'mutationFn'
|
||||
> = {}) => {
|
||||
return useMutation<ProjectRestartServicesData, ResponseError, ProjectRestartServicesVariables>(
|
||||
(vars) => restartProjectServices(vars),
|
||||
{
|
||||
async onSuccess(data, variables, context) {
|
||||
await onSuccess?.(data, variables, context)
|
||||
},
|
||||
async onError(data, variables, context) {
|
||||
if (onError === undefined) {
|
||||
toast.error(`Failed to restart project: ${data.message}`)
|
||||
} else {
|
||||
onError(data, variables, context)
|
||||
}
|
||||
},
|
||||
...options,
|
||||
}
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user