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
+3 -88
View File
@@ -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>;
}
+26 -7
View File
@@ -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,
}
);
}
}
+44
View File
@@ -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 }
);
}
}
+27 -15
View File
@@ -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(
{
+6 -6
View File
@@ -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,
},
}
);
}
+9
View File
@@ -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
View 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>
+2 -8
View File
@@ -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 />;