feat: add s3 localstack, upload api
This commit is contained in:
@@ -1,12 +1,11 @@
|
||||
import { RunDisplay } from "../../components/RunDisplay";
|
||||
import { LoadingIcon } from "@/components/LoadingIcon";
|
||||
import { RunsTable } from "../../components/RunsTable";
|
||||
import { findFirstTableWithVersion } from "../../server/findFirstTableWithVersion";
|
||||
import { MachinesWSMain } from "@/components/MachinesWS";
|
||||
import {
|
||||
MachineSelect,
|
||||
RunWorkflowButton,
|
||||
VersionSelect,
|
||||
} from "@/components/VersionSelect";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
@@ -14,49 +13,8 @@ import {
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCaption,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { db } from "@/db/db";
|
||||
import {
|
||||
workflowRunsTable,
|
||||
workflowTable,
|
||||
workflowVersionTable,
|
||||
} from "@/db/schema";
|
||||
import { getRelativeTime } from "@/lib/getRelativeTime";
|
||||
import { getMachines } from "@/server/curdMachine";
|
||||
import { desc, eq } from "drizzle-orm";
|
||||
|
||||
export async function findFirstTableWithVersion(workflow_id: string) {
|
||||
return await db.query.workflowTable.findFirst({
|
||||
with: { versions: { orderBy: desc(workflowVersionTable.version) } },
|
||||
where: eq(workflowTable.id, workflow_id),
|
||||
});
|
||||
}
|
||||
|
||||
export async function findAllRuns(workflow_id: string) {
|
||||
return await db.query.workflowRunsTable.findMany({
|
||||
where: eq(workflowRunsTable.workflow_id, workflow_id),
|
||||
orderBy: desc(workflowRunsTable.created_at),
|
||||
with: {
|
||||
machine: {
|
||||
columns: {
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
version: {
|
||||
columns: {
|
||||
version: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export default async function Page({
|
||||
params,
|
||||
@@ -69,7 +27,7 @@ export default async function Page({
|
||||
const machines = await getMachines();
|
||||
|
||||
return (
|
||||
<div className="mt-4 w-full flex flex-col lg:flex-row gap-4">
|
||||
<div className="mt-4 w-full flex flex-col lg:flex-row gap-4 max-h-[calc(100dvh-100px)]">
|
||||
<Card className="w-full lg:w-fit lg:min-w-[500px] h-fit">
|
||||
<CardHeader>
|
||||
<CardTitle>{workflow?.name}</CardTitle>
|
||||
@@ -101,46 +59,3 @@ export default async function Page({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
async function RunsTable(props: { workflow_id: string }) {
|
||||
const allRuns = await findAllRuns(props.workflow_id);
|
||||
return (
|
||||
<Table>
|
||||
<TableCaption>A list of your recent runs.</TableCaption>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-[100px]">Version</TableHead>
|
||||
<TableHead>Machine</TableHead>
|
||||
<TableHead>Time</TableHead>
|
||||
<TableHead>Live Status</TableHead>
|
||||
<TableHead className="text-right">Status</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{allRuns.map((run) => (
|
||||
<RunDisplay run={run} key={run.id} />
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
export function StatusBadge({
|
||||
run,
|
||||
}: {
|
||||
run: Awaited<ReturnType<typeof findAllRuns>>[0];
|
||||
}) {
|
||||
switch (run.status) {
|
||||
case "running":
|
||||
return (
|
||||
<Badge variant="secondary">
|
||||
{run.status} <LoadingIcon />
|
||||
</Badge>
|
||||
);
|
||||
case "success":
|
||||
return <Badge variant="success">{run.status}</Badge>;
|
||||
case "failed":
|
||||
return <Badge variant="destructive">{run.status}</Badge>;
|
||||
}
|
||||
return <Badge variant="secondary">{run.status}</Badge>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { parseDataSafe } from "../../../lib/parseDataSafe";
|
||||
import { createRun } from "../../../server/createRun";
|
||||
import { NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
|
||||
const Request = z.object({
|
||||
@@ -8,12 +9,6 @@ const Request = z.object({
|
||||
machine_id: z.string(),
|
||||
});
|
||||
|
||||
export const ComfyAPI_Run = z.object({
|
||||
prompt_id: z.string(),
|
||||
number: z.number(),
|
||||
node_errors: z.any(),
|
||||
});
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const [data, error] = await parseDataSafe(Request, request);
|
||||
if (!data || error) return error;
|
||||
@@ -22,5 +17,29 @@ export async function POST(request: Request) {
|
||||
|
||||
const { workflow_version_id, machine_id } = data;
|
||||
|
||||
return await createRun(origin, workflow_version_id, machine_id);
|
||||
try {
|
||||
const workflow_run_id = await createRun(
|
||||
origin,
|
||||
workflow_version_id,
|
||||
machine_id
|
||||
);
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
workflow_run_id: workflow_run_id,
|
||||
},
|
||||
{
|
||||
status: 200,
|
||||
}
|
||||
);
|
||||
} catch (error: any) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: error.message,
|
||||
},
|
||||
{
|
||||
status: 500,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { parseDataSafe } from "../../../lib/parseDataSafe";
|
||||
import { handleResourceUpload } from "@/server/resource";
|
||||
import { NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
|
||||
const Request = z.object({
|
||||
file_name: z.string(),
|
||||
run_id: z.string(),
|
||||
type: z.enum(["image/png", "image/jpeg"]),
|
||||
});
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export async function GET(request: Request) {
|
||||
const [data, error] = await parseDataSafe(Request, request);
|
||||
if (!data || error) return error;
|
||||
|
||||
const { file_name, run_id, type } = data;
|
||||
|
||||
try {
|
||||
const uploadUrl = await handleResourceUpload({
|
||||
resourceBucket: "comfyui-deploy",
|
||||
resourceId: `outputs/runs/${run_id}/${file_name}`,
|
||||
resourceType: type,
|
||||
isPublic: true,
|
||||
});
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
url: uploadUrl,
|
||||
},
|
||||
{ status: 200 }
|
||||
);
|
||||
} catch (error: unknown) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : "Unknown error";
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: errorMessage,
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,47 @@
|
||||
import { parseDataSafe } from "../../../lib/parseDataSafe";
|
||||
import { db } from "@/db/db";
|
||||
import { workflowRunsTable } from "@/db/schema";
|
||||
import { workflowRunOutputs, workflowRunsTable } from "@/db/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
|
||||
const Request = z.object({
|
||||
run_id: z.string(),
|
||||
status: z.enum(["not-started", "running", "success", "failed"]),
|
||||
status: z.enum(["not-started", "running", "success", "failed"]).optional(),
|
||||
output_data: z.any().optional(),
|
||||
});
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const [data, error] = await parseDataSafe(Request, request);
|
||||
if (!data || error) return error;
|
||||
|
||||
const { run_id, status } = data;
|
||||
const { run_id, status, output_data } = data;
|
||||
|
||||
const workflow_run = await db
|
||||
.update(workflowRunsTable)
|
||||
.set({
|
||||
status: status,
|
||||
})
|
||||
.where(eq(workflowRunsTable.id, run_id))
|
||||
.returning();
|
||||
console.log(run_id, status, output_data);
|
||||
|
||||
const workflow_version = await db.query.workflowVersionTable.findFirst({
|
||||
where: eq(workflowRunsTable.id, workflow_run[0].workflow_version_id),
|
||||
});
|
||||
if (output_data) {
|
||||
const workflow_run_output = await db.insert(workflowRunOutputs).values({
|
||||
run_id: run_id,
|
||||
data: output_data,
|
||||
});
|
||||
} else if (status) {
|
||||
console.log("status", status);
|
||||
const workflow_run = await db
|
||||
.update(workflowRunsTable)
|
||||
.set({
|
||||
status: status,
|
||||
ended_at:
|
||||
status === "success" || status === "failed" ? new Date() : null,
|
||||
})
|
||||
.where(eq(workflowRunsTable.id, run_id))
|
||||
.returning();
|
||||
}
|
||||
|
||||
revalidatePath(`./${workflow_version?.workflow_id}`);
|
||||
// const workflow_version = await db.query.workflowVersionTable.findFirst({
|
||||
// where: eq(workflowRunsTable.id, workflow_run[0].workflow_version_id),
|
||||
// });
|
||||
|
||||
// revalidatePath(`./${workflow_version?.workflow_id}`);
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { parseDataSafe } from "../../../lib/parseDataSafe";
|
||||
import { db } from "@/db/db";
|
||||
import { workflowTable, workflowVersionTable } from "@/db/schema";
|
||||
import { eq, sql } from "drizzle-orm";
|
||||
import { sql } from "drizzle-orm";
|
||||
import { NextResponse } from "next/server";
|
||||
import { ZodFormattedError, z } from "zod";
|
||||
import { z } from "zod";
|
||||
|
||||
const corsHeaders = {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
@@ -36,7 +36,7 @@ export async function POST(request: Request) {
|
||||
const [data, error] = await parseDataSafe(
|
||||
UploadRequest,
|
||||
request,
|
||||
corsHeaders,
|
||||
corsHeaders
|
||||
);
|
||||
|
||||
if (!data || error) return error;
|
||||
@@ -96,7 +96,7 @@ export async function POST(request: Request) {
|
||||
status: 500,
|
||||
statusText: "Invalid request",
|
||||
headers: corsHeaders,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
} catch (error: any) {
|
||||
@@ -108,7 +108,7 @@ export async function POST(request: Request) {
|
||||
status: 500,
|
||||
statusText: "Invalid request",
|
||||
headers: corsHeaders,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -120,6 +120,6 @@ export async function POST(request: Request) {
|
||||
{
|
||||
status: 200,
|
||||
headers: corsHeaders,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { NextResponse, type NextRequest } from "next/server";
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const file = new URL(request.url).searchParams.get("file");
|
||||
console.log(file);
|
||||
return NextResponse.redirect(
|
||||
`${process.env.SPACES_ENDPOINT}/comfyui-deploy/${file}`
|
||||
);
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import "./globals.css";
|
||||
import { NavbarRight } from "@/components/NavbarRight";
|
||||
import type { Metadata } from "next";
|
||||
import { Inter } from "next/font/google";
|
||||
import { Toaster } from "sonner";
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
@@ -29,6 +30,7 @@ export default function RootLayout({
|
||||
<div className="md:px-10 px-6 w-full flex items-start">
|
||||
{children}
|
||||
</div>
|
||||
<Toaster richColors />
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import { MachineList } from "@/components/MachineList";
|
||||
import { WorkflowList } from "@/components/WorkflowList";
|
||||
import { db } from "@/db/db";
|
||||
import {
|
||||
machinesTable,
|
||||
usersTable,
|
||||
workflowTable,
|
||||
workflowVersionTable,
|
||||
} from "@/db/schema";
|
||||
import { machinesTable, usersTable } from "@/db/schema";
|
||||
import { auth, clerkClient } from "@clerk/nextjs";
|
||||
import { desc, eq, sql } from "drizzle-orm";
|
||||
import { desc, eq } from "drizzle-orm";
|
||||
|
||||
export default function Page() {
|
||||
return <MachineListServer />;
|
||||
|
||||
Reference in New Issue
Block a user