diff --git a/apps/dokploy/components/dashboard/docker/logs/docker-logs-id.tsx b/apps/dokploy/components/dashboard/docker/logs/docker-logs-id.tsx index 611af355e..54db7945b 100644 --- a/apps/dokploy/components/dashboard/docker/logs/docker-logs-id.tsx +++ b/apps/dokploy/components/dashboard/docker/logs/docker-logs-id.tsx @@ -1,13 +1,14 @@ +import { Download as DownloadIcon, Loader2, Pause, Play } from "lucide-react"; +import React, { useEffect, useRef } from "react"; +import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; -import { Download as DownloadIcon, Loader2 } from "lucide-react"; -import React, { useEffect, useRef } from "react"; import { LineCountFilter } from "./line-count-filter"; import { SinceLogsFilter, type TimeFilter } from "./since-logs-filter"; import { StatusLogsFilter } from "./status-logs-filter"; import { TerminalLine } from "./terminal-line"; -import { type LogLine, getLogType, parseLogs } from "./utils"; +import { getLogType, type LogLine, parseLogs } from "./utils"; interface Props { containerId: string; @@ -61,6 +62,9 @@ export const DockerLogsId: React.FC = ({ const [showTimestamp, setShowTimestamp] = React.useState(true); const [since, setSince] = React.useState("all"); const [typeFilter, setTypeFilter] = React.useState([]); + const [isPaused, setIsPaused] = React.useState(false); + const [messageBuffer, setMessageBuffer] = React.useState([]); + const isPausedRef = useRef(false); const scrollRef = useRef(null); const [isLoading, setIsLoading] = React.useState(false); @@ -85,15 +89,38 @@ export const DockerLogsId: React.FC = ({ const handleLines = (lines: number) => { setRawLogs(""); setFilteredLogs([]); + setMessageBuffer([]); setLines(lines); }; const handleSince = (value: TimeFilter) => { setRawLogs(""); setFilteredLogs([]); + setMessageBuffer([]); setSince(value); }; + const handlePauseResume = () => { + if (isPaused) { + // Resume: Apply all buffered messages + if (messageBuffer.length > 0) { + const bufferedContent = messageBuffer.join(""); + setRawLogs((prev) => { + const updated = prev + bufferedContent; + const splitLines = updated.split("\n"); + if (splitLines.length > lines) { + return splitLines.slice(-lines).join("\n"); + } + return updated; + }); + setMessageBuffer([]); + } + } + const newPausedState = !isPaused; + setIsPaused(newPausedState); + isPausedRef.current = newPausedState; + }; + useEffect(() => { if (!containerId) return; @@ -102,6 +129,10 @@ export const DockerLogsId: React.FC = ({ setIsLoading(true); setRawLogs(""); setFilteredLogs([]); + setMessageBuffer([]); + // Reset pause state when container changes + setIsPaused(false); + isPausedRef.current = false; const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; const params = new globalThis.URLSearchParams({ @@ -140,14 +171,22 @@ export const DockerLogsId: React.FC = ({ ws.onmessage = (e) => { if (!isCurrentConnection) return; - setRawLogs((prev) => { - const updated = prev + e.data; - const splitLines = updated.split("\n"); - if (splitLines.length > lines) { - return splitLines.slice(-lines).join("\n"); - } - return updated; - }); + + if (isPausedRef.current) { + // When paused, buffer the messages instead of displaying them + setMessageBuffer((prev) => [...prev, e.data]); + } else { + // When not paused, display messages normally + setRawLogs((prev) => { + const updated = prev + e.data; + const splitLines = updated.split("\n"); + if (splitLines.length > lines) { + return splitLines.slice(-lines).join("\n"); + } + return updated; + }); + } + setIsLoading(false); if (noDataTimeout) clearTimeout(noDataTimeout); }; @@ -210,9 +249,15 @@ export const DockerLogsId: React.FC = ({ }); }; + // Sync isPausedRef with isPaused state + useEffect(() => { + isPausedRef.current = isPaused; + }, [isPaused]); + useEffect(() => { setRawLogs(""); setFilteredLogs([]); + setMessageBuffer([]); }, [containerId]); useEffect(() => { @@ -260,17 +305,48 @@ export const DockerLogsId: React.FC = ({ /> - +
+ + +
+ {isPaused && ( + +
+ + + Logs paused + {messageBuffer.length > 0 && ( + + ({messageBuffer.length} messages buffered) + + )} + +
+
+ )}