import copy from "copy-to-clipboard"; import { Check, Copy, Loader2 } from "lucide-react"; import { useEffect, useRef, useState } from "react"; import { AnalyzeLogs } from "@/components/dashboard/docker/logs/analyze-logs"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { TerminalLine } from "../../docker/logs/terminal-line"; import { type LogLine, parseLogs } from "../../docker/logs/utils"; interface Props { logPath: string | null; open: boolean; onClose: () => void; serverId?: string; errorMessage?: string; } export const ShowDeployment = ({ logPath, open, onClose, serverId, errorMessage, }: Props) => { const [data, setData] = useState(""); const [showExtraLogs, setShowExtraLogs] = useState(false); const [filteredLogs, setFilteredLogs] = useState([]); const wsRef = useRef(null); const [autoScroll, setAutoScroll] = useState(true); const scrollRef = useRef(null); const [copied, setCopied] = useState(false); const scrollToBottom = () => { if (autoScroll && scrollRef.current) { scrollRef.current.scrollTop = scrollRef.current.scrollHeight; } }; const handleScroll = () => { if (!scrollRef.current) return; const { scrollTop, scrollHeight, clientHeight } = scrollRef.current; const isAtBottom = Math.abs(scrollHeight - scrollTop - clientHeight) < 10; setAutoScroll(isAtBottom); }; useEffect(() => { if (!open || !logPath) return; setData(""); const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; const wsUrl = `${protocol}//${window.location.host}/listen-deployment?logPath=${logPath}${serverId ? `&serverId=${serverId}` : ""}`; const ws = new WebSocket(wsUrl); wsRef.current = ws; // Store WebSocket instance in ref ws.onmessage = (e) => { setData((currentData) => currentData + e.data); }; ws.onerror = (error) => { console.error("WebSocket error: ", error); }; ws.onclose = () => { wsRef.current = null; // Clear reference on close }; return () => { if (wsRef.current?.readyState === WebSocket.OPEN) { ws.close(); wsRef.current = null; } }; }, [logPath, open]); useEffect(() => { const logs = parseLogs(data); let filteredLogsResult = logs; if (serverId) { let hideSubsequentLogs = false; filteredLogsResult = logs.filter((log) => { if ( log.message.includes( "===================================EXTRA LOGS============================================", ) ) { hideSubsequentLogs = true; return showExtraLogs; } return showExtraLogs ? true : !hideSubsequentLogs; }); } setFilteredLogs(filteredLogsResult); }, [data, showExtraLogs]); useEffect(() => { scrollToBottom(); if (autoScroll && scrollRef.current) { scrollRef.current.scrollTop = scrollRef.current.scrollHeight; } }, [filteredLogs, autoScroll]); const handleCopy = () => { const logContent = filteredLogs .map(({ timestamp, message }: LogLine) => `${timestamp?.toISOString() || ""} ${message}`.trim(), ) .join("\n"); const success = copy(logContent); if (success) { setCopied(true); setTimeout(() => setCopied(false), 2000); } }; const optionalErrors = parseLogs(errorMessage || ""); return ( { onClose(); if (!e) { setData(""); } if (wsRef.current) { if (wsRef.current.readyState === WebSocket.OPEN) { wsRef.current.close(); } } }} > Deployment See all the details of this deployment |{" "} {filteredLogs.length} lines {serverId && (
setShowExtraLogs(checked as boolean) } />
)}
{" "} {filteredLogs.length > 0 ? ( filteredLogs.map((log: LogLine, index: number) => ( )) ) : ( <> {optionalErrors.length > 0 ? ( optionalErrors.map((log: LogLine, index: number) => ( )) ) : (
)} )}
); };