diff --git a/web-plugin/index.js b/web-plugin/index.js index 5aedf7e..199b460 100644 --- a/web-plugin/index.js +++ b/web-plugin/index.js @@ -102,6 +102,12 @@ const ext = { * @typedef {import('../../../web/types/litegraph.js').LGraphNode} LGraphNode */ +function showError(title, message) { + infoDialog.show( + `

${title}


${message} `, + ); +} + function addButton() { const menu = document.querySelector(".comfy-menu"); @@ -113,9 +119,46 @@ function addButton() { /** @type {LGraph} */ const graph = app.graph; - const title = deploy.querySelector("#button-title") + const snapshot = await fetch("/snapshot/get_current").then((x) => x.json()); + console.log(snapshot); + + if (!snapshot) { + showError( + "Error when deploying", + "Unable to generate snapshot, please install ComfyUI Manager", + ); + return; + } + + const title = deploy.querySelector("#button-title"); + + let deployMeta = graph.findNodesByType("ComfyDeploy"); + + if (deployMeta.length == 0) { + const text = await inputDialog.input( + "Create your deployment", + "Workflow name", + ); + if (!text) return + console.log(text); + app.graph.beforeChange(); + var node = LiteGraph.createNode("ComfyDeploy"); + node.configure({ + widgets_values: [ + text + ] + }); + node.pos = [0, 0]; + app.graph.add(node); + app.graph.afterChange(); + deployMeta=[node] + // return; + // showError( + // "Error when deploying", + // "Unable to to find ComfyDeploy node, please add it first.", + // ); + } - const deployMeta = graph.findNodesByType("ComfyDeploy"); const deployMetaNode = deployMeta[0]; console.log(deployMetaNode); @@ -132,10 +175,7 @@ function addButton() { // const endpoint = localStorage.getItem("endpoint") ?? ""; // const apiKey = localStorage.getItem("apiKey"); - const { - endpoint, - apiKey, - } = getData(); + const { endpoint, apiKey } = getData(); if (!endpoint || !apiKey || apiKey === "" || endpoint === "") { configDialog.show(); @@ -152,7 +192,7 @@ function addButton() { endpoint = endpoint.slice(0, -1); } - const apiRoute = endpoint + "/api/upload" + const apiRoute = endpoint + "/api/upload"; // const userId = apiKey try { let data = await fetch(apiRoute, { @@ -162,10 +202,11 @@ function addButton() { workflow_id, workflow: prompt.workflow, workflow_api: prompt.output, + snapshot: snapshot, }), headers: { "Content-Type": "application/json", - "Authorization": "Bearer " + apiKey, + Authorization: "Bearer " + apiKey, }, }); @@ -185,7 +226,7 @@ function addButton() { graph.change(); infoDialog.show( - `Deployed successfully!

Workflow ID: ${data.workflow_id}
Workflow Name: ${workflow_name}
Workflow Version: ${data.version}`, + `Deployed successfully! View here ->

Workflow ID: ${data.workflow_id}
Workflow Name: ${workflow_name}
Workflow Version: ${data.version}
`, ); setTimeout(() => { @@ -210,19 +251,18 @@ function addButton() { config.style.position = "absolute"; config.style.right = "10px"; config.style.top = "0px"; - + // set aspect ratio to square config.style.width = "20px"; - config.src = "https://api.iconify.design/material-symbols-light:settings.svg?color=%23888888"; + config.src = + "https://api.iconify.design/material-symbols-light:settings.svg?color=%23888888"; config.onclick = (e) => { - e.preventDefault() - e.stopPropagation() + e.preventDefault(); + e.stopPropagation(); configDialog.show(); }; - deploy.append( - config - ) + deploy.append(config); deploy.style.order = "99"; @@ -231,44 +271,113 @@ function addButton() { app.registerExtension(ext); - -import { ComfyDialog, $el } from '../../scripts/ui.js'; +import { ComfyDialog, $el } from "../../scripts/ui.js"; export class InfoDialog extends ComfyDialog { - constructor() { - super(); - this.element.classList.add("comfy-normal-modal"); - } - createButtons() { - return [ - $el("button", { - type: "button", - textContent: "Close", - onclick: () => this.close(), - }), - ]; - } + constructor() { + super(); + this.element.classList.add("comfy-normal-modal"); + this.element.style.paddingBottom = "20px"; + } + createButtons() { + return [ + $el("button", { + type: "button", + textContent: "Close", + onclick: () => this.close(), + }), + ]; + } - close() { - this.element.style.display = "none"; - } + close() { + this.element.style.display = "none"; + } - show(html) { + show(html) { + this.textElement.style["white-space"] = "normal"; this.textElement.style.color = "white"; - if (typeof html === "string") { - this.textElement.innerHTML = html; - } else { - this.textElement.replaceChildren(html); - } - this.element.style.display = "flex"; - this.element.style.zIndex = 1001; - } + this.textElement.style.marginTop = "0px"; + if (typeof html === "string") { + this.textElement.innerHTML = html; + } else { + this.textElement.replaceChildren(html); + } + this.element.style.display = "flex"; + this.element.style.zIndex = 1001; + } } -export const infoDialog = new InfoDialog() +export class InputDialog extends InfoDialog { + callback = undefined; + + constructor() { + super(); + } + + createButtons() { + return [ + $el( + "div", + { + type: "div", + style: { + display: "flex", + gap: "6px", + justifyContent: "flex-end", + width: "100%", + }, + }, + [ + $el("button", { + type: "button", + textContent: "Save", + onclick: () => { + const input = this.textElement.querySelector("#input").value; + if (input.trim() === "") { + showError("Input validation", "Input cannot be empty"); + } else { + this.callback?.(input); + this.close(); + this.textElement.querySelector("#input").value = ""; + } + }, + }), + $el("button", { + type: "button", + textContent: "Close", + onclick: () => { + this.callback?.(undefined); + this.close() + }, + }), + ], + ), + ]; + } + + input(title, message) { + return new Promise((resolve, reject) => { + this.callback = resolve; + this.show(` +
+

${title}

+ +
+ `); + }); + } +} + +export const inputDialog = new InputDialog(); + +export const infoDialog = new InfoDialog(); function getData(environment) { - const deployOption = environment || localStorage.getItem("comfy_deploy_env") || "cloud"; + const deployOption = + environment || localStorage.getItem("comfy_deploy_env") || "cloud"; const data = localStorage.getItem("comfy_deploy_env_data_" + deployOption); if (!data) { if (deployOption == "cloud") @@ -276,7 +385,7 @@ function getData(environment) { endpoint: "https://www.comfydeploy.com", apiKey: "", }; - else + else return { endpoint: "http://localhost:3000", apiKey: "", @@ -289,12 +398,12 @@ function getData(environment) { } export class ConfigDialog extends ComfyDialog { - container = null; constructor() { super(); this.element.classList.add("comfy-normal-modal"); + this.element.style.paddingBottom = "20px"; this.container = document.createElement("div"); this.element.querySelector(".comfy-modal-content").prepend(this.container); @@ -302,16 +411,18 @@ export class ConfigDialog extends ComfyDialog { createButtons() { return [ - $el("div", { - type: "div", - style: { - display: "flex", - gap: "6px", - justifyContent: "flex-end", - width: "100%", + $el( + "div", + { + type: "div", + style: { + display: "flex", + gap: "6px", + justifyContent: "flex-end", + width: "100%", + }, + onclick: () => this.save(), }, - onclick: () => this.save(), - }, [ $el("button", { type: "button", @@ -322,8 +433,8 @@ export class ConfigDialog extends ComfyDialog { type: "button", textContent: "Close", onclick: () => this.close(), - }) - ] + }), + ], ), ]; } @@ -341,8 +452,11 @@ export class ConfigDialog extends ComfyDialog { const data = { endpoint, apiKey, - } - localStorage.setItem("comfy_deploy_env_data_" + deployOption, JSON.stringify(data)); + }; + localStorage.setItem( + "comfy_deploy_env_data_" + deployOption, + JSON.stringify(data), + ); this.close(); } @@ -350,39 +464,47 @@ export class ConfigDialog extends ComfyDialog { this.container.style.color = "white"; const data = getData(); - + this.container.innerHTML = `

Comfy Deploy Config

`; const apiKeyInput = this.container.querySelector("#apiKey"); - apiKeyInput.addEventListener("paste", function(e) { + apiKeyInput.addEventListener("paste", function (e) { e.stopPropagation(); }); const deployOption = this.container.querySelector("#deployOption"); - const container = this.container - deployOption.addEventListener("change", function() { + const container = this.container; + deployOption.addEventListener("change", function () { const selectedOption = this.value; const data = getData(selectedOption); localStorage.setItem("comfy_deploy_env", selectedOption); - + container.querySelector("#endpoint").value = data.endpoint; container.querySelector("#apiKey").value = data.apiKey; }); @@ -392,4 +514,4 @@ export class ConfigDialog extends ComfyDialog { } } -export const configDialog = new ConfigDialog(); \ No newline at end of file +export const configDialog = new ConfigDialog(); diff --git a/web/drizzle/0022_petite_bishop.sql b/web/drizzle/0022_petite_bishop.sql new file mode 100644 index 0000000..b52e475 --- /dev/null +++ b/web/drizzle/0022_petite_bishop.sql @@ -0,0 +1 @@ +ALTER TABLE "comfyui_deploy"."workflow_versions" ADD COLUMN "snapshot" jsonb; \ No newline at end of file diff --git a/web/drizzle/meta/0022_snapshot.json b/web/drizzle/meta/0022_snapshot.json new file mode 100644 index 0000000..e546133 --- /dev/null +++ b/web/drizzle/meta/0022_snapshot.json @@ -0,0 +1,710 @@ +{ + "id": "9153404d-9279-4f43-a61f-7f5efefc12b7", + "prevId": "5893c5f8-4466-4b51-b9b7-24756c37dced", + "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 + }, + "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": "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 + }, + "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()" + } + }, + "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" + } + }, + "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" + } + }, + "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 4faf384..c0a4c41 100644 --- a/web/drizzle/meta/_journal.json +++ b/web/drizzle/meta/_journal.json @@ -155,6 +155,13 @@ "when": 1704380757696, "tag": "0021_aromatic_sabra", "breakpoints": true + }, + { + "idx": 22, + "version": "5", + "when": 1704453649633, + "tag": "0022_petite_bishop", + "breakpoints": true } ] } \ No newline at end of file diff --git a/web/src/app/(app)/api/upload/route.ts b/web/src/app/(app)/api/upload/route.ts index 93fe167..c6c2d8f 100644 --- a/web/src/app/(app)/api/upload/route.ts +++ b/web/src/app/(app)/api/upload/route.ts @@ -1,6 +1,7 @@ import { parseJWT } from "../../../../server/parseJWT"; import { db } from "@/db/db"; import { + snapshotType, workflowAPIType, workflowTable, workflowType, @@ -18,11 +19,11 @@ const corsHeaders = { }; const UploadRequest = z.object({ - // user_id: z.string(), workflow_id: z.string().optional(), - workflow_name: z.string().optional(), + workflow_name: z.string().min(1).optional(), workflow: workflowType, workflow_api: workflowAPIType, + snapshot: snapshotType, }); export async function OPTIONS(request: Request) { @@ -64,6 +65,7 @@ export async function POST(request: Request) { workflow_api, workflow_id: _workflow_id, workflow_name, + snapshot, } = data; let workflow_id = _workflow_id; @@ -93,6 +95,7 @@ export async function POST(request: Request) { workflow, workflow_api, version: 1, + snapshot: snapshot, }) .returning(); version = data[0].version; @@ -105,6 +108,7 @@ export async function POST(request: Request) { workflow: workflow, workflow_api, // version: sql`${workflowVersionTable.version} + 1`, + snapshot: snapshot, version: sql`( SELECT COALESCE(MAX(version), 0) + 1 FROM ${workflowVersionTable} diff --git a/web/src/db/schema.ts b/web/src/db/schema.ts index aa27b59..bb1c65d 100644 --- a/web/src/db/schema.ts +++ b/web/src/db/schema.ts @@ -69,6 +69,7 @@ export const workflowVersionTable = dbSchema.table("workflow_versions", { workflow: jsonb("workflow").$type>(), workflow_api: jsonb("workflow_api").$type>(), version: integer("version").notNull(), + snapshot: jsonb("snapshot").$type>(), created_at: timestamp("created_at").defaultNow().notNull(), updated_at: timestamp("updated_at").defaultNow().notNull(), @@ -201,10 +202,21 @@ export const machinesTable = dbSchema.table("machines", { auth_token: text("auth_token"), type: machinesType("type").notNull().default("classic"), status: machinesStatus("status").notNull().default("ready"), - snapshot: jsonb("snapshot").$type(), + snapshot: jsonb("snapshot").$type>(), build_log: text("build_log"), }); +export const snapshotType = z.object({ + comfyui: z.string(), + git_custom_nodes: z.record( + z.object({ + hash: z.string(), + disabled: z.boolean(), + }) + ), + file_custom_nodes: z.array(z.any()), +}); + export const insertMachineSchema = createInsertSchema(machinesTable, { name: (schema) => schema.name.default("My Machine"), endpoint: (schema) => schema.endpoint.default("http://127.0.0.1:8188"),