Skip to main content
Glama

Convex MCP server

Official
by get-convex
BackupRestoreStatus.tsx7.63 kB
import { Button } from "@ui/Button"; import { TimestampDistance } from "@common/elements/TimestampDistance"; import { Callout } from "@ui/Callout"; import { Modal } from "@ui/Modal"; import { formatNumberCompact } from "@common/lib/format"; import { useQuery } from "convex/react"; import { useConfirmImport } from "hooks/deploymentApi"; import { useEffect, useState } from "react"; import { Doc } from "system-udfs/convex/_generated/dataModel"; import { DeploymentResponse, Team } from "generatedApi"; import udfs from "@common/udfs"; import { CheckCircledIcon, CrossCircledIcon } from "@radix-ui/react-icons"; import { ProgressBar } from "@ui/ProgressBar"; import { useListCloudBackups, BackupResponse } from "api/backups"; import { TransferSummary } from "./BackupListItem"; import { ImportSummary } from "./SnapshotImport"; export function BackupRestoreStatus({ deployment, team, }: { deployment: DeploymentResponse; team: Team; }) { const currentRestore = useLatestRestore(); const requestor = currentRestore?.requestor; if (requestor && requestor.type !== "cloudRestore") { throw new Error("Unexpected requestor for restore"); } const sourceCloudBackupId = requestor?.sourceCloudBackupId; const backups = useListCloudBackups(team.id); const backup = currentRestore && (backups?.find((b) => BigInt(b.id) === sourceCloudBackupId) ?? null); // Automatically call confirmImport when the current backup is waiting for confirmation. // This is necessary because the snapshot import flow has a confirmation step, but for backups // the user doesn’t confirm the import after it’s created. const confirmImport = useConfirmImport(); useEffect(() => { if (currentRestore?.state.state === "waiting_for_confirmation") { void confirmImport(currentRestore._id); } }, [currentRestore?.state.state, currentRestore?._id, confirmImport]); if (!currentRestore || backup === undefined) return null; const { state } = currentRestore; switch (state.state) { case "in_progress": return <BackupRestoreOngoing progressMessage={state.progress_message} />; case "uploaded": case "waiting_for_confirmation": return <BackupRestoreOngoing progressMessage="Starting the restore…" />; case "completed": return ( <BackupRestoreSuccess completedTime={new Date(Number(state.timestamp / BigInt(1000000)))} restoredRowsCount={state.num_rows_written} deployment={deployment} team={team} backup={backup} snapshotImportCheckpoints={currentRestore.checkpoints} /> ); case "failed": return ( <BackupRestoreFail errorMessage={state.error_message} restoreStartTime={new Date(currentRestore._creationTime)} deployment={deployment} team={team} backup={backup} /> ); default: { state satisfies never; return null; } } } export function useLatestRestore(): | Doc<"_snapshot_imports"> | null | undefined { const allImports = useQuery(udfs.snapshotImport.list); return allImports?.find((i) => i.requestor.type === "cloudRestore") ?? null; } export function BackupRestoreFail({ errorMessage, restoreStartTime, deployment, team, backup, }: { errorMessage: string; restoreStartTime: Date; deployment: DeploymentResponse; team: Team; backup: BackupResponse | null; }) { const [isModalOpen, setIsModalOpen] = useState(false); return ( <> <div className="flex min-h-16 flex-wrap items-center gap-2 rounded-sm border bg-background-secondary px-4 py-2"> <div className="flex grow gap-2"> <CrossCircledIcon className="size-5 shrink-0 text-content-errorSecondary" /> <p className="grow text-sm leading-tight text-balance text-content-secondary"> The restore started{" "} <TimestampDistance date={restoreStartTime} className="text-sm text-inherit" />{" "} failed. </p> </div> <Button size="sm" variant="neutral" onClick={() => setIsModalOpen(true)} > More Details </Button> </div> {isModalOpen && ( <Modal onClose={() => setIsModalOpen(false)} title={ <div className="flex items-center gap-1"> Restore Failed <CrossCircledIcon className="text-content-error" /> </div> } size="md" > <TransferSummary backup={backup} targetDeployment={deployment} latestBackupInTargetDeployment={undefined} team={team} /> <p className="my-2">Encountered an error while restoring:</p> <Callout variant="error">{errorMessage}</Callout> </Modal> )} </> ); } export function BackupRestoreSuccess({ completedTime, restoredRowsCount, deployment, team, backup, snapshotImportCheckpoints, }: { completedTime: Date; // `number` is used from stories because Storybook crashes when using bigint restoredRowsCount: bigint | number; deployment: DeploymentResponse; team: Team; backup: BackupResponse | null; snapshotImportCheckpoints: Doc<"_snapshot_imports">["checkpoints"] | null; }) { const [isModalOpen, setIsModalOpen] = useState(false); return ( <> <div className="flex min-h-16 flex-wrap items-center gap-2 rounded-sm border bg-background-secondary px-4 py-2"> <div className="flex grow items-center gap-2"> <CheckCircledIcon className="shrink-0 text-content-success" /> <p className="grow text-sm leading-tight text-balance text-content-secondary"> <strong>{`${formatNumberCompact(restoredRowsCount)} ${restoredRowsCount === BigInt(1) ? "document" : "documents"}`}</strong>{" "} {restoredRowsCount === BigInt(1) ? "was" : "were"} restored from a backup{" "} <TimestampDistance date={completedTime} className="text-sm text-inherit" /> . </p> </div> <Button size="sm" variant="neutral" onClick={() => setIsModalOpen(true)} > More Details </Button> </div> {isModalOpen && ( <Modal onClose={() => setIsModalOpen(false)} title={ <div className="flex items-center gap-1"> Restore Succeeded{" "} <CheckCircledIcon className="text-content-success" /> </div> } size="md" > <TransferSummary backup={backup} targetDeployment={deployment} latestBackupInTargetDeployment={undefined} team={team} /> <ImportSummary snapshotImportCheckpoints={snapshotImportCheckpoints} /> </Modal> )} </> ); } export function BackupRestoreOngoing({ progressMessage, }: { progressMessage: string; }) { return ( <div className="flex min-h-16 flex-col flex-wrap justify-center gap-2 rounded-sm border bg-background-secondary px-4 py-2 text-sm"> <div className="flex flex-wrap justify-end gap-4"> <div className="grow font-semibold">Restoring from a backup</div> <div className="min-w-56 text-right text-content-secondary"> {progressMessage} </div> </div> <ProgressBar fraction={undefined} ariaLabel="In progress" /> </div> ); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/get-convex/convex-backend'

If you have feedback or need assistance with the MCP directory API, please join our Discord server