diff --git a/comfy-nodes/external_image.py b/comfy-nodes/external_image.py new file mode 100644 index 0000000..846ceaa --- /dev/null +++ b/comfy-nodes/external_image.py @@ -0,0 +1,51 @@ +import folder_paths +from PIL import Image, ImageOps +import numpy as np +import torch + +class ComfyUIDeployExternalImage: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "name": ( + "STRING", + {"multiline": False, "default": "input_image"}, + ), + }, + "optional": { + "default_image": ("IMAGE",), + } + } + + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("image",) + + FUNCTION = "run" + + CATEGORY = "image" + + def run(self, name, default_image=None): + image = default_image + try: + if name.startswith('http'): + import requests + from io import BytesIO + print("Fetching image from url: ", name) + response = requests.get(name) + image = Image.open(BytesIO(response.content)) + else: + raise ValueError("Invalid image url provided.") + # image_path = folder_paths.get_annotated_filepath(name) + # image = Image.open(image_path) + image = ImageOps.exif_transpose(image) + # image = image.convert("RGB") + image = np.array(image).astype(np.float32) / 255.0 + image = torch.from_numpy(image)[None,] + return [image] + except: + return [image] + + +NODE_CLASS_MAPPINGS = {"ComfyUIDeployExternalImage": ComfyUIDeployExternalImage} +NODE_DISPLAY_NAME_MAPPINGS = {"ComfyUIDeployExternalImage": "External Image (ComfyUI Deploy)"} \ No newline at end of file diff --git a/web-plugin/index.js b/web-plugin/index.js index 66924ca..7d22642 100644 --- a/web-plugin/index.js +++ b/web-plugin/index.js @@ -22,6 +22,7 @@ const ext = { this.properties = {}; this.properties.workflow_name = ""; this.properties.workflow_id = ""; + this.properties.version = ""; } ComfyWidgets.STRING( @@ -38,6 +39,13 @@ const ext = { app, ); + ComfyWidgets.STRING( + this, + "version", + ["", { default: this.properties.version, multiline: false }], + app, + ); + // this.widgets.forEach((w) => { // // w.computeSize = () => [200,10] // w.computedHeight = 2; @@ -150,6 +158,7 @@ function addButton() { deploy.style.color = "green"; deployMetaNode.widgets[1].value = data.workflow_id; + deployMetaNode.widgets[2].value = data.version; graph.change(); infoDialog.show( diff --git a/web/drizzle/0007_thin_greymalkin.sql b/web/drizzle/0007_thin_greymalkin.sql new file mode 100644 index 0000000..7d69a0e --- /dev/null +++ b/web/drizzle/0007_thin_greymalkin.sql @@ -0,0 +1 @@ +ALTER TABLE "comfy_deploy"."workflow_runs" ADD COLUMN "workflow_inputs" jsonb; \ No newline at end of file diff --git a/web/drizzle/meta/0007_snapshot.json b/web/drizzle/meta/0007_snapshot.json new file mode 100644 index 0000000..85c78d5 --- /dev/null +++ b/web/drizzle/meta/0007_snapshot.json @@ -0,0 +1,537 @@ +{ + "id": "621d6c47-6ecb-4dcd-8478-a3721b7accee", + "prevId": "c68b3727-5154-41eb-b44e-d5f26b72be44", + "version": "5", + "dialect": "pg", + "tables": { + "deployments": { + "name": "deployments", + "schema": "comfy_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 + }, + "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 + }, + "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": "no action", + "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": "comfy_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 + }, + "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()" + } + }, + "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": "comfy_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": "comfy_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": "comfy_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 + }, + "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()" + } + }, + "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": "comfy_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 + }, + "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": "comfy_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 + }, + "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" + } + }, + "workflow_run_status": { + "name": "workflow_run_status", + "values": { + "not-started": "not-started", + "running": "running", + "success": "success", + "failed": "failed" + } + } + }, + "schemas": { + "comfy_deploy": "comfy_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 c11df69..33fb779 100644 --- a/web/drizzle/meta/_journal.json +++ b/web/drizzle/meta/_journal.json @@ -50,6 +50,13 @@ "when": 1702556119574, "tag": "0006_chief_mariko_yashida", "breakpoints": true + }, + { + "idx": 7, + "version": "5", + "when": 1702609665746, + "tag": "0007_thin_greymalkin", + "breakpoints": true } ] } \ No newline at end of file diff --git a/web/package.json b/web/package.json index 4c1b84d..f327b62 100644 --- a/web/package.json +++ b/web/package.json @@ -9,7 +9,7 @@ "start": "next start", "lint": "next lint", "generate": "bunx drizzle-kit generate:pg", - "migrate-production": "cp .env.local .env.local.bak && vercel env pull --environment=production && bun run migrate.mts && mv .env.local.bak .env.local", + "migrate-production": "bun run migrate.mts", "migrate-local": "SSL=false LOCAL=true bun run migrate.mts", "db-up": "docker-compose up", "db-dev": "bun run db-up && bun run migrate-local" diff --git a/web/src/app/api/run/route.ts b/web/src/app/api/run/route.ts index 2aa4cdd..485206e 100644 --- a/web/src/app/api/run/route.ts +++ b/web/src/app/api/run/route.ts @@ -10,6 +10,7 @@ import { z } from "zod"; const Request = z.object({ deployment_id: z.string(), + inputs: z.record(z.string()).optional(), }); const Request2 = z.object({ @@ -48,7 +49,7 @@ export async function POST(request: Request) { const origin = new URL(request.url).origin; - const { deployment_id } = data; + const { deployment_id, inputs } = data; try { const deploymentData = await db.query.deploymentsTable.findFirst({ @@ -60,7 +61,8 @@ export async function POST(request: Request) { const run_id = await createRun( origin, deploymentData.workflow_version_id, - deploymentData.machine_id + deploymentData.machine_id, + inputs ); return NextResponse.json( diff --git a/web/src/app/api/view/route.ts b/web/src/app/api/view/route.ts index dfcd078..1a7031c 100644 --- a/web/src/app/api/view/route.ts +++ b/web/src/app/api/view/route.ts @@ -1,9 +1,10 @@ +import { replaceCDNUrl } from "@/server/resource"; import { NextResponse, type NextRequest } from "next/server"; export async function GET(request: NextRequest) { const file = new URL(request.url).searchParams.get("file"); - console.log(`${process.env.SPACES_ENDPOINT}/comfyui-deploy/${file}`); + // console.log(`${process.env.SPACES_ENDPOINT}/comfyui-deploy/${file}`); return NextResponse.redirect( - `${process.env.SPACES_ENDPOINT}/comfyui-deploy/${file}` + replaceCDNUrl(`${process.env.SPACES_ENDPOINT}/comfyui-deploy/${file}`) ); } diff --git a/web/src/db/schema.ts b/web/src/db/schema.ts index 62f80d2..9883e13 100644 --- a/web/src/db/schema.ts +++ b/web/src/db/schema.ts @@ -82,6 +82,7 @@ export const workflowRunsTable = dbSchema.table("workflow_runs", { onDelete: "set null", } ), + workflow_inputs: jsonb("workflow_inputs").$type>(), workflow_id: uuid("workflow_id") .notNull() .references(() => workflowTable.id, { diff --git a/web/src/server/createRun.ts b/web/src/server/createRun.ts index 55551e6..0e3294e 100644 --- a/web/src/server/createRun.ts +++ b/web/src/server/createRun.ts @@ -10,7 +10,8 @@ import "server-only"; export async function createRun( origin: string, workflow_version_id: string, - machine_id: string + machine_id: string, + inputs?: Record ) { const machine = await db.query.machinesTable.findFirst({ where: eq(workflowRunsTable.id, machine_id), @@ -46,6 +47,15 @@ export async function createRun( const comfyui_endpoint = `${machine.endpoint}/comfyui-deploy/run`; + let workflow_api = workflow_version_data.workflow_api; + + // Replace the inputs + if (inputs) { + for (const key in inputs) { + workflow_api = workflow_api.replace(`"${key}"`, `"${inputs[key]}"`); + } + } + // Sending to comfyui const result = await fetch(comfyui_endpoint, { method: "POST", @@ -53,9 +63,10 @@ export async function createRun( // "Content-Type": "application/json", // }, body: JSON.stringify({ - workflow_api: workflow_version_data.workflow_api, + workflow_api: workflow_api, status_endpoint: `${origin}/api/update-run`, file_upload_endpoint: `${origin}/api/file-upload`, + inputs: inputs, }), }).then(async (res) => ComfyAPI_Run.parseAsync(await res.json())); // .catch((error) => { @@ -79,6 +90,7 @@ export async function createRun( id: result.prompt_id, workflow_id: workflow_version_data.workflow_id, workflow_version_id: workflow_version_data.id, + workflow_inputs: inputs, machine_id, }) .returning(); diff --git a/web/src/server/curdDeploments.ts b/web/src/server/curdDeploments.ts index 03742c0..74dc3f2 100644 --- a/web/src/server/curdDeploments.ts +++ b/web/src/server/curdDeploments.ts @@ -43,4 +43,7 @@ export async function createDeployments( }); } revalidatePath(`/${workflow_id}`); + return { + message: `Successfully created deployment for ${environment}`, + }; }