feat: add opening directly from comfy deploy -> machines
This commit is contained in:
		
							parent
							
								
									c0450b58d5
								
							
						
					
					
						commit
						52d6e07eeb
					
				@ -9,6 +9,62 @@ const ext = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  init(app) {
 | 
					  init(app) {
 | 
				
			||||||
    addButton();
 | 
					    addButton();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const queryParams = new URLSearchParams(window.location.search);
 | 
				
			||||||
 | 
					    const workflow_version_id = queryParams.get("workflow_version_id");
 | 
				
			||||||
 | 
					    const auth_token = queryParams.get("auth_token");
 | 
				
			||||||
 | 
					    const org_display = queryParams.get("org_display");
 | 
				
			||||||
 | 
					    const origin = queryParams.get("origin");
 | 
				
			||||||
 | 
					    if (!workflow_version_id) {
 | 
				
			||||||
 | 
					      console.error("No workflow_version_id provided in query parameters.");
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      const data = getData();
 | 
				
			||||||
 | 
					      let endpoint = data.endpoint;
 | 
				
			||||||
 | 
					      let apiKey = data.apiKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // If there is auth token override it
 | 
				
			||||||
 | 
					      if (auth_token) {
 | 
				
			||||||
 | 
					        apiKey = auth_token;
 | 
				
			||||||
 | 
					        endpoint = origin;
 | 
				
			||||||
 | 
					        saveData({
 | 
				
			||||||
 | 
					          displayName: org_display,
 | 
				
			||||||
 | 
					          endpoint: origin,
 | 
				
			||||||
 | 
					          apiKey: auth_token,
 | 
				
			||||||
 | 
					          displayName: org_display,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      loadingDialog.showLoading(
 | 
				
			||||||
 | 
					        "Loading workflow from " + org_display,
 | 
				
			||||||
 | 
					        "Please wait...",
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      fetch(endpoint + "/api/workflow-version/" + workflow_version_id, {
 | 
				
			||||||
 | 
					        method: "GET",
 | 
				
			||||||
 | 
					        headers: {
 | 
				
			||||||
 | 
					          "Content-Type": "application/json",
 | 
				
			||||||
 | 
					          Authorization: "Bearer " + apiKey,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					        .then(async (res) => {
 | 
				
			||||||
 | 
					          const data = await res.json();
 | 
				
			||||||
 | 
					          const { workflow, error } = data;
 | 
				
			||||||
 | 
					          if (error) {
 | 
				
			||||||
 | 
					            infoDialog.showMessage("Unable to load this workflow", error);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          /** @type {LGraph} */
 | 
				
			||||||
 | 
					          app.loadGraphData(workflow);
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .catch((e) => infoDialog.showMessage("Error", e.message))
 | 
				
			||||||
 | 
					        .finally(() => {
 | 
				
			||||||
 | 
					          loadingDialog.close();
 | 
				
			||||||
 | 
					          window.history.replaceState(
 | 
				
			||||||
 | 
					            {},
 | 
				
			||||||
 | 
					            document.title,
 | 
				
			||||||
 | 
					            window.location.pathname,
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  registerCustomNodes() {
 | 
					  registerCustomNodes() {
 | 
				
			||||||
@ -191,7 +247,7 @@ function addButton() {
 | 
				
			|||||||
      endpoint = endpoint.slice(0, -1);
 | 
					      endpoint = endpoint.slice(0, -1);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const apiRoute = endpoint + "/api/upload";
 | 
					    const apiRoute = endpoint + "/api/workflow";
 | 
				
			||||||
    // const userId = apiKey
 | 
					    // const userId = apiKey
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const body = {
 | 
					      const body = {
 | 
				
			||||||
@ -280,14 +336,16 @@ export class InfoDialog extends ComfyDialog {
 | 
				
			|||||||
    this.element.classList.add("comfy-normal-modal");
 | 
					    this.element.classList.add("comfy-normal-modal");
 | 
				
			||||||
    this.element.style.paddingBottom = "20px";
 | 
					    this.element.style.paddingBottom = "20px";
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  button = undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  createButtons() {
 | 
					  createButtons() {
 | 
				
			||||||
    return [
 | 
					    this.button = $el("button", {
 | 
				
			||||||
      $el("button", {
 | 
					 | 
				
			||||||
      type: "button",
 | 
					      type: "button",
 | 
				
			||||||
      textContent: "Close",
 | 
					      textContent: "Close",
 | 
				
			||||||
      onclick: () => this.close(),
 | 
					      onclick: () => this.close(),
 | 
				
			||||||
      }),
 | 
					    });
 | 
				
			||||||
    ];
 | 
					    return [this.button];
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  close() {
 | 
					  close() {
 | 
				
			||||||
@ -317,6 +375,58 @@ export class InfoDialog extends ComfyDialog {
 | 
				
			|||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      `);
 | 
					      `);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  loadingIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><g fill="none" stroke="#888888" stroke-linecap="round" stroke-width="2"><path stroke-dasharray="60" stroke-dashoffset="60" stroke-opacity=".3" d="M12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3Z"><animate fill="freeze" attributeName="stroke-dashoffset" dur="1.3s" values="60;0"/></path><path stroke-dasharray="15" stroke-dashoffset="15" d="M12 3C16.9706 3 21 7.02944 21 12"><animate fill="freeze" attributeName="stroke-dashoffset" dur="0.3s" values="15;0"/><animateTransform attributeName="transform" dur="1.5s" repeatCount="indefinite" type="rotate" values="0 12 12;360 12 12"/></path></g></svg>`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  showLoading(title, message) {
 | 
				
			||||||
 | 
					    this.show(`
 | 
				
			||||||
 | 
					      <div style="width: 400px; display: flex; gap: 18px; flex-direction: column; overflow: unset">
 | 
				
			||||||
 | 
					        <h3 style="margin: 0px; display: flex; align-items: center; justify-content: center;">${title} ${this.loadingIcon}</h3>
 | 
				
			||||||
 | 
					        <label>
 | 
				
			||||||
 | 
					          ${message}
 | 
				
			||||||
 | 
					        </label>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      `);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class LoadingDialog extends ComfyDialog {
 | 
				
			||||||
 | 
					  constructor() {
 | 
				
			||||||
 | 
					    super();
 | 
				
			||||||
 | 
					    this.element.classList.add("comfy-normal-modal");
 | 
				
			||||||
 | 
					    // this.element.style.paddingBottom = "20px";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  createButtons() {
 | 
				
			||||||
 | 
					    return [];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  close() {
 | 
				
			||||||
 | 
					    this.element.style.display = "none";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  show(html) {
 | 
				
			||||||
 | 
					    this.textElement.style["white-space"] = "normal";
 | 
				
			||||||
 | 
					    this.textElement.style.color = "white";
 | 
				
			||||||
 | 
					    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;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  loadingIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><g fill="none" stroke="#888888" stroke-linecap="round" stroke-width="2"><path stroke-dasharray="60" stroke-dashoffset="60" stroke-opacity=".3" d="M12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3Z"><animate fill="freeze" attributeName="stroke-dashoffset" dur="1.3s" values="60;0"/></path><path stroke-dasharray="15" stroke-dashoffset="15" d="M12 3C16.9706 3 21 7.02944 21 12"><animate fill="freeze" attributeName="stroke-dashoffset" dur="0.3s" values="15;0"/><animateTransform attributeName="transform" dur="1.5s" repeatCount="indefinite" type="rotate" values="0 12 12;360 12 12"/></path></g></svg>`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  showLoading(title, message) {
 | 
				
			||||||
 | 
					    this.show(`
 | 
				
			||||||
 | 
					      <div style="width: 400px; display: flex; gap: 18px; flex-direction: column; overflow: unset">
 | 
				
			||||||
 | 
					        <h3 style="margin: 0px; display: flex; align-items: center; justify-content: center; gap: 4px;">${title} ${this.loadingIcon}</h3>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      `);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class InputDialog extends InfoDialog {
 | 
					export class InputDialog extends InfoDialog {
 | 
				
			||||||
@ -444,9 +554,15 @@ export class ConfirmDialog extends InfoDialog {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const inputDialog = new InputDialog();
 | 
					export const inputDialog = new InputDialog();
 | 
				
			||||||
 | 
					export const loadingDialog = new LoadingDialog();
 | 
				
			||||||
export const infoDialog = new InfoDialog();
 | 
					export const infoDialog = new InfoDialog();
 | 
				
			||||||
export const confirmDialog = new ConfirmDialog();
 | 
					export const confirmDialog = new ConfirmDialog();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Retrieves deployment data from local storage or defaults.
 | 
				
			||||||
 | 
					 * @param {string} [environment] - The environment to get the data for.
 | 
				
			||||||
 | 
					 * @returns {{endpoint: string, apiKey: string, displayName: string, environment?: string}} The deployment data.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
function getData(environment) {
 | 
					function getData(environment) {
 | 
				
			||||||
  const deployOption =
 | 
					  const deployOption =
 | 
				
			||||||
    environment || localStorage.getItem("comfy_deploy_env") || "cloud";
 | 
					    environment || localStorage.getItem("comfy_deploy_env") || "cloud";
 | 
				
			||||||
@ -469,6 +585,17 @@ function getData(environment) {
 | 
				
			|||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Retrieves deployment data from local storage or defaults.
 | 
				
			||||||
 | 
					 * @param {{endpoint: string, apiKey: string, displayName: string, environment?: string}} [data] - The environment to get the data for.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					function saveData(data) {
 | 
				
			||||||
 | 
					  localStorage.setItem(
 | 
				
			||||||
 | 
					    "comfy_deploy_env_data_" + data.environment,
 | 
				
			||||||
 | 
					    JSON.stringify(data),
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class ConfigDialog extends ComfyDialog {
 | 
					export class ConfigDialog extends ComfyDialog {
 | 
				
			||||||
  container = null;
 | 
					  container = null;
 | 
				
			||||||
  poll = null;
 | 
					  poll = null;
 | 
				
			||||||
@ -527,15 +654,12 @@ export class ConfigDialog extends ComfyDialog {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const endpoint = this.container.querySelector("#endpoint").value;
 | 
					    const endpoint = this.container.querySelector("#endpoint").value;
 | 
				
			||||||
    const apiKey = api_key ?? this.container.querySelector("#apiKey").value;
 | 
					    const apiKey = api_key ?? this.container.querySelector("#apiKey").value;
 | 
				
			||||||
    const data = {
 | 
					    saveData({
 | 
				
			||||||
      endpoint,
 | 
					      endpoint,
 | 
				
			||||||
      apiKey,
 | 
					      apiKey,
 | 
				
			||||||
      displayName,
 | 
					      displayName,
 | 
				
			||||||
    };
 | 
					      environment: deployOption,
 | 
				
			||||||
    localStorage.setItem(
 | 
					    });
 | 
				
			||||||
      "comfy_deploy_env_data_" + deployOption,
 | 
					 | 
				
			||||||
      JSON.stringify(data),
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
    this.close();
 | 
					    this.close();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,8 @@ import { handle } from "hono/vercel";
 | 
				
			|||||||
import { app } from "../../../../routes/app";
 | 
					import { app } from "../../../../routes/app";
 | 
				
			||||||
import { registerWorkflowUploadRoute } from "@/routes/registerWorkflowUploadRoute";
 | 
					import { registerWorkflowUploadRoute } from "@/routes/registerWorkflowUploadRoute";
 | 
				
			||||||
import { registerGetAuthResponse } from "@/routes/registerGetAuthResponse";
 | 
					import { registerGetAuthResponse } from "@/routes/registerGetAuthResponse";
 | 
				
			||||||
 | 
					import { registerGetWorkflowRoute } from "@/routes/registerGetWorkflow";
 | 
				
			||||||
 | 
					import { cors } from "hono/cors";
 | 
				
			||||||
export const dynamic = "force-dynamic";
 | 
					export const dynamic = "force-dynamic";
 | 
				
			||||||
export const maxDuration = 300; // 5 minutes
 | 
					export const maxDuration = 300; // 5 minutes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -18,17 +19,24 @@ declare module "hono" {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function checkAuth(c: Context, next: Next) {
 | 
					async function checkAuth(c: Context, next: Next, headers?: HeadersInit) {
 | 
				
			||||||
  const token = c.req.raw.headers.get("Authorization")?.split(" ")?.[1]; // Assuming token is sent as "Bearer your_token"
 | 
					  const token = c.req.raw.headers.get("Authorization")?.split(" ")?.[1]; // Assuming token is sent as "Bearer your_token"
 | 
				
			||||||
  const userData = token ? parseJWT(token) : undefined;
 | 
					  const userData = token ? parseJWT(token) : undefined;
 | 
				
			||||||
  if (!userData || token === undefined) {
 | 
					  if (!userData || token === undefined) {
 | 
				
			||||||
    return c.text("Invalid or expired token", 401);
 | 
					    return c.text("Invalid or expired token", {
 | 
				
			||||||
 | 
					      status: 401,
 | 
				
			||||||
 | 
					      headers: headers,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // If the key has expiration, this is a temporary key and not in our db, so we can skip checking
 | 
					  // If the key has expiration, this is a temporary key and not in our db, so we can skip checking
 | 
				
			||||||
  if (userData.exp === undefined) {
 | 
					  if (userData.exp === undefined) {
 | 
				
			||||||
    const revokedKey = await isKeyRevoked(token);
 | 
					    const revokedKey = await isKeyRevoked(token);
 | 
				
			||||||
    if (revokedKey) return c.text("Revoked token", 401);
 | 
					    if (revokedKey)
 | 
				
			||||||
 | 
					      return c.text("Revoked token", {
 | 
				
			||||||
 | 
					        status: 401,
 | 
				
			||||||
 | 
					        headers: headers,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  c.set("apiKeyTokenData", userData);
 | 
					  c.set("apiKeyTokenData", userData);
 | 
				
			||||||
@ -36,9 +44,29 @@ async function checkAuth(c: Context, next: Next) {
 | 
				
			|||||||
  await next();
 | 
					  await next();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function checkAuthCORS(c: Context, next: Next) {
 | 
				
			||||||
 | 
					  return checkAuth(c, next, {
 | 
				
			||||||
 | 
					    "Access-Control-Allow-Origin": "*",
 | 
				
			||||||
 | 
					    "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
 | 
				
			||||||
 | 
					    "Access-Control-Allow-Headers": "Content-Type, Authorization",
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
app.use("/run", checkAuth);
 | 
					app.use("/run", checkAuth);
 | 
				
			||||||
app.use("/upload-url", checkAuth);
 | 
					app.use("/upload-url", checkAuth);
 | 
				
			||||||
app.use("/upload-workflow", checkAuth);
 | 
					
 | 
				
			||||||
 | 
					const corsHandler = cors({
 | 
				
			||||||
 | 
					  origin: "*",
 | 
				
			||||||
 | 
					  allowHeaders: ["Authorization", "Content-Type"],
 | 
				
			||||||
 | 
					  allowMethods: ["POST", "GET", "OPTIONS"],
 | 
				
			||||||
 | 
					  exposeHeaders: ["Content-Length"],
 | 
				
			||||||
 | 
					  maxAge: 600,
 | 
				
			||||||
 | 
					  credentials: true,
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CORS Check
 | 
				
			||||||
 | 
					app.use("/workflow", corsHandler, checkAuth);
 | 
				
			||||||
 | 
					app.use("/workflow-version/*", corsHandler, checkAuth);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// create run endpoint
 | 
					// create run endpoint
 | 
				
			||||||
registerCreateRunRoute(app);
 | 
					registerCreateRunRoute(app);
 | 
				
			||||||
@ -47,9 +75,12 @@ registerGetOutputRoute(app);
 | 
				
			|||||||
// file upload endpoint
 | 
					// file upload endpoint
 | 
				
			||||||
registerUploadRoute(app);
 | 
					registerUploadRoute(app);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
registerWorkflowUploadRoute(app);
 | 
					// Anon
 | 
				
			||||||
registerGetAuthResponse(app);
 | 
					registerGetAuthResponse(app);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					registerWorkflowUploadRoute(app);
 | 
				
			||||||
 | 
					registerGetWorkflowRoute(app);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// The OpenAPI documentation will be available at /doc
 | 
					// The OpenAPI documentation will be available at /doc
 | 
				
			||||||
app.doc("/doc", {
 | 
					app.doc("/doc", {
 | 
				
			||||||
  openapi: "3.0.0",
 | 
					  openapi: "3.0.0",
 | 
				
			||||||
@ -76,3 +107,4 @@ const handler = handle(app);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const GET = handler;
 | 
					export const GET = handler;
 | 
				
			||||||
export const POST = handler;
 | 
					export const POST = handler;
 | 
				
			||||||
 | 
					export const OPTIONS = handler;
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@ import {
 | 
				
			|||||||
  CopyWorkflowVersion,
 | 
					  CopyWorkflowVersion,
 | 
				
			||||||
  CreateDeploymentButton,
 | 
					  CreateDeploymentButton,
 | 
				
			||||||
  MachineSelect,
 | 
					  MachineSelect,
 | 
				
			||||||
 | 
					  OpenEditButton,
 | 
				
			||||||
  RunWorkflowButton,
 | 
					  RunWorkflowButton,
 | 
				
			||||||
  VersionSelect,
 | 
					  VersionSelect,
 | 
				
			||||||
  ViewWorkflowDetailsButton,
 | 
					  ViewWorkflowDetailsButton,
 | 
				
			||||||
@ -48,6 +49,7 @@ export default async function Page({
 | 
				
			|||||||
          <CreateShareButton workflow={workflow} machines={machines} />
 | 
					          <CreateShareButton workflow={workflow} machines={machines} />
 | 
				
			||||||
          <CopyWorkflowVersion workflow={workflow} />
 | 
					          <CopyWorkflowVersion workflow={workflow} />
 | 
				
			||||||
          <ViewWorkflowDetailsButton workflow={workflow} />
 | 
					          <ViewWorkflowDetailsButton workflow={workflow} />
 | 
				
			||||||
 | 
					          <OpenEditButton workflow={workflow} machines={machines} />
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <VersionDetails workflow={workflow} />
 | 
					        <VersionDetails workflow={workflow} />
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
import { setInitialUserData } from "../../../lib/setInitialUserData";
 | 
					import { setInitialUserData } from "../../../lib/setInitialUserData";
 | 
				
			||||||
import { getAllUserWorkflow } from "../../../server/getAllUserWorkflow";
 | 
					import { getAllUserWorkflow } from "../../../server/crudWorkflow";
 | 
				
			||||||
import { WorkflowList } from "@/components/WorkflowList";
 | 
					import { WorkflowList } from "@/components/WorkflowList";
 | 
				
			||||||
import { db } from "@/db/db";
 | 
					import { db } from "@/db/db";
 | 
				
			||||||
import { usersTable } from "@/db/schema";
 | 
					import { usersTable } from "@/db/schema";
 | 
				
			||||||
 | 
				
			|||||||
@ -41,7 +41,14 @@ import { checkStatus, createRun } from "@/server/createRun";
 | 
				
			|||||||
import { createDeployments } from "@/server/curdDeploments";
 | 
					import { createDeployments } from "@/server/curdDeploments";
 | 
				
			||||||
import type { getMachines } from "@/server/curdMachine";
 | 
					import type { getMachines } from "@/server/curdMachine";
 | 
				
			||||||
import type { findFirstTableWithVersion } from "@/server/findFirstTableWithVersion";
 | 
					import type { findFirstTableWithVersion } from "@/server/findFirstTableWithVersion";
 | 
				
			||||||
import { Copy, ExternalLink, Info, MoreVertical, Play } from "lucide-react";
 | 
					import {
 | 
				
			||||||
 | 
					  Copy,
 | 
				
			||||||
 | 
					  Edit,
 | 
				
			||||||
 | 
					  ExternalLink,
 | 
				
			||||||
 | 
					  Info,
 | 
				
			||||||
 | 
					  MoreVertical,
 | 
				
			||||||
 | 
					  Play,
 | 
				
			||||||
 | 
					} from "lucide-react";
 | 
				
			||||||
import { parseAsInteger, useQueryState } from "next-usequerystate";
 | 
					import { parseAsInteger, useQueryState } from "next-usequerystate";
 | 
				
			||||||
import { useEffect, useMemo, useState } from "react";
 | 
					import { useEffect, useMemo, useState } from "react";
 | 
				
			||||||
import { toast } from "sonner";
 | 
					import { toast } from "sonner";
 | 
				
			||||||
@ -51,6 +58,8 @@ import { create } from "zustand";
 | 
				
			|||||||
import { workflowVersionInputsToZod } from "../lib/workflowVersionInputsToZod";
 | 
					import { workflowVersionInputsToZod } from "../lib/workflowVersionInputsToZod";
 | 
				
			||||||
import { callServerPromise } from "./callServerPromise";
 | 
					import { callServerPromise } from "./callServerPromise";
 | 
				
			||||||
import fetcher from "./fetcher";
 | 
					import fetcher from "./fetcher";
 | 
				
			||||||
 | 
					import { ButtonAction } from "@/components/ButtonActionLoader";
 | 
				
			||||||
 | 
					import { editWorkflowOnMachine } from "@/server/editWorkflowOnMachine";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function VersionSelect({
 | 
					export function VersionSelect({
 | 
				
			||||||
  workflow,
 | 
					  workflow,
 | 
				
			||||||
@ -219,7 +228,7 @@ export function RunWorkflowButton({
 | 
				
			|||||||
  const schema = useMemo(() => {
 | 
					  const schema = useMemo(() => {
 | 
				
			||||||
    const workflow_version = getWorkflowVersionFromVersionIndex(
 | 
					    const workflow_version = getWorkflowVersionFromVersionIndex(
 | 
				
			||||||
      workflow,
 | 
					      workflow,
 | 
				
			||||||
      version
 | 
					      version,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!workflow_version) return null;
 | 
					    if (!workflow_version) return null;
 | 
				
			||||||
@ -233,7 +242,7 @@ export function RunWorkflowButton({
 | 
				
			|||||||
    const val = Object.keys(values).length > 0 ? values : undefined;
 | 
					    const val = Object.keys(values).length > 0 ? values : undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const workflow_version_id = workflow?.versions.find(
 | 
					    const workflow_version_id = workflow?.versions.find(
 | 
				
			||||||
      (x) => x.version === version
 | 
					      (x) => x.version === version,
 | 
				
			||||||
    )?.id;
 | 
					    )?.id;
 | 
				
			||||||
    console.log(workflow_version_id);
 | 
					    console.log(workflow_version_id);
 | 
				
			||||||
    if (!workflow_version_id) return;
 | 
					    if (!workflow_version_id) return;
 | 
				
			||||||
@ -248,7 +257,7 @@ export function RunWorkflowButton({
 | 
				
			|||||||
          machine_id: machine,
 | 
					          machine_id: machine,
 | 
				
			||||||
          inputs: val,
 | 
					          inputs: val,
 | 
				
			||||||
          runOrigin: "manual",
 | 
					          runOrigin: "manual",
 | 
				
			||||||
        })
 | 
					        }),
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      // console.log(res.json());
 | 
					      // console.log(res.json());
 | 
				
			||||||
      setIsLoading(false);
 | 
					      setIsLoading(false);
 | 
				
			||||||
@ -317,7 +326,7 @@ export function CreateDeploymentButton({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  const [isLoading, setIsLoading] = useState(false);
 | 
					  const [isLoading, setIsLoading] = useState(false);
 | 
				
			||||||
  const workflow_version_id = workflow?.versions.find(
 | 
					  const workflow_version_id = workflow?.versions.find(
 | 
				
			||||||
    (x) => x.version === version
 | 
					    (x) => x.version === version,
 | 
				
			||||||
  )?.id;
 | 
					  )?.id;
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <DropdownMenu>
 | 
					    <DropdownMenu>
 | 
				
			||||||
@ -337,8 +346,8 @@ export function CreateDeploymentButton({
 | 
				
			|||||||
                workflow.id,
 | 
					                workflow.id,
 | 
				
			||||||
                workflow_version_id,
 | 
					                workflow_version_id,
 | 
				
			||||||
                machine,
 | 
					                machine,
 | 
				
			||||||
                "production"
 | 
					                "production",
 | 
				
			||||||
              )
 | 
					              ),
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
            setIsLoading(false);
 | 
					            setIsLoading(false);
 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
@ -355,8 +364,8 @@ export function CreateDeploymentButton({
 | 
				
			|||||||
                workflow.id,
 | 
					                workflow.id,
 | 
				
			||||||
                workflow_version_id,
 | 
					                workflow_version_id,
 | 
				
			||||||
                machine,
 | 
					                machine,
 | 
				
			||||||
                "staging"
 | 
					                "staging",
 | 
				
			||||||
              )
 | 
					              ),
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
            setIsLoading(false);
 | 
					            setIsLoading(false);
 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
@ -368,6 +377,49 @@ export function CreateDeploymentButton({
 | 
				
			|||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function OpenEditButton({
 | 
				
			||||||
 | 
					  workflow,
 | 
				
			||||||
 | 
					  machines,
 | 
				
			||||||
 | 
					}: {
 | 
				
			||||||
 | 
					  workflow: Awaited<ReturnType<typeof findFirstTableWithVersion>>;
 | 
				
			||||||
 | 
					  machines: Awaited<ReturnType<typeof getMachines>>;
 | 
				
			||||||
 | 
					}) {
 | 
				
			||||||
 | 
					  const [version] = useQueryState("version", {
 | 
				
			||||||
 | 
					    defaultValue: workflow?.versions[0].version ?? 1,
 | 
				
			||||||
 | 
					    ...parseAsInteger,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  const [machine] = useSelectedMachine(machines);
 | 
				
			||||||
 | 
					  const workflow_version_id = workflow?.versions.find(
 | 
				
			||||||
 | 
					    (x) => x.version == version,
 | 
				
			||||||
 | 
					  )?.id;
 | 
				
			||||||
 | 
					  const [isLoading, setIsLoading] = useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    workflow_version_id &&
 | 
				
			||||||
 | 
					    machine && (
 | 
				
			||||||
 | 
					      <Button
 | 
				
			||||||
 | 
					        className="gap-2"
 | 
				
			||||||
 | 
					        onClick={async () => {
 | 
				
			||||||
 | 
					          setIsLoading(true);
 | 
				
			||||||
 | 
					          const url = await callServerPromise(
 | 
				
			||||||
 | 
					            editWorkflowOnMachine(workflow_version_id, machine),
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					          if (url && typeof url !== "object") {
 | 
				
			||||||
 | 
					            window.open(url, "_blank");
 | 
				
			||||||
 | 
					          } else if (url && typeof url === "object" && url.error) {
 | 
				
			||||||
 | 
					            console.error(url.error);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          setIsLoading(false);
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					        // asChild
 | 
				
			||||||
 | 
					        variant="outline"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        Edit {isLoading ? <LoadingIcon /> : <Edit size={14} />}
 | 
				
			||||||
 | 
					      </Button>
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function CopyWorkflowVersion({
 | 
					export function CopyWorkflowVersion({
 | 
				
			||||||
  workflow,
 | 
					  workflow,
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
@ -378,7 +430,7 @@ export function CopyWorkflowVersion({
 | 
				
			|||||||
    ...parseAsInteger,
 | 
					    ...parseAsInteger,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
  const workflow_version = workflow?.versions.find(
 | 
					  const workflow_version = workflow?.versions.find(
 | 
				
			||||||
    (x) => x.version === version
 | 
					    (x) => x.version === version,
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <DropdownMenu>
 | 
					    <DropdownMenu>
 | 
				
			||||||
@ -402,7 +454,7 @@ export function CopyWorkflowVersion({
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            navigator.clipboard.writeText(
 | 
					            navigator.clipboard.writeText(
 | 
				
			||||||
              JSON.stringify(workflow_version?.workflow)
 | 
					              JSON.stringify(workflow_version?.workflow),
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
            toast("Copied to clipboard");
 | 
					            toast("Copied to clipboard");
 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
@ -412,7 +464,7 @@ export function CopyWorkflowVersion({
 | 
				
			|||||||
        <DropdownMenuItem
 | 
					        <DropdownMenuItem
 | 
				
			||||||
          onClick={async () => {
 | 
					          onClick={async () => {
 | 
				
			||||||
            navigator.clipboard.writeText(
 | 
					            navigator.clipboard.writeText(
 | 
				
			||||||
              JSON.stringify(workflow_version?.workflow_api)
 | 
					              JSON.stringify(workflow_version?.workflow_api),
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
            toast("Copied to clipboard");
 | 
					            toast("Copied to clipboard");
 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
@ -426,7 +478,7 @@ export function CopyWorkflowVersion({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export function getWorkflowVersionFromVersionIndex(
 | 
					export function getWorkflowVersionFromVersionIndex(
 | 
				
			||||||
  workflow: Awaited<ReturnType<typeof findFirstTableWithVersion>>,
 | 
					  workflow: Awaited<ReturnType<typeof findFirstTableWithVersion>>,
 | 
				
			||||||
  version: number
 | 
					  version: number,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
  const workflow_version = workflow?.versions.find((x) => x.version == version);
 | 
					  const workflow_version = workflow?.versions.find((x) => x.version == version);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -452,7 +504,7 @@ export function ViewWorkflowDetailsButton({
 | 
				
			|||||||
    isLoading: isNodesIndexLoading,
 | 
					    isLoading: isNodesIndexLoading,
 | 
				
			||||||
  } = useSWR(
 | 
					  } = useSWR(
 | 
				
			||||||
    "https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main/extension-node-map.json",
 | 
					    "https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main/extension-node-map.json",
 | 
				
			||||||
    fetcher
 | 
					    fetcher,
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const groupedByAuxName = useMemo(() => {
 | 
					  const groupedByAuxName = useMemo(() => {
 | 
				
			||||||
@ -462,7 +514,7 @@ export function ViewWorkflowDetailsButton({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const workflow_version = getWorkflowVersionFromVersionIndex(
 | 
					    const workflow_version = getWorkflowVersionFromVersionIndex(
 | 
				
			||||||
      workflow,
 | 
					      workflow,
 | 
				
			||||||
      version
 | 
					      version,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const api = workflow_version?.workflow_api;
 | 
					    const api = workflow_version?.workflow_api;
 | 
				
			||||||
@ -473,7 +525,7 @@ export function ViewWorkflowDetailsButton({
 | 
				
			|||||||
      .map(([_, value]) => {
 | 
					      .map(([_, value]) => {
 | 
				
			||||||
        const classType = value.class_type;
 | 
					        const classType = value.class_type;
 | 
				
			||||||
        const classTypeData = Object.entries(data).find(([_, nodeArray]) =>
 | 
					        const classTypeData = Object.entries(data).find(([_, nodeArray]) =>
 | 
				
			||||||
          nodeArray[0].includes(classType)
 | 
					          nodeArray[0].includes(classType),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        return classTypeData ? { node: value, classTypeData } : null;
 | 
					        return classTypeData ? { node: value, classTypeData } : null;
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
@ -503,7 +555,7 @@ export function ViewWorkflowDetailsButton({
 | 
				
			|||||||
          node: z.infer<typeof workflowAPINodeType>[];
 | 
					          node: z.infer<typeof workflowAPINodeType>[];
 | 
				
			||||||
          url: string;
 | 
					          url: string;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      >
 | 
					      >,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // console.log(groupedByAuxName);
 | 
					    // console.log(groupedByAuxName);
 | 
				
			||||||
@ -544,7 +596,8 @@ export function ViewWorkflowDetailsButton({
 | 
				
			|||||||
                        <a
 | 
					                        <a
 | 
				
			||||||
                          href={group.url}
 | 
					                          href={group.url}
 | 
				
			||||||
                          target="_blank"
 | 
					                          target="_blank"
 | 
				
			||||||
                          className="hover:underline" rel="noreferrer"
 | 
					                          className="hover:underline"
 | 
				
			||||||
 | 
					                          rel="noreferrer"
 | 
				
			||||||
                        >
 | 
					                        >
 | 
				
			||||||
                          {key}
 | 
					                          {key}
 | 
				
			||||||
                          <ExternalLink
 | 
					                          <ExternalLink
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ import {
 | 
				
			|||||||
  timestamp,
 | 
					  timestamp,
 | 
				
			||||||
  uuid,
 | 
					  uuid,
 | 
				
			||||||
} from "drizzle-orm/pg-core";
 | 
					} from "drizzle-orm/pg-core";
 | 
				
			||||||
import { createInsertSchema } from "drizzle-zod";
 | 
					import { createInsertSchema, createSelectSchema } from "drizzle-zod";
 | 
				
			||||||
import { z } from "zod";
 | 
					import { z } from "zod";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const dbSchema = pgSchema("comfyui_deploy");
 | 
					export const dbSchema = pgSchema("comfyui_deploy");
 | 
				
			||||||
@ -35,6 +35,8 @@ export const workflowTable = dbSchema.table("workflows", {
 | 
				
			|||||||
  updated_at: timestamp("updated_at").defaultNow().notNull(),
 | 
					  updated_at: timestamp("updated_at").defaultNow().notNull(),
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const workflowSchema = createSelectSchema(workflowTable);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const workflowRelations = relations(workflowTable, ({ many, one }) => ({
 | 
					export const workflowRelations = relations(workflowTable, ({ many, one }) => ({
 | 
				
			||||||
  user: one(usersTable, {
 | 
					  user: one(usersTable, {
 | 
				
			||||||
    fields: [workflowTable.user_id],
 | 
					    fields: [workflowTable.user_id],
 | 
				
			||||||
@ -79,6 +81,7 @@ export const workflowVersionTable = dbSchema.table("workflow_versions", {
 | 
				
			|||||||
  created_at: timestamp("created_at").defaultNow().notNull(),
 | 
					  created_at: timestamp("created_at").defaultNow().notNull(),
 | 
				
			||||||
  updated_at: timestamp("updated_at").defaultNow().notNull(),
 | 
					  updated_at: timestamp("updated_at").defaultNow().notNull(),
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					export const workflowVersionSchema = createSelectSchema(workflowVersionTable);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const workflowVersionRelations = relations(
 | 
					export const workflowVersionRelations = relations(
 | 
				
			||||||
  workflowVersionTable,
 | 
					  workflowVersionTable,
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										86
									
								
								web/src/routes/registerGetWorkflow.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								web/src/routes/registerGetWorkflow.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,86 @@
 | 
				
			|||||||
 | 
					import { workflowVersionSchema } from "@/db/schema";
 | 
				
			||||||
 | 
					import type { App } from "@/routes/app";
 | 
				
			||||||
 | 
					import { authError } from "@/routes/authError";
 | 
				
			||||||
 | 
					import { getWorkflowVersion } from "@/server/crudWorkflow";
 | 
				
			||||||
 | 
					import { z, createRoute } from "@hono/zod-openapi";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const route = createRoute({
 | 
				
			||||||
 | 
					  method: "get",
 | 
				
			||||||
 | 
					  path: "/workflow-version/:version_id",
 | 
				
			||||||
 | 
					  tags: ["comfyui"],
 | 
				
			||||||
 | 
					  summary: "Get comfyui workflow",
 | 
				
			||||||
 | 
					  description: "Use this to retrieve comfyui workflow by id",
 | 
				
			||||||
 | 
					  request: {
 | 
				
			||||||
 | 
					    params: z.object({
 | 
				
			||||||
 | 
					      version_id: z.string(),
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  responses: {
 | 
				
			||||||
 | 
					    200: {
 | 
				
			||||||
 | 
					      content: {
 | 
				
			||||||
 | 
					        "application/json": {
 | 
				
			||||||
 | 
					          schema: workflowVersionSchema,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      description: "Retrieve the output",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    500: {
 | 
				
			||||||
 | 
					      content: {
 | 
				
			||||||
 | 
					        "application/json": {
 | 
				
			||||||
 | 
					          schema: z.object({
 | 
				
			||||||
 | 
					            error: z.string(),
 | 
				
			||||||
 | 
					          }),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      description: "Error when uploading the workflow",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    ...authError,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const registerGetWorkflowRoute = (app: App) => {
 | 
				
			||||||
 | 
					  return app.openapi(route, async (c) => {
 | 
				
			||||||
 | 
					    const { version_id } = c.req.valid("param");
 | 
				
			||||||
 | 
					    const apiUser = c.get("apiKeyTokenData")!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!apiUser.user_id)
 | 
				
			||||||
 | 
					      return c.json(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          error: "Invalid user_id",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          status: 500,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const workflow_version = await getWorkflowVersion(apiUser, version_id);
 | 
				
			||||||
 | 
					      if (workflow_version) {
 | 
				
			||||||
 | 
					        return c.json(workflow_version, {
 | 
				
			||||||
 | 
					          status: 200,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        return c.json(
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            error: "No version found",
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            status: 500,
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (error: unknown) {
 | 
				
			||||||
 | 
					      const errorMessage =
 | 
				
			||||||
 | 
					        error instanceof Error ? error.message : "Unknown error";
 | 
				
			||||||
 | 
					      return c.json(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          error: errorMessage,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          statusText: "Invalid request",
 | 
				
			||||||
 | 
					          status: 500,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -1,4 +1,10 @@
 | 
				
			|||||||
import { snapshotType, workflowAPIType, workflowType } from "@/db/schema";
 | 
					import { db } from "@/db/db";
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  snapshotType,
 | 
				
			||||||
 | 
					  workflowAPIType,
 | 
				
			||||||
 | 
					  workflowTable,
 | 
				
			||||||
 | 
					  workflowType,
 | 
				
			||||||
 | 
					} from "@/db/schema";
 | 
				
			||||||
import type { App } from "@/routes/app";
 | 
					import type { App } from "@/routes/app";
 | 
				
			||||||
import { authError } from "@/routes/authError";
 | 
					import { authError } from "@/routes/authError";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
@ -6,10 +12,11 @@ import {
 | 
				
			|||||||
  createNewWorkflowVersion,
 | 
					  createNewWorkflowVersion,
 | 
				
			||||||
} from "@/server/createNewWorkflow";
 | 
					} from "@/server/createNewWorkflow";
 | 
				
			||||||
import { z, createRoute } from "@hono/zod-openapi";
 | 
					import { z, createRoute } from "@hono/zod-openapi";
 | 
				
			||||||
 | 
					import { and, eq } from "drizzle-orm";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const route = createRoute({
 | 
					const route = createRoute({
 | 
				
			||||||
  method: "post",
 | 
					  method: "post",
 | 
				
			||||||
  path: "/upload-workflow",
 | 
					  path: "/workflow",
 | 
				
			||||||
  tags: ["comfyui"],
 | 
					  tags: ["comfyui"],
 | 
				
			||||||
  summary: "Upload workflow from ComfyUI",
 | 
					  summary: "Upload workflow from ComfyUI",
 | 
				
			||||||
  description:
 | 
					  description:
 | 
				
			||||||
@ -106,6 +113,30 @@ export const registerWorkflowUploadRoute = (app: App) => {
 | 
				
			|||||||
        workflow_id = _workflow_id;
 | 
					        workflow_id = _workflow_id;
 | 
				
			||||||
        version = _version;
 | 
					        version = _version;
 | 
				
			||||||
      } else if (workflow_id) {
 | 
					      } else if (workflow_id) {
 | 
				
			||||||
 | 
					        const workflow = await db
 | 
				
			||||||
 | 
					          .select()
 | 
				
			||||||
 | 
					          .from(workflowTable)
 | 
				
			||||||
 | 
					          .where(
 | 
				
			||||||
 | 
					            and(
 | 
				
			||||||
 | 
					              eq(workflowTable.id, workflow_id),
 | 
				
			||||||
 | 
					              eq(workflowTable.user_id, user_id),
 | 
				
			||||||
 | 
					              eq(workflowTable.org_id, org_id),
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (workflow.length === 0) {
 | 
				
			||||||
 | 
					          return c.json(
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              error: "Invalid workflow_id",
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              status: 500,
 | 
				
			||||||
 | 
					              statusText: "Invalid workflow_id",
 | 
				
			||||||
 | 
					              headers: corsHeaders,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Case 2 update workflow
 | 
					        // Case 2 update workflow
 | 
				
			||||||
        const { version: _version } = await createNewWorkflowVersion({
 | 
					        const { version: _version } = await createNewWorkflowVersion({
 | 
				
			||||||
          workflow_id: workflow_id,
 | 
					          workflow_id: workflow_id,
 | 
				
			||||||
 | 
				
			|||||||
@ -4,8 +4,10 @@ import {
 | 
				
			|||||||
  workflowTable,
 | 
					  workflowTable,
 | 
				
			||||||
  workflowVersionTable,
 | 
					  workflowVersionTable,
 | 
				
			||||||
} from "@/db/schema";
 | 
					} from "@/db/schema";
 | 
				
			||||||
 | 
					import { APIKeyUserType } from "@/server/APIKeyBodyRequest";
 | 
				
			||||||
import { auth } from "@clerk/nextjs";
 | 
					import { auth } from "@clerk/nextjs";
 | 
				
			||||||
import { and, desc, eq, isNull } from "drizzle-orm";
 | 
					import { and, desc, eq, isNull } from "drizzle-orm";
 | 
				
			||||||
 | 
					import { redirect } from "next/navigation";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function getAllUserWorkflow() {
 | 
					export async function getAllUserWorkflow() {
 | 
				
			||||||
  const { userId, orgId } = await auth();
 | 
					  const { userId, orgId } = await auth();
 | 
				
			||||||
@ -51,3 +53,29 @@ export async function getAllUserWorkflow() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  return workflow;
 | 
					  return workflow;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function getWorkflowVersion(
 | 
				
			||||||
 | 
					  apiUser: APIKeyUserType,
 | 
				
			||||||
 | 
					  version_id: string,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					  const { org_id, user_id } = apiUser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!user_id) {
 | 
				
			||||||
 | 
					    throw new Error("No user id");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const parentWorkflow = await db.query.workflowTable.findFirst({
 | 
				
			||||||
 | 
					    where:
 | 
				
			||||||
 | 
					      org_id != undefined
 | 
				
			||||||
 | 
					        ? eq(workflowTable.org_id, org_id)
 | 
				
			||||||
 | 
					        : and(eq(workflowTable.user_id, user_id), isNull(workflowTable.org_id)),
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!parentWorkflow) {
 | 
				
			||||||
 | 
					    throw new Error("No workflow found");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return db.query.workflowVersionTable.findFirst({
 | 
				
			||||||
 | 
					    where: eq(workflowVersionTable.id, version_id),
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -151,7 +151,8 @@ export const removePublicShareDeployment = withServerPromise(
 | 
				
			|||||||
          eq(deploymentsTable.environment, "public-share"),
 | 
					          eq(deploymentsTable.environment, "public-share"),
 | 
				
			||||||
          eq(deploymentsTable.id, deployment_id),
 | 
					          eq(deploymentsTable.id, deployment_id),
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
			).returning();
 | 
					      )
 | 
				
			||||||
 | 
					      .returning();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // revalidatePath(
 | 
					    // revalidatePath(
 | 
				
			||||||
    //   `/workflows/${removed.workflow_id}`
 | 
					    //   `/workflows/${removed.workflow_id}`
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										43
									
								
								web/src/server/editWorkflowOnMachine.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								web/src/server/editWorkflowOnMachine.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					"use server";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { getMachineById } from "@/server/curdMachine";
 | 
				
			||||||
 | 
					import { auth } from "@clerk/nextjs";
 | 
				
			||||||
 | 
					import jwt from "jsonwebtoken";
 | 
				
			||||||
 | 
					import { getOrgOrUserDisplayName } from "@/server/getOrgOrUserDisplayName";
 | 
				
			||||||
 | 
					import { withServerPromise } from "@/server/withServerPromise";
 | 
				
			||||||
 | 
					import "server-only";
 | 
				
			||||||
 | 
					import { headers } from "next/headers";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const editWorkflowOnMachine = withServerPromise(
 | 
				
			||||||
 | 
					  async (workflow_version_id: string, machine_id: string) => {
 | 
				
			||||||
 | 
					    const { userId, orgId } = auth();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const headersList = headers();
 | 
				
			||||||
 | 
					    const host = headersList.get("host") || "";
 | 
				
			||||||
 | 
					    const protocol = headersList.get("x-forwarded-proto") || "";
 | 
				
			||||||
 | 
					    const domain = `${protocol}://${host}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!userId) {
 | 
				
			||||||
 | 
					      throw new Error("No user id");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const machine = await getMachineById(machine_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const expireTime = "1w";
 | 
				
			||||||
 | 
					    const token = jwt.sign(
 | 
				
			||||||
 | 
					      { user_id: userId, org_id: orgId },
 | 
				
			||||||
 | 
					      process.env.JWT_SECRET!,
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        expiresIn: expireTime,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const userName = await getOrgOrUserDisplayName(orgId, userId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return `${
 | 
				
			||||||
 | 
					      machine.endpoint
 | 
				
			||||||
 | 
					    }?workflow_version_id=${workflow_version_id}&auth_token=${token}&org_display=${encodeURIComponent(
 | 
				
			||||||
 | 
					      userName,
 | 
				
			||||||
 | 
					    )}&origin=${encodeURIComponent(domain)}`;
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user