feat: machines logs viewer
This commit is contained in:
@@ -1,9 +1,17 @@
|
||||
"use client";
|
||||
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import type { getMachines } from "@/server/curdMachine";
|
||||
import { Check, CircleOff, SatelliteDish } from "lucide-react";
|
||||
import React, { useEffect } from "react";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import useWebSocket, { ReadyState } from "react-use-websocket";
|
||||
import { create } from "zustand";
|
||||
|
||||
@@ -16,6 +24,12 @@ type State = {
|
||||
data: any;
|
||||
};
|
||||
}[];
|
||||
logs: {
|
||||
machine_id: string;
|
||||
logs: string;
|
||||
timestamp: number;
|
||||
}[];
|
||||
addLogs: (id: string, logs: string) => void;
|
||||
addData: (
|
||||
id: string,
|
||||
json: {
|
||||
@@ -27,6 +41,13 @@ type State = {
|
||||
|
||||
export const useStore = create<State>((set) => ({
|
||||
data: [],
|
||||
logs: [],
|
||||
addLogs(id, logs) {
|
||||
set((state) => ({
|
||||
...state,
|
||||
logs: [...state.logs, { machine_id: id, logs, timestamp: Date.now() }],
|
||||
}));
|
||||
},
|
||||
addData: (id, json) =>
|
||||
set((state) => ({
|
||||
...state,
|
||||
@@ -54,7 +75,14 @@ function MachineWS({
|
||||
}: {
|
||||
machine: Awaited<ReturnType<typeof getMachines>>[0];
|
||||
}) {
|
||||
const { addData } = useStore();
|
||||
const { addData, addLogs } = useStore();
|
||||
const logs = useStore((x) =>
|
||||
x.logs
|
||||
.filter((p) => p.machine_id === machine.id)
|
||||
.sort((a, b) => a.timestamp - b.timestamp)
|
||||
);
|
||||
const [sid, setSid] = useState("");
|
||||
|
||||
const wsEndpoint = machine.endpoint.replace(/^http/, "ws");
|
||||
const { lastMessage, readyState } = useWebSocket(
|
||||
`${wsEndpoint}/comfyui-deploy/ws`,
|
||||
@@ -62,6 +90,9 @@ function MachineWS({
|
||||
shouldReconnect: () => true,
|
||||
reconnectAttempts: 20,
|
||||
reconnectInterval: 1000,
|
||||
// queryParams: {
|
||||
// clientId: sid,
|
||||
// },
|
||||
}
|
||||
);
|
||||
|
||||
@@ -75,20 +106,72 @@ function MachineWS({
|
||||
[ReadyState.UNINSTANTIATED]: "Uninstantiated",
|
||||
}[readyState];
|
||||
|
||||
const container = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!lastMessage?.data) return;
|
||||
|
||||
const message = JSON.parse(lastMessage.data);
|
||||
console.log(message.event, message);
|
||||
|
||||
if (message.data.sid) {
|
||||
setSid(message.data.sid);
|
||||
}
|
||||
|
||||
if (message.data?.prompt_id) {
|
||||
addData(message.data.prompt_id, message);
|
||||
}
|
||||
|
||||
if (message.event === "LOGS") {
|
||||
addLogs(machine.id, message.data);
|
||||
}
|
||||
}, [lastMessage]);
|
||||
|
||||
useEffect(() => {
|
||||
// console.log(logs.length, container.current);
|
||||
if (container.current) {
|
||||
const scrollHeight = container.current.scrollHeight;
|
||||
|
||||
container.current.scrollTo({
|
||||
top: scrollHeight,
|
||||
behavior: "smooth",
|
||||
});
|
||||
}
|
||||
}, [logs.length]);
|
||||
|
||||
return (
|
||||
<Badge className="text-sm flex gap-2 font-normal" variant="outline">
|
||||
{machine.name} {connectionStatus}
|
||||
</Badge>
|
||||
<Dialog>
|
||||
<DialogTrigger asChild className="">
|
||||
<Badge className="text-sm flex gap-2 font-normal" variant="outline">
|
||||
{machine.name} {connectionStatus}
|
||||
</Badge>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-w-3xl max-h-full">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Machine Logs</DialogTitle>
|
||||
<DialogDescription>
|
||||
You can view your run's outputs here
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div
|
||||
ref={(ref) => {
|
||||
if (!container.current && ref) {
|
||||
const scrollHeight = ref.scrollHeight;
|
||||
|
||||
ref.scrollTo({
|
||||
top: scrollHeight,
|
||||
behavior: "instant",
|
||||
});
|
||||
}
|
||||
container.current = ref;
|
||||
}}
|
||||
className="flex flex-col text-xs p-2 overflow-y-scroll max-h-[400px] whitespace-break-spaces"
|
||||
>
|
||||
{logs.map((x, i) => (
|
||||
<div key={i}>{x.logs}</div>
|
||||
))}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
import { RunInputs } from "@/components/RunInputs";
|
||||
import { LiveStatus } from "./LiveStatus";
|
||||
import { RunInputs } from "@/components/RunInputs";
|
||||
import { RunOutputs } from "@/components/RunOutputs";
|
||||
import {
|
||||
Dialog,
|
||||
@@ -22,10 +21,7 @@ export async function RunDisplay({
|
||||
}) {
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger
|
||||
asChild
|
||||
className="appearance-none hover:cursor-pointer"
|
||||
>
|
||||
<DialogTrigger asChild className="appearance-none hover:cursor-pointer">
|
||||
<TableRow>
|
||||
<TableCell>{run.number}</TableCell>
|
||||
<TableCell className="font-medium">{run.machine?.name}</TableCell>
|
||||
@@ -42,7 +38,7 @@ export async function RunDisplay({
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="max-h-96 overflow-y-scroll">
|
||||
<RunInputs run={run}/>
|
||||
<RunInputs run={run} />
|
||||
<Suspense>
|
||||
<RunOutputs run_id={run.id} />
|
||||
</Suspense>
|
||||
|
||||
@@ -39,7 +39,7 @@ export async function RunsTable(props: { workflow_id: string }) {
|
||||
export async function DeploymentsTable(props: { workflow_id: string }) {
|
||||
const allRuns = await findAllDeployments(props.workflow_id);
|
||||
return (
|
||||
<div className="overflow-auto h-fit lg:h-[400px] w-full">
|
||||
<div className="overflow-auto h-fit w-full">
|
||||
<Table className="">
|
||||
<TableCaption>A list of your deployments</TableCaption>
|
||||
<TableHeader className="bg-background top-0 sticky">
|
||||
|
||||
Reference in New Issue
Block a user