feat: add s3 localstack, upload api

This commit is contained in:
BennyKok
2023-12-13 17:14:55 +08:00
parent f9ed8145d2
commit 0835d966f1
34 changed files with 1112 additions and 227 deletions
+16 -5
View File
@@ -56,6 +56,7 @@ import {
import { ArrowUpDown, MoreHorizontal } from "lucide-react";
import * as React from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
export type Machine = {
@@ -159,9 +160,8 @@ export const columns: ColumnDef<Machine>[] = [
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem
className="text-destructive"
onClick={() => {
deleteMachine(workflow.id);
// navigator.clipboard.writeText(payment.id)
onClick={async () => {
callServerWithToast(await deleteMachine(workflow.id));
}}
>
Delete Machine
@@ -176,6 +176,17 @@ export const columns: ColumnDef<Machine>[] = [
},
];
async function callServerWithToast(result: {
message: string;
error?: boolean;
}) {
if (result.error) {
toast.error(result.message);
} else {
toast.success(result.message);
}
}
export function MachineList({ data }: { data: Machine[] }) {
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
@@ -333,8 +344,8 @@ function AddMachinesDialog() {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
name: "",
endpoint: "",
name: "My Local Machine",
endpoint: "http://127.0.0.1:8188",
},
});
+2 -1
View File
@@ -8,6 +8,7 @@ import { create } from "zustand";
type State = {
data: {
id: string;
timestamp: number;
json: {
event: string;
data: any;
@@ -27,7 +28,7 @@ export const useStore = create<State>((set) => ({
addData: (id, json) =>
set((state) => ({
...state,
data: [...state.data, { id, json }],
data: [...state.data, { id, json, timestamp: Date.now() }],
})),
}));
+109 -13
View File
@@ -1,27 +1,123 @@
"use client";
import type { findAllRuns } from "../app/[workflow_id]/page";
import { StatusBadge } from "../app/[workflow_id]/page";
import { useStore } from "@/components/MachinesWS";
import { TableCell, TableRow } from "@/components/ui/table";
import { StatusBadge } from "@/components/StatusBadge";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { getRelativeTime } from "@/lib/getRelativeTime";
import { type findAllRuns } from "@/server/findAllRuns";
import { getRunsOutput } from "@/server/getRunsOutput";
import { useEffect, useState } from "react";
export function RunDisplay({
run,
}: {
run: Awaited<ReturnType<typeof findAllRuns>>[0];
}) {
const data = useStore((state) => state.data.find((x) => x.id === run.id));
const data = useStore(
(state) =>
state.data
.filter((x) => x.id === run.id)
.sort((a, b) => b.timestamp - a.timestamp)?.[0]
);
let status = run.status;
if (data?.json.event == "executing" && data.json.data.node == undefined) {
status = "success";
} else if (data?.json.event == "executing") {
status = "running";
}
return (
<TableRow>
<TableCell>{run.version.version}</TableCell>
<TableCell className="font-medium">{run.machine.name}</TableCell>
<TableCell>{getRelativeTime(run.created_at)}</TableCell>
<TableCell>{data ? data.json.event : "-"}</TableCell>
<TableCell className="text-right">
<StatusBadge run={run} />
</TableCell>
</TableRow>
<Dialog>
<DialogTrigger asChild className="appearance-none hover:cursor-pointer">
<TableRow>
<TableCell>{run.version?.version}</TableCell>
<TableCell className="font-medium">{run.machine?.name}</TableCell>
<TableCell>{getRelativeTime(run.created_at)}</TableCell>
<TableCell>
{data && status != "success"
? `${data.json.event} - ${data.json.data.node}`
: "-"}
</TableCell>
<TableCell className="text-right">
<StatusBadge status={status} />
</TableCell>
</TableRow>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Run outputs</DialogTitle>
<DialogDescription>
You can view your run&apos;s outputs here
</DialogDescription>
</DialogHeader>
<RunOutputs run_id={run.id} />
</DialogContent>
</Dialog>
);
}
export function RunOutputs({ run_id }: { run_id: string }) {
const [outputs, setOutputs] = useState<
Awaited<ReturnType<typeof getRunsOutput>>
>([]);
useEffect(() => {
getRunsOutput(run_id).then((x) => setOutputs(x));
}, [run_id]);
return (
<Table>
{/* <TableCaption>A list of your recent runs.</TableCaption> */}
<TableHeader className="bg-background top-0 sticky">
<TableRow>
<TableHead className="w-[100px]">File</TableHead>
<TableHead className="">Output</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{outputs?.map((run) => {
const fileName = run.data.images[0].filename;
// const filePath
return (
<TableRow key={run.id}>
<TableCell>{fileName}</TableCell>
<TableCell>
<OutputRender run_id={run_id} filename={fileName} />
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
);
}
export function OutputRender(props: { run_id: string; filename: string }) {
if (props.filename.endsWith(".png")) {
return (
<img
alt={props.filename}
src={`api/view?file=${encodeURIComponent(
`outputs/runs/${props.run_id}/${props.filename}`
)}`}
/>
);
}
}
+35
View File
@@ -0,0 +1,35 @@
import { findAllRuns } from "../server/findAllRuns";
import { RunDisplay } from "./RunDisplay";
import {
Table,
TableBody,
TableCaption,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
export async function RunsTable(props: { workflow_id: string }) {
const allRuns = await findAllRuns(props.workflow_id);
return (
<div className="overflow-auto h-[400px] w-full">
<Table className="">
<TableCaption>A list of your recent runs.</TableCaption>
<TableHeader className="bg-background top-0 sticky">
<TableRow>
<TableHead className=" w-[100px]">Version</TableHead>
<TableHead className="">Machine</TableHead>
<TableHead className="">Time</TableHead>
<TableHead className="">Live Status</TableHead>
<TableHead className=" text-right">Status</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{allRuns.map((run) => (
<RunDisplay run={run} key={run.id} />
))}
</TableBody>
</Table>
</div>
);
}
+23
View File
@@ -0,0 +1,23 @@
import type { findAllRuns } from "../server/findAllRuns";
import { LoadingIcon } from "@/components/LoadingIcon";
import { Badge } from "@/components/ui/badge";
export function StatusBadge({
status,
}: {
status: Awaited<ReturnType<typeof findAllRuns>>[0]["status"];
}) {
switch (status) {
case "running":
return (
<Badge variant="secondary">
{status} <LoadingIcon />
</Badge>
);
case "success":
return <Badge variant="success">{status}</Badge>;
case "failed":
return <Badge variant="destructive">{status}</Badge>;
}
return <Badge variant="secondary">{status}</Badge>;
}
+3 -2
View File
@@ -1,6 +1,5 @@
"use client";
import type { findFirstTableWithVersion } from "@/app/[workflow_id]/page";
import { LoadingIcon } from "@/components/LoadingIcon";
import { Button } from "@/components/ui/button";
import {
@@ -14,6 +13,7 @@ import {
} from "@/components/ui/select";
import { createRun } from "@/server/createRun";
import type { getMachines } from "@/server/curdMachine";
import type { findFirstTableWithVersion } from "@/server/findFirstTableWithVersion";
import { Play } from "lucide-react";
import { parseAsInteger, useQueryState } from "next-usequerystate";
import { useState } from "react";
@@ -56,7 +56,7 @@ export function MachineSelect({
machines: Awaited<ReturnType<typeof getMachines>>;
}) {
const [machine, setMachine] = useQueryState("machine", {
defaultValue: machines[0].id ?? "",
defaultValue: machines?.[0].id ?? "",
});
return (
<Select
@@ -111,6 +111,7 @@ export function RunWorkflowButton({
try {
const origin = window.location.origin;
await createRun(origin, workflow_version_id, machine);
// console.log(res.json());
setIsLoading(false);
} catch (error) {
setIsLoading(false);
+26 -27
View File
@@ -1,28 +1,27 @@
import * as React from "react"
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
import * as React from "react";
const Table = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className="relative w-full overflow-auto">
<table
ref={ref}
className={cn("w-full caption-bottom text-sm", className)}
{...props}
/>
</div>
))
Table.displayName = "Table"
// <div className="relative w-full overflow-auto">
<table
ref={ref}
className={cn("w-full caption-bottom text-sm", className)}
{...props}
/>
// </div>
));
Table.displayName = "Table";
const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
))
TableHeader.displayName = "TableHeader"
));
TableHeader.displayName = "TableHeader";
const TableBody = React.forwardRef<
HTMLTableSectionElement,
@@ -33,8 +32,8 @@ const TableBody = React.forwardRef<
className={cn("[&_tr:last-child]:border-0", className)}
{...props}
/>
))
TableBody.displayName = "TableBody"
));
TableBody.displayName = "TableBody";
const TableFooter = React.forwardRef<
HTMLTableSectionElement,
@@ -48,8 +47,8 @@ const TableFooter = React.forwardRef<
)}
{...props}
/>
))
TableFooter.displayName = "TableFooter"
));
TableFooter.displayName = "TableFooter";
const TableRow = React.forwardRef<
HTMLTableRowElement,
@@ -63,8 +62,8 @@ const TableRow = React.forwardRef<
)}
{...props}
/>
))
TableRow.displayName = "TableRow"
));
TableRow.displayName = "TableRow";
const TableHead = React.forwardRef<
HTMLTableCellElement,
@@ -78,8 +77,8 @@ const TableHead = React.forwardRef<
)}
{...props}
/>
))
TableHead.displayName = "TableHead"
));
TableHead.displayName = "TableHead";
const TableCell = React.forwardRef<
HTMLTableCellElement,
@@ -90,8 +89,8 @@ const TableCell = React.forwardRef<
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
{...props}
/>
))
TableCell.displayName = "TableCell"
));
TableCell.displayName = "TableCell";
const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
@@ -102,8 +101,8 @@ const TableCaption = React.forwardRef<
className={cn("mt-4 text-sm text-muted-foreground", className)}
{...props}
/>
))
TableCaption.displayName = "TableCaption"
));
TableCaption.displayName = "TableCaption";
export {
Table,
@@ -114,4 +113,4 @@ export {
TableRow,
TableCell,
TableCaption,
}
};