Skip to content

Commit b1fb2d6

Browse files
committed
fix: prevent paused sandbox from auto-resuming via live inspect connections
Optimistically flip sandbox state to paused on pause so the terminal PTY and filesystem watcher tear down immediately instead of auto-resuming the sandbox with their envd traffic. Also show the paused empty-state for the filesystem instead of the stale cached tree.
1 parent 798f5c3 commit b1fb2d6

2 files changed

Lines changed: 26 additions & 9 deletions

File tree

src/features/dashboard/sandbox/header/pause-button.tsx

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
'use client'
22

3-
import { useMutation } from '@tanstack/react-query'
3+
import { useMutation, useQueryClient } from '@tanstack/react-query'
44
import { useState } from 'react'
55
import { toast } from 'sonner'
6+
import { useRouteParams } from '@/lib/hooks/use-route-params'
67
import { useTRPC } from '@/trpc/client'
78
import { AlertPopover } from '@/ui/alert-popover'
89
import { Button } from '@/ui/primitives/button'
910
import { PausedIcon } from '@/ui/primitives/icons'
10-
import { useDashboard } from '../../context'
1111
import { useSandboxContext } from '../context'
1212

1313
interface PauseButtonProps {
@@ -17,28 +17,45 @@ interface PauseButtonProps {
1717
export default function PauseButton({ className }: PauseButtonProps) {
1818
const [open, setOpen] = useState(false)
1919
const { sandboxInfo, refetchSandboxInfo } = useSandboxContext()
20-
const { team } = useDashboard()
20+
const { teamSlug, sandboxId } =
21+
useRouteParams<'/dashboard/[teamSlug]/sandboxes/[sandboxId]'>()
2122
const trpc = useTRPC()
23+
const queryClient = useQueryClient()
2224

2325
const canPause = sandboxInfo?.state === 'running'
26+
const detailsKey = trpc.sandbox.details.queryKey({ teamSlug, sandboxId })
2427

2528
const { mutate: pause, isPending } = useMutation(
2629
trpc.sandbox.pause.mutationOptions({
30+
// Optimistically mark the sandbox as paused so the live terminal/filesystem
31+
// connections tear down immediately. Otherwise their envd traffic
32+
// auto-resumes the sandbox while the pause snapshot is being created.
33+
onMutate: async () => {
34+
await queryClient.cancelQueries({ queryKey: detailsKey })
35+
const previous = queryClient.getQueryData(detailsKey)
36+
queryClient.setQueryData(detailsKey, (old) =>
37+
old?.state === 'running' ? { ...old, state: 'paused' as const } : old
38+
)
39+
return { previous }
40+
},
41+
onError: (_error, _variables, context) => {
42+
if (context?.previous !== undefined) {
43+
queryClient.setQueryData(detailsKey, context.previous)
44+
}
45+
toast.error('Failed to pause sandbox. Please try again.')
46+
},
2747
onSuccess: async () => {
2848
toast.success('Sandbox paused successfully')
2949
setOpen(false)
3050
refetchSandboxInfo()
3151
},
32-
onError: () => {
33-
toast.error('Failed to pause sandbox. Please try again.')
34-
},
3552
})
3653
)
3754

3855
const handlePause = () => {
3956
if (!canPause || !sandboxInfo?.sandboxID) return
4057

41-
pause({ teamSlug: team.slug, sandboxId: sandboxInfo.sandboxID })
58+
pause({ teamSlug, sandboxId: sandboxInfo.sandboxID })
4259
}
4360

4461
if (!canPause) return null

src/features/dashboard/sandbox/inspect/filesystem.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export default function SandboxInspectFilesystem({
3030

3131
return (
3232
<div className="h-full flex-1 flex flex-col gap-1 overflow-hidden">
33-
<StoppedBanner rootNodeCount={children.length} />
33+
{isRunning && <StoppedBanner rootNodeCount={children.length} />}
3434
<SandboxInspectFrame
3535
initial={{
3636
flex: 1,
@@ -41,7 +41,7 @@ export default function SandboxInspectFilesystem({
4141
<div className="h-full flex-1 overflow-hidden">
4242
{showRootLoading ? (
4343
<LoadingLayout />
44-
) : !children.length ? (
44+
) : !isRunning || !children.length ? (
4545
<SandboxInspectNotFound
4646
isResumePending={isSandboxResumePending}
4747
onResumeSandbox={() => void resumeSandbox()}

0 commit comments

Comments
 (0)