diff --git a/web/drizzle/0031_fast_lyja.sql b/web/drizzle/0031_fast_lyja.sql new file mode 100644 index 0000000..f39ccce --- /dev/null +++ b/web/drizzle/0031_fast_lyja.sql @@ -0,0 +1 @@ +ALTER TABLE "comfyui_deploy"."workflow_runs" ADD COLUMN "started_at" timestamp; \ No newline at end of file diff --git a/web/drizzle/meta/0031_snapshot.json b/web/drizzle/meta/0031_snapshot.json new file mode 100644 index 0000000..264946f --- /dev/null +++ b/web/drizzle/meta/0031_snapshot.json @@ -0,0 +1,762 @@ +{ + "id": "1ca4fdb7-c0c4-4c39-8b47-f40282293da0", + "prevId": "db06ea66-92c2-4ebe-93c1-6cb8a90ccd8b", + "version": "5", + "dialect": "pg", + "tables": { + "api_keys": { + "name": "api_keys", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revoked": { + "name": "revoked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "api_keys_user_id_users_id_fk": { + "name": "api_keys_user_id_users_id_fk", + "tableFrom": "api_keys", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_keys_key_unique": { + "name": "api_keys_key_unique", + "nullsNotDistinct": false, + "columns": [ + "key" + ] + } + } + }, + "deployments": { + "name": "deployments", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workflow_version_id": { + "name": "workflow_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "machine_id": { + "name": "machine_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "showcase_media": { + "name": "showcase_media", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "environment": { + "name": "environment", + "type": "deployment_environment", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "deployments_user_id_users_id_fk": { + "name": "deployments_user_id_users_id_fk", + "tableFrom": "deployments", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployments_workflow_version_id_workflow_versions_id_fk": { + "name": "deployments_workflow_version_id_workflow_versions_id_fk", + "tableFrom": "deployments", + "tableTo": "workflow_versions", + "columnsFrom": [ + "workflow_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "deployments_workflow_id_workflows_id_fk": { + "name": "deployments_workflow_id_workflows_id_fk", + "tableFrom": "deployments", + "tableTo": "workflows", + "columnsFrom": [ + "workflow_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployments_machine_id_machines_id_fk": { + "name": "deployments_machine_id_machines_id_fk", + "tableFrom": "deployments", + "tableTo": "machines", + "columnsFrom": [ + "machine_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "machines": { + "name": "machines", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "auth_token": { + "name": "auth_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "machine_type", + "primaryKey": false, + "notNull": true, + "default": "'classic'" + }, + "status": { + "name": "status", + "type": "machine_status", + "primaryKey": false, + "notNull": true, + "default": "'ready'" + }, + "snapshot": { + "name": "snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "models": { + "name": "models", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "gpu": { + "name": "gpu", + "type": "machine_gpu", + "primaryKey": false, + "notNull": false + }, + "build_machine_instance_id": { + "name": "build_machine_instance_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "build_log": { + "name": "build_log", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "machines_user_id_users_id_fk": { + "name": "machines_user_id_users_id_fk", + "tableFrom": "machines", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "users": { + "name": "users", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "workflow_run_outputs": { + "name": "workflow_run_outputs", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workflow_run_outputs_run_id_workflow_runs_id_fk": { + "name": "workflow_run_outputs_run_id_workflow_runs_id_fk", + "tableFrom": "workflow_run_outputs", + "tableTo": "workflow_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "workflow_runs": { + "name": "workflow_runs", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workflow_version_id": { + "name": "workflow_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "workflow_inputs": { + "name": "workflow_inputs", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "workflow_id": { + "name": "workflow_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "machine_id": { + "name": "machine_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "origin": { + "name": "origin", + "type": "workflow_run_origin", + "primaryKey": false, + "notNull": true, + "default": "'api'" + }, + "status": { + "name": "status", + "type": "workflow_run_status", + "primaryKey": false, + "notNull": true, + "default": "'not-started'" + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "workflow_runs_workflow_version_id_workflow_versions_id_fk": { + "name": "workflow_runs_workflow_version_id_workflow_versions_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "workflow_versions", + "columnsFrom": [ + "workflow_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "workflow_runs_workflow_id_workflows_id_fk": { + "name": "workflow_runs_workflow_id_workflows_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "workflows", + "columnsFrom": [ + "workflow_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_runs_machine_id_machines_id_fk": { + "name": "workflow_runs_machine_id_machines_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "machines", + "columnsFrom": [ + "machine_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "workflows": { + "name": "workflows", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workflows_user_id_users_id_fk": { + "name": "workflows_user_id_users_id_fk", + "tableFrom": "workflows", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "workflow_versions": { + "name": "workflow_versions", + "schema": "comfyui_deploy", + "columns": { + "workflow_id": { + "name": "workflow_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workflow": { + "name": "workflow", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "workflow_api": { + "name": "workflow_api", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "snapshot": { + "name": "snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workflow_versions_workflow_id_workflows_id_fk": { + "name": "workflow_versions_workflow_id_workflows_id_fk", + "tableFrom": "workflow_versions", + "tableTo": "workflows", + "columnsFrom": [ + "workflow_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": { + "deployment_environment": { + "name": "deployment_environment", + "values": { + "staging": "staging", + "production": "production", + "public-share": "public-share" + } + }, + "machine_gpu": { + "name": "machine_gpu", + "values": { + "T4": "T4", + "A10G": "A10G", + "A100": "A100" + } + }, + "machine_status": { + "name": "machine_status", + "values": { + "ready": "ready", + "building": "building", + "error": "error" + } + }, + "machine_type": { + "name": "machine_type", + "values": { + "classic": "classic", + "runpod-serverless": "runpod-serverless", + "modal-serverless": "modal-serverless", + "comfy-deploy-serverless": "comfy-deploy-serverless" + } + }, + "workflow_run_origin": { + "name": "workflow_run_origin", + "values": { + "manual": "manual", + "api": "api", + "public-share": "public-share" + } + }, + "workflow_run_status": { + "name": "workflow_run_status", + "values": { + "not-started": "not-started", + "running": "running", + "uploading": "uploading", + "success": "success", + "failed": "failed" + } + } + }, + "schemas": { + "comfyui_deploy": "comfyui_deploy" + }, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/web/drizzle/meta/_journal.json b/web/drizzle/meta/_journal.json index 3bfcc76..360bc49 100644 --- a/web/drizzle/meta/_journal.json +++ b/web/drizzle/meta/_journal.json @@ -218,6 +218,13 @@ "when": 1705716303820, "tag": "0030_kind_doorman", "breakpoints": true + }, + { + "idx": 31, + "version": "5", + "when": 1705763980972, + "tag": "0031_fast_lyja", + "breakpoints": true } ] } \ No newline at end of file diff --git a/web/src/components/RunDisplay.tsx b/web/src/components/RunDisplay.tsx index fa518ac..e2fa5ed 100644 --- a/web/src/components/RunDisplay.tsx +++ b/web/src/components/RunDisplay.tsx @@ -2,62 +2,73 @@ import { RunInputs } from "@/components/RunInputs"; import { RunOutputs } from "@/components/RunOutputs"; import { Badge } from "@/components/ui/badge"; import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, - DialogTrigger, + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, } from "@/components/ui/dialog"; import { TableCell, TableRow } from "@/components/ui/table"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { getDuration, getRelativeTime } from "@/lib/getRelativeTime"; import { type findAllRuns } from "@/server/findAllRuns"; import { Suspense } from "react"; import { LiveStatus } from "./LiveStatus"; export async function RunDisplay({ - run, + run, }: { - run: Awaited>[0]; + run: Awaited>[0]; }) { - return ( - - - - {run.number} - - {run.machine?.name} - - - {getRelativeTime(run.created_at)} - - {run.version?.version} - - - {run.origin} - - - - {getDuration(run.duration)} - - - - - - - Run outputs - - You can view your run's outputs here - - -
- - - - -
- {/*
{view}
*/} -
-
- ); + return ( + + + + {run.number} + + {run.machine?.name} + + + {getRelativeTime(run.created_at)} + + {run.version?.version} + + + {run.origin} + + + + + {getDuration(run.duration)} + +
Cold start: {getDuration(run.cold_start_duration)}
+
Run duration: {getDuration(run.run_duration)}
+
+
+
+ +
+
+ + + Run outputs + + You can view your run's outputs here + + +
+ + + + +
+ {/*
{view}
*/} +
+
+ ); } diff --git a/web/src/components/RunsTable.tsx b/web/src/components/RunsTable.tsx index a13e6bf..a790b8a 100644 --- a/web/src/components/RunsTable.tsx +++ b/web/src/components/RunsTable.tsx @@ -1,10 +1,3 @@ -import { - findAllDeployments, - findAllRunsWithCounts, -} from "../server/findAllRuns"; -import { DeploymentDisplay } from "./DeploymentDisplay"; -import { PaginationControl } from "./PaginationControl"; -import { RunDisplay } from "./RunDisplay"; import { Table, TableBody, @@ -15,6 +8,13 @@ import { } from "@/components/ui/table"; import { parseAsInteger } from "next-usequerystate"; import { headers } from "next/headers"; +import { + findAllDeployments, + findAllRunsWithCounts, +} from "../server/findAllRuns"; +import { DeploymentDisplay } from "./DeploymentDisplay"; +import { PaginationControl } from "./PaginationControl"; +import { RunDisplay } from "./RunDisplay"; const itemPerPage = 6; const pageParser = parseAsInteger.withDefault(1); @@ -33,40 +33,40 @@ export async function RunsTable(props: { offset: (page - 1) * itemPerPage, }); return ( -
-
- - {allRuns.length == 0 && ( - A list of your recent runs. - )} - - - Number - Machine - Time - Version - Origin - Duration - Live Status - Status - - - - {allRuns.map((run) => ( - - ))} - -
-
+
+
+ + {allRuns.length === 0 && ( + A list of your recent runs. + )} + + + Number + Machine + Time + Version + Origin + Duration + Live Status + Status + + + + {allRuns.map((run) => ( + + ))} + +
+
- {Math.ceil(total / itemPerPage) > 0 && ( - - )} -
- ); + {Math.ceil(total / itemPerPage) > 0 && ( + + )} +
+ ); } export async function DeploymentsTable(props: { workflow_id: string }) { diff --git a/web/src/db/schema.ts b/web/src/db/schema.ts index f5f8fdf..783717c 100644 --- a/web/src/db/schema.ts +++ b/web/src/db/schema.ts @@ -1,13 +1,13 @@ -import { relations, type InferSelectModel } from "drizzle-orm"; +import { type InferSelectModel, relations } from "drizzle-orm"; import { - text, - pgSchema, - uuid, - integer, - timestamp, - jsonb, - pgEnum, - boolean, + boolean, + integer, + jsonb, + pgEnum, + pgSchema, + text, + timestamp, + uuid, } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { z } from "zod"; @@ -130,29 +130,30 @@ export const machinesStatus = pgEnum("machine_status", [ // We still want to keep the workflow run record. export const workflowRunsTable = dbSchema.table("workflow_runs", { - id: uuid("id").primaryKey().defaultRandom().notNull(), - // when workflow version deleted, still want to keep this record - workflow_version_id: uuid("workflow_version_id").references( - () => workflowVersionTable.id, - { - onDelete: "set null", - } - ), - workflow_inputs: - jsonb("workflow_inputs").$type>(), - workflow_id: uuid("workflow_id") - .notNull() - .references(() => workflowTable.id, { - onDelete: "cascade", - }), - // when machine deleted, still want to keep this record - machine_id: uuid("machine_id").references(() => machinesTable.id, { - onDelete: "set null", - }), - origin: workflowRunOrigin("origin").notNull().default("api"), - status: workflowRunStatus("status").notNull().default("not-started"), - ended_at: timestamp("ended_at"), - created_at: timestamp("created_at").defaultNow().notNull(), + id: uuid("id").primaryKey().defaultRandom().notNull(), + // when workflow version deleted, still want to keep this record + workflow_version_id: uuid("workflow_version_id").references( + () => workflowVersionTable.id, + { + onDelete: "set null", + }, + ), + workflow_inputs: + jsonb("workflow_inputs").$type>(), + workflow_id: uuid("workflow_id") + .notNull() + .references(() => workflowTable.id, { + onDelete: "cascade", + }), + // when machine deleted, still want to keep this record + machine_id: uuid("machine_id").references(() => machinesTable.id, { + onDelete: "set null", + }), + origin: workflowRunOrigin("origin").notNull().default("api"), + status: workflowRunStatus("status").notNull().default("not-started"), + ended_at: timestamp("ended_at"), + created_at: timestamp("created_at").defaultNow().notNull(), + started_at: timestamp("started_at"), }); export const workflowRunRelations = relations( diff --git a/web/src/server/createRun.ts b/web/src/server/createRun.ts index 6022918..90f51f9 100644 --- a/web/src/server/createRun.ts +++ b/web/src/server/createRun.ts @@ -1,11 +1,10 @@ "use server"; -import { withServerPromise } from "./withServerPromise"; import { db } from "@/db/db"; import type { - MachineType, - WorkflowRunOriginType, - WorkflowVersionType, + MachineType, + WorkflowRunOriginType, + WorkflowVersionType, } from "@/db/schema"; import { machinesTable, workflowRunsTable } from "@/db/schema"; import type { APIKeyUserType } from "@/server/APIKeyBodyRequest"; @@ -16,219 +15,229 @@ import { and, eq } from "drizzle-orm"; import { revalidatePath } from "next/cache"; import "server-only"; import { v4 } from "uuid"; +import { withServerPromise } from "./withServerPromise"; export const createRun = withServerPromise( - async ({ - origin, - workflow_version_id, - machine_id, - inputs, - runOrigin, - apiUser, - }: { - origin: string; - workflow_version_id: string | WorkflowVersionType; - machine_id: string | MachineType; - inputs?: Record; - runOrigin?: WorkflowRunOriginType; - apiUser?: APIKeyUserType; - }) => { - const machine = - typeof machine_id === "string" - ? await db.query.machinesTable.findFirst({ - where: and( - eq(machinesTable.id, machine_id), - eq(machinesTable.disabled, false) - ), - }) - : machine_id; + async ({ + origin, + workflow_version_id, + machine_id, + inputs, + runOrigin, + apiUser, + }: { + origin: string; + workflow_version_id: string | WorkflowVersionType; + machine_id: string | MachineType; + inputs?: Record; + runOrigin?: WorkflowRunOriginType; + apiUser?: APIKeyUserType; + }) => { + const machine = + typeof machine_id === "string" + ? await db.query.machinesTable.findFirst({ + where: and( + eq(machinesTable.id, machine_id), + eq(machinesTable.disabled, false), + ), + }) + : machine_id; - if (!machine) { - throw new Error("Machine not found"); - } + if (!machine) { + throw new Error("Machine not found"); + } - const workflow_version_data = - typeof workflow_version_id === "string" - ? await db.query.workflowVersionTable.findFirst({ - where: eq(workflowRunsTable.id, workflow_version_id), - with: { - workflow: { - columns: { - org_id: true, - user_id: true, - }, - }, - }, - }) - : workflow_version_id; + const workflow_version_data = + typeof workflow_version_id === "string" + ? await db.query.workflowVersionTable.findFirst({ + where: eq(workflowRunsTable.id, workflow_version_id), + with: { + workflow: { + columns: { + org_id: true, + user_id: true, + }, + }, + }, + }) + : workflow_version_id; - if (!workflow_version_data) { - throw new Error("Workflow version not found"); - } + if (!workflow_version_data) { + throw new Error("Workflow version not found"); + } - if (apiUser) - if (apiUser.org_id) { - // is org api call, check org only - if (apiUser.org_id != workflow_version_data.workflow.org_id) { - throw new Error("Workflow not found"); - } - } else { - // is user api call, check user only - if ( - apiUser.user_id != workflow_version_data.workflow.user_id && - workflow_version_data.workflow.org_id == null - ) { - throw new Error("Workflow not found"); - } - } + if (apiUser) + if (apiUser.org_id) { + // is org api call, check org only + if (apiUser.org_id != workflow_version_data.workflow.org_id) { + throw new Error("Workflow not found"); + } + } else { + // is user api call, check user only + if ( + apiUser.user_id != workflow_version_data.workflow.user_id && + workflow_version_data.workflow.org_id == null + ) { + throw new Error("Workflow not found"); + } + } - const workflow_api = workflow_version_data.workflow_api; + const workflow_api = workflow_version_data.workflow_api; - // Replace the inputs - if (inputs && workflow_api) { - for (const key in inputs) { - Object.entries(workflow_api).forEach(([_, node]) => { - if (node.inputs["input_id"] === key) { - node.inputs["input_id"] = inputs[key]; - } - }); - } - } + // Replace the inputs + if (inputs && workflow_api) { + for (const key in inputs) { + Object.entries(workflow_api).forEach(([_, node]) => { + if (node.inputs["input_id"] === key) { + node.inputs["input_id"] = inputs[key]; + } + }); + } + } - let prompt_id: string | undefined = undefined; - const shareData = { - workflow_api: workflow_api, - status_endpoint: `${origin}/api/update-run`, - file_upload_endpoint: `${origin}/api/file-upload`, - }; + let prompt_id: string | undefined = undefined; + const shareData = { + workflow_api: workflow_api, + status_endpoint: `${origin}/api/update-run`, + file_upload_endpoint: `${origin}/api/file-upload`, + }; - prompt_id = v4(); + prompt_id = v4(); - // Add to our db - const workflow_run = await db - .insert(workflowRunsTable) - .values({ - id: prompt_id, - workflow_id: workflow_version_data.workflow_id, - workflow_version_id: workflow_version_data.id, - workflow_inputs: inputs, - machine_id: machine.id, - origin: runOrigin, - }) - .returning(); + // Add to our db + const workflow_run = await db + .insert(workflowRunsTable) + .values({ + id: prompt_id, + workflow_id: workflow_version_data.workflow_id, + workflow_version_id: workflow_version_data.id, + workflow_inputs: inputs, + machine_id: machine.id, + origin: runOrigin, + }) + .returning(); - revalidatePath(`/${workflow_version_data.workflow_id}`); + revalidatePath(`/${workflow_version_data.workflow_id}`); - try { - switch (machine.type) { - case "comfy-deploy-serverless": - case "modal-serverless": - const _data = { - input: { - ...shareData, - prompt_id: prompt_id, - }, - }; + try { + switch (machine.type) { + case "comfy-deploy-serverless": + case "modal-serverless": + const _data = { + input: { + ...shareData, + prompt_id: prompt_id, + }, + }; - const ___result = await fetch(`${machine.endpoint}/run`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(_data), - cache: "no-store", - }); - console.log(___result); - if (!___result.ok) - throw new Error( - `Error creating run, ${ - ___result.statusText - } ${await ___result.text()}` - ); - console.log(_data, ___result); - break; - case "runpod-serverless": - const data = { - input: { - ...shareData, - prompt_id: prompt_id, - }, - }; + const ___result = await fetch(`${machine.endpoint}/run`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(_data), + cache: "no-store", + }); + console.log(___result); + if (!___result.ok) + throw new Error( + `Error creating run, ${ + ___result.statusText + } ${await ___result.text()}`, + ); + console.log(_data, ___result); + break; + case "runpod-serverless": + const data = { + input: { + ...shareData, + prompt_id: prompt_id, + }, + }; - if ( - !machine.auth_token && - !machine.endpoint.includes("localhost") && - !machine.endpoint.includes("127.0.0.1") - ) { - throw new Error("Machine auth token not found"); - } + if ( + !machine.auth_token && + !machine.endpoint.includes("localhost") && + !machine.endpoint.includes("127.0.0.1") + ) { + throw new Error("Machine auth token not found"); + } - const __result = await fetch(`${machine.endpoint}/run`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${machine.auth_token}`, - }, - body: JSON.stringify(data), - cache: "no-store", - }); - console.log(__result); - if (!__result.ok) - throw new Error( - `Error creating run, ${ - __result.statusText - } ${await __result.text()}` - ); - console.log(data, __result); - break; - case "classic": - const body = { - ...shareData, - prompt_id: prompt_id, - }; - // console.log(body); - const comfyui_endpoint = `${machine.endpoint}/comfyui-deploy/run`; - const _result = await fetch(comfyui_endpoint, { - method: "POST", - body: JSON.stringify(body), - cache: "no-store", - }); - // console.log(_result); + const __result = await fetch(`${machine.endpoint}/run`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${machine.auth_token}`, + }, + body: JSON.stringify(data), + cache: "no-store", + }); + console.log(__result); + if (!__result.ok) + throw new Error( + `Error creating run, ${ + __result.statusText + } ${await __result.text()}`, + ); + console.log(data, __result); + break; + case "classic": + const body = { + ...shareData, + prompt_id: prompt_id, + }; + // console.log(body); + const comfyui_endpoint = `${machine.endpoint}/comfyui-deploy/run`; + const _result = await fetch(comfyui_endpoint, { + method: "POST", + body: JSON.stringify(body), + cache: "no-store", + }); + // console.log(_result); - if (!_result.ok) { - let message = `Error creating run, ${_result.statusText}`; - try { - const result = await ComfyAPI_Run.parseAsync( - await _result.json() - ); - message += ` ${result.node_errors}`; - } catch (error) {} - throw new Error(message); - } - // prompt_id = result.prompt_id; - break; - } - } catch (e) { - console.error(e); - await db - .update(workflowRunsTable) - .set({ - status: "failed", - }) - .where(eq(workflowRunsTable.id, workflow_run[0].id)); - throw e; - } + if (!_result.ok) { + let message = `Error creating run, ${_result.statusText}`; + try { + const result = await ComfyAPI_Run.parseAsync( + await _result.json(), + ); + message += ` ${result.node_errors}`; + } catch (error) {} + throw new Error(message); + } + // prompt_id = result.prompt_id; + break; + } + } catch (e) { + console.error(e); + await db + .update(workflowRunsTable) + .set({ + status: "failed", + }) + .where(eq(workflowRunsTable.id, workflow_run[0].id)); + throw e; + } - return { - workflow_run_id: workflow_run[0].id, - message: "Successful workflow run", - }; - } + // It successfully started, update the started_at time + + await db + .update(workflowRunsTable) + .set({ + started_at: new Date(), + }) + .where(eq(workflowRunsTable.id, workflow_run[0].id)); + + return { + workflow_run_id: workflow_run[0].id, + message: "Successful workflow run", + }; + }, ); export async function checkStatus(run_id: string) { - const { userId } = auth(); - if (!userId) throw new Error("User not found"); + const { userId } = auth(); + if (!userId) throw new Error("User not found"); - return await getRunsData(run_id); + return await getRunsData(run_id); } diff --git a/web/src/server/findAllRuns.tsx b/web/src/server/findAllRuns.tsx index 121b48d..08bef24 100644 --- a/web/src/server/findAllRuns.tsx +++ b/web/src/server/findAllRuns.tsx @@ -16,32 +16,42 @@ export async function findAllRuns({ offset = 0, }: RunsSearchTypes) { return await db.query.workflowRunsTable.findMany({ - where: eq(workflowRunsTable.workflow_id, workflow_id), - orderBy: desc(workflowRunsTable.created_at), - offset: offset, - limit: limit, - extras: { - number: sql`row_number() over (order by created_at)`.as("number"), - total: sql`count(*) over ()`.as("total"), - duration: - sql`(extract(epoch from ended_at) - extract(epoch from created_at))`.as( - "duration" - ), - }, - with: { - machine: { - columns: { - name: true, - endpoint: true, - }, - }, - version: { - columns: { - version: true, - }, - }, - }, - }); + where: eq(workflowRunsTable.workflow_id, workflow_id), + orderBy: desc(workflowRunsTable.created_at), + offset: offset, + limit: limit, + extras: { + number: sql`row_number() over (order by created_at)`.as( + "number", + ), + total: sql`count(*) over ()`.as("total"), + duration: + sql`(extract(epoch from ended_at) - extract(epoch from created_at))`.as( + "duration", + ), + cold_start_duration: + sql`(extract(epoch from started_at) - extract(epoch from created_at))`.as( + "cold_start_duration", + ), + run_duration: + sql`(extract(epoch from ended_at) - extract(epoch from started_at))`.as( + "run_duration", + ), + }, + with: { + machine: { + columns: { + name: true, + endpoint: true, + }, + }, + version: { + columns: { + version: true, + }, + }, + }, + }); } export async function findAllRunsWithCounts(props: RunsSearchTypes) {