Fix SSE connection handling and optimize Dockerfile
- Fixed SSE connection not being properly closed when pod logs dialog is closed - Added proper cleanup for EventSource connections in K8sResourceList.tsx - Added debugging logs to track SSE connection lifecycle - Optimized Dockerfile to avoid copying frontend files during Go build stage - Fixed backend handler to properly use context from request for log streaming 🤖 Generated with [Qoder][https://qoder.com]
This commit is contained in:
@@ -76,6 +76,7 @@ export default function K8sResourceList() {
|
||||
const [logsDialogOpen, setLogsDialogOpen] = useState(false)
|
||||
const [logs, setLogs] = useState<string[]>([])
|
||||
const [selectedPod, setSelectedPod] = useState<{ name: string; namespace: string } | null>(null)
|
||||
const eventSourceRef = useRef<EventSource | null>(null)
|
||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false)
|
||||
const [deleteTarget, setDeleteTarget] = useState<{ name: string; namespace: string } | null>(null)
|
||||
const [deleting, setDeleting] = useState(false)
|
||||
@@ -98,6 +99,17 @@ export default function K8sResourceList() {
|
||||
}
|
||||
}, [selectedKind, namespace, nameFilter])
|
||||
|
||||
// Clean up SSE connection on component unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (eventSourceRef.current) {
|
||||
console.log('Cleaning up SSE connection on component unmount')
|
||||
eventSourceRef.current.close()
|
||||
eventSourceRef.current = null
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
const fetchKubeconfig = async () => {
|
||||
try {
|
||||
const res = await fetch('/api/v1/k8s/config')
|
||||
@@ -171,24 +183,62 @@ export default function K8sResourceList() {
|
||||
}
|
||||
|
||||
const handleViewLogs = (podName: string, podNamespace: string) => {
|
||||
console.log('handleViewLogs called with:', { podName, podNamespace })
|
||||
setSelectedPod({ name: podName, namespace: podNamespace })
|
||||
setLogs([])
|
||||
setLogsDialogOpen(true)
|
||||
|
||||
// Close any existing connection
|
||||
if (eventSourceRef.current) {
|
||||
console.log('Closing existing EventSource connection')
|
||||
eventSourceRef.current.close()
|
||||
eventSourceRef.current = null
|
||||
}
|
||||
|
||||
const eventSource = new EventSource(
|
||||
`/api/v1/k8s/pod/logs?name=${encodeURIComponent(podName)}&namespace=${encodeURIComponent(podNamespace)}&tail=1000&follow=true`
|
||||
)
|
||||
|
||||
// Save reference to the EventSource
|
||||
eventSourceRef.current = eventSource
|
||||
|
||||
eventSource.onmessage = (event) => {
|
||||
setLogs((prev) => [...prev, event.data])
|
||||
setTimeout(() => logsEndRef.current?.scrollIntoView({ behavior: 'smooth' }), 100)
|
||||
}
|
||||
// Listen for the specific event type 'pod-logs'
|
||||
eventSource.addEventListener('pod-logs', (event: MessageEvent) => {
|
||||
try {
|
||||
const message = JSON.parse(event.data)
|
||||
if (message.type === 'log') {
|
||||
setLogs((prev) => [...prev, message.data])
|
||||
setTimeout(() => logsEndRef.current?.scrollIntoView({ behavior: 'smooth' }), 100)
|
||||
} else if (message.type === 'EOF') {
|
||||
// Handle end of stream if needed
|
||||
} else if (message.type === 'error') {
|
||||
setLogs((prev) => [...prev, `Error: ${message.data}`])
|
||||
}
|
||||
} catch (e) {
|
||||
// If parsing fails, treat as plain text (fallback)
|
||||
setLogs((prev) => [...prev, event.data])
|
||||
setTimeout(() => logsEndRef.current?.scrollIntoView({ behavior: 'smooth' }), 100)
|
||||
}
|
||||
})
|
||||
|
||||
eventSource.onerror = () => {
|
||||
eventSource.close()
|
||||
console.log('EventSource error occurred')
|
||||
if (eventSourceRef.current) {
|
||||
eventSourceRef.current.close()
|
||||
eventSourceRef.current = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return () => eventSource.close()
|
||||
const handleCloseLogsDialog = () => {
|
||||
console.log('handleCloseLogsDialog called')
|
||||
// Close the EventSource connection if it exists
|
||||
if (eventSourceRef.current) {
|
||||
console.log('Closing EventSource connection')
|
||||
eventSourceRef.current.close()
|
||||
eventSourceRef.current = null
|
||||
}
|
||||
setLogsDialogOpen(false)
|
||||
}
|
||||
|
||||
const handleDeleteResource = async () => {
|
||||
@@ -843,7 +893,7 @@ export default function K8sResourceList() {
|
||||
|
||||
<Dialog
|
||||
open={logsDialogOpen}
|
||||
onClose={() => setLogsDialogOpen(false)}
|
||||
onClose={handleCloseLogsDialog}
|
||||
maxWidth="lg"
|
||||
fullWidth
|
||||
>
|
||||
@@ -852,7 +902,7 @@ export default function K8sResourceList() {
|
||||
<Typography variant="h6">
|
||||
Pod 日志: {selectedPod?.name} ({selectedPod?.namespace})
|
||||
</Typography>
|
||||
<IconButton onClick={() => setLogsDialogOpen(false)}>
|
||||
<IconButton onClick={handleCloseLogsDialog}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user