Hydrate terminal drawer with current log history
This commit is contained in:
@@ -95,6 +95,9 @@ export function ServerManagementTerminalDrawer({ open, serverId, serverName, onC
|
||||
const serverTimeRef = useRef<string | undefined>(undefined);
|
||||
const initialHistoryPendingRef = useRef(false);
|
||||
const liveSessionRef = useRef<string | null | undefined>(undefined);
|
||||
const liveStreamsRef = useRef<LogStreamResponse[]>([]);
|
||||
const hydratedLiveStreamKeysRef = useRef<Set<string>>(new Set());
|
||||
const liveHistoryRequestRef = useRef(0);
|
||||
const historyRequestRef = useRef(0);
|
||||
|
||||
const appendLines = useCallback((incoming: TerminalLine[]) => {
|
||||
@@ -128,6 +131,9 @@ export function ServerManagementTerminalDrawer({ open, serverId, serverName, onC
|
||||
setHistoryLines({ status: "idle" });
|
||||
setSelectedHistoryStreamId("");
|
||||
liveSessionRef.current = undefined;
|
||||
liveStreamsRef.current = [];
|
||||
hydratedLiveStreamKeysRef.current = new Set();
|
||||
liveHistoryRequestRef.current += 1;
|
||||
serverTimeRef.current = undefined;
|
||||
historyRequestRef.current += 1;
|
||||
initialHistoryPendingRef.current = true;
|
||||
@@ -145,6 +151,29 @@ export function ServerManagementTerminalDrawer({ open, serverId, serverName, onC
|
||||
return () => window.cancelAnimationFrame(frame);
|
||||
}, [lines, open]);
|
||||
|
||||
const hydrateCurrentSessionHistory = useCallback((sessionId: string | null | undefined) => {
|
||||
if (!sessionId) return;
|
||||
const streamsToHydrate = liveStreamsRef.current.filter((stream) => eventBelongsToLiveSession(stream.logSessionId, sessionId) && stream.latestSeq > 0 && !hydratedLiveStreamKeysRef.current.has(liveHistoryStreamKey(sessionId, stream)));
|
||||
if (streamsToHydrate.length === 0) return;
|
||||
const requestId = liveHistoryRequestRef.current + 1;
|
||||
liveHistoryRequestRef.current = requestId;
|
||||
for (const stream of streamsToHydrate) hydratedLiveStreamKeysRef.current.add(liveHistoryStreamKey(sessionId, stream));
|
||||
void Promise.all(streamsToHydrate.map(async (stream) => {
|
||||
const response = await platformApiClient.queryLogStream({ logStreamId: stream.id, afterSeq: Math.max(0, stream.latestSeq - terminalHistoryWindow), limit: terminalHistoryWindow });
|
||||
return response.entries.map((entry) => terminalLineFromLog(stream, entry));
|
||||
})).then((lineGroups) => {
|
||||
if (liveHistoryRequestRef.current !== requestId || liveSessionRef.current !== sessionId) return;
|
||||
const historicalLines = lineGroups.flat();
|
||||
if (historicalLines.length === 0) return;
|
||||
appendLines(historicalLines);
|
||||
lockTerminalFollow();
|
||||
}).catch(() => {
|
||||
if (liveHistoryRequestRef.current !== requestId || liveSessionRef.current !== sessionId) return;
|
||||
appendLines([terminalSystemLine("warn", "当前会话历史读取失败,继续等待实时输出。", "SYSTEM", `session-history-failed-${sessionId}`, serverTimeRef.current)]);
|
||||
lockTerminalFollow();
|
||||
});
|
||||
}, [appendLines, lockTerminalFollow]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return undefined;
|
||||
let ready = false;
|
||||
@@ -159,6 +188,9 @@ export function ServerManagementTerminalDrawer({ open, serverId, serverName, onC
|
||||
liveSessionRef.current = nextSessionId;
|
||||
setLiveSessionId(nextSessionId);
|
||||
if (previousSessionId === nextSessionId) return;
|
||||
liveStreamsRef.current = [];
|
||||
hydratedLiveStreamKeysRef.current = new Set();
|
||||
liveHistoryRequestRef.current += 1;
|
||||
setStreams({ status: "ready", data: [] });
|
||||
setLines(nextSessionId
|
||||
? [terminalSystemLine("info", previousSessionId === undefined ? "已跟随当前受管进程输出会话。" : "Run 已切换到新的受管进程输出会话。", "SYSTEM", `session-${nextSessionId}`, serverTimeRef.current)]
|
||||
@@ -169,11 +201,13 @@ export function ServerManagementTerminalDrawer({ open, serverId, serverName, onC
|
||||
const stream = parseLogStreamEvent(event);
|
||||
if (!stream || !eventBelongsToLiveSession(stream.logSessionId, liveSessionRef.current)) return;
|
||||
ready = true;
|
||||
liveStreamsRef.current = mergeLogStreams(liveStreamsRef.current, stream);
|
||||
setStreams((current) => ({ status: "ready", data: mergeLogStreams(current.status === "ready" ? current.data : [], stream) }));
|
||||
});
|
||||
events.addEventListener("ready", () => {
|
||||
ready = true;
|
||||
setStreams((current) => current.status === "ready" ? current : { status: "ready", data: [] });
|
||||
hydrateCurrentSessionHistory(liveSessionRef.current);
|
||||
lockTerminalFollow();
|
||||
});
|
||||
events.addEventListener("log", (event) => {
|
||||
@@ -181,6 +215,7 @@ export function ServerManagementTerminalDrawer({ open, serverId, serverName, onC
|
||||
if (!payload || !eventBelongsToLiveSession(payload.logSessionId, liveSessionRef.current)) return;
|
||||
ready = true;
|
||||
const stream = streamFromServerLogEvent(payload);
|
||||
liveStreamsRef.current = mergeLogStreams(liveStreamsRef.current, stream);
|
||||
setStreams((current) => ({ status: "ready", data: mergeLogStreams(current.status === "ready" ? current.data : [], stream) }));
|
||||
appendLines([terminalLineFromLog(stream, payload.entry)]);
|
||||
});
|
||||
@@ -188,7 +223,7 @@ export function ServerManagementTerminalDrawer({ open, serverId, serverName, onC
|
||||
if (!ready) setStreams({ status: "error", reason: "实时日志推送连接失败" });
|
||||
};
|
||||
return () => events.close();
|
||||
}, [appendLines, lockTerminalFollow, open, serverId]);
|
||||
}, [appendLines, hydrateCurrentSessionHistory, lockTerminalFollow, open, serverId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open || !historyOpen) return;
|
||||
@@ -381,6 +416,10 @@ function eventBelongsToLiveSession(eventSessionId: string | undefined, liveSessi
|
||||
return Boolean(normalizedEventSessionId && normalizedEventSessionId === liveSessionId);
|
||||
}
|
||||
|
||||
function liveHistoryStreamKey(sessionId: string, stream: LogStreamResponse): string {
|
||||
return `${sessionId}:${stream.id}`;
|
||||
}
|
||||
|
||||
function mergeTerminalLines(current: TerminalLine[], incoming: TerminalLine[]): TerminalLine[] {
|
||||
const seen = new Set(current.map((line) => line.id));
|
||||
const merged = [...current];
|
||||
|
||||
Reference in New Issue
Block a user