feat: add new auth_request flow for logging in with comfy deploy
This commit is contained in:
parent
3043093d22
commit
47168930dc
@ -120,7 +120,7 @@ function addButton() {
|
|||||||
const graph = app.graph;
|
const graph = app.graph;
|
||||||
|
|
||||||
const snapshot = await fetch("/snapshot/get_current").then((x) => x.json());
|
const snapshot = await fetch("/snapshot/get_current").then((x) => x.json());
|
||||||
console.log(snapshot);
|
// console.log(snapshot);
|
||||||
|
|
||||||
if (!snapshot) {
|
if (!snapshot) {
|
||||||
showError(
|
showError(
|
||||||
@ -154,7 +154,7 @@ function addButton() {
|
|||||||
|
|
||||||
const deployMetaNode = deployMeta[0];
|
const deployMetaNode = deployMeta[0];
|
||||||
|
|
||||||
console.log(deployMetaNode);
|
// console.log(deployMetaNode);
|
||||||
|
|
||||||
const workflow_name = deployMetaNode.widgets[0].value;
|
const workflow_name = deployMetaNode.widgets[0].value;
|
||||||
const workflow_id = deployMetaNode.widgets[1].value;
|
const workflow_id = deployMetaNode.widgets[1].value;
|
||||||
@ -168,20 +168,23 @@ function addButton() {
|
|||||||
// const endpoint = localStorage.getItem("endpoint") ?? "";
|
// const endpoint = localStorage.getItem("endpoint") ?? "";
|
||||||
// const apiKey = localStorage.getItem("apiKey");
|
// const apiKey = localStorage.getItem("apiKey");
|
||||||
|
|
||||||
const { endpoint, apiKey } = getData();
|
const { endpoint, apiKey, displayName } = getData();
|
||||||
|
|
||||||
if (!endpoint || !apiKey || apiKey === "" || endpoint === "") {
|
if (!endpoint || !apiKey || apiKey === "" || endpoint === "") {
|
||||||
configDialog.show();
|
configDialog.show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ok = await confirmDialog.confirm("Confirm deployment", "A new version will be deployed, are you conform?")
|
const ok = await confirmDialog.confirm(
|
||||||
|
"Confirm deployment -> " + displayName,
|
||||||
|
"A new version will be deployed, are you conform?",
|
||||||
|
);
|
||||||
if (!ok) return;
|
if (!ok) return;
|
||||||
|
|
||||||
title.innerText = "Deploying...";
|
title.innerText = "Deploying...";
|
||||||
title.style.color = "orange";
|
title.style.color = "orange";
|
||||||
|
|
||||||
console.log(prompt);
|
// console.log(prompt);
|
||||||
|
|
||||||
// TODO trim the ending / from endpoint is there is
|
// TODO trim the ending / from endpoint is there is
|
||||||
if (endpoint.endsWith("/")) {
|
if (endpoint.endsWith("/")) {
|
||||||
@ -191,15 +194,17 @@ function addButton() {
|
|||||||
const apiRoute = endpoint + "/api/upload";
|
const apiRoute = endpoint + "/api/upload";
|
||||||
// const userId = apiKey
|
// const userId = apiKey
|
||||||
try {
|
try {
|
||||||
|
const body = {
|
||||||
|
workflow_name,
|
||||||
|
workflow_id,
|
||||||
|
workflow: prompt.workflow,
|
||||||
|
workflow_api: prompt.output,
|
||||||
|
snapshot: snapshot,
|
||||||
|
};
|
||||||
|
console.log(body);
|
||||||
let data = await fetch(apiRoute, {
|
let data = await fetch(apiRoute, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({
|
body: JSON.stringify(body),
|
||||||
workflow_name,
|
|
||||||
workflow_id,
|
|
||||||
workflow: prompt.workflow,
|
|
||||||
workflow_api: prompt.output,
|
|
||||||
snapshot: snapshot,
|
|
||||||
}),
|
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: "Bearer " + apiKey,
|
Authorization: "Bearer " + apiKey,
|
||||||
@ -301,6 +306,17 @@ export class InfoDialog extends ComfyDialog {
|
|||||||
this.element.style.display = "flex";
|
this.element.style.display = "flex";
|
||||||
this.element.style.zIndex = 1001;
|
this.element.style.zIndex = 1001;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showMessage(title, message) {
|
||||||
|
this.show(`
|
||||||
|
<div style="width: 400px; display: flex; gap: 18px; flex-direction: column; overflow: unset">
|
||||||
|
<h3 style="margin: 0px;">${title}</h3>
|
||||||
|
<label>
|
||||||
|
${message}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class InputDialog extends InfoDialog {
|
export class InputDialog extends InfoDialog {
|
||||||
@ -367,7 +383,6 @@ export class InputDialog extends InfoDialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export class ConfirmDialog extends InfoDialog {
|
export class ConfirmDialog extends InfoDialog {
|
||||||
callback = undefined;
|
callback = undefined;
|
||||||
|
|
||||||
@ -456,6 +471,8 @@ function getData(environment) {
|
|||||||
|
|
||||||
export class ConfigDialog extends ComfyDialog {
|
export class ConfigDialog extends ComfyDialog {
|
||||||
container = null;
|
container = null;
|
||||||
|
poll = null;
|
||||||
|
timeout = null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
@ -498,17 +515,22 @@ export class ConfigDialog extends ComfyDialog {
|
|||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.element.style.display = "none";
|
this.element.style.display = "none";
|
||||||
|
clearInterval(this.poll);
|
||||||
|
clearTimeout(this.timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
save() {
|
save(api_key, displayName) {
|
||||||
|
if (!displayName) displayName = getData().displayName;
|
||||||
|
|
||||||
const deployOption = this.container.querySelector("#deployOption").value;
|
const deployOption = this.container.querySelector("#deployOption").value;
|
||||||
localStorage.setItem("comfy_deploy_env", deployOption);
|
localStorage.setItem("comfy_deploy_env", deployOption);
|
||||||
|
|
||||||
const endpoint = this.container.querySelector("#endpoint").value;
|
const endpoint = this.container.querySelector("#endpoint").value;
|
||||||
const apiKey = this.container.querySelector("#apiKey").value;
|
const apiKey = api_key ?? this.container.querySelector("#apiKey").value;
|
||||||
const data = {
|
const data = {
|
||||||
endpoint,
|
endpoint,
|
||||||
apiKey,
|
apiKey,
|
||||||
|
displayName,
|
||||||
};
|
};
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
"comfy_deploy_env_data_" + deployOption,
|
"comfy_deploy_env_data_" + deployOption,
|
||||||
@ -527,12 +549,8 @@ export class ConfigDialog extends ComfyDialog {
|
|||||||
<h3 style="margin: 0px;">Comfy Deploy Config</h3>
|
<h3 style="margin: 0px;">Comfy Deploy Config</h3>
|
||||||
<label style="color: white; width: 100%;">
|
<label style="color: white; width: 100%;">
|
||||||
<select id="deployOption" style="margin: 8px 0px; width: 100%; height:30px; box-sizing: border-box;" >
|
<select id="deployOption" style="margin: 8px 0px; width: 100%; height:30px; box-sizing: border-box;" >
|
||||||
<option value="cloud" ${
|
<option value="cloud" ${data.environment === "cloud" ? "selected" : ""}>Cloud</option>
|
||||||
data.environment === "cloud" ? "selected" : ""
|
<option value="local" ${data.environment === "local" ? "selected" : ""}>Local</option>
|
||||||
}>Cloud</option>
|
|
||||||
<option value="local" ${
|
|
||||||
data.environment === "local" ? "selected" : ""
|
|
||||||
}>Local</option>
|
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<label style="color: white; width: 100%;">
|
<label style="color: white; width: 100%;">
|
||||||
@ -542,16 +560,61 @@ export class ConfigDialog extends ComfyDialog {
|
|||||||
}">
|
}">
|
||||||
</label>
|
</label>
|
||||||
<label style="color: white;">
|
<label style="color: white;">
|
||||||
API Key:
|
API Key: ${data.displayName ?? ""}
|
||||||
<input id="apiKey" style="margin-top: 8px; width: 100%; height:40px; box-sizing: border-box; padding: 0px 6px;" type="password" value="${
|
<input id="apiKey" style="margin-top: 8px; width: 100%; height:40px; box-sizing: border-box; padding: 0px 6px;" type="password" value="${
|
||||||
data.apiKey
|
data.apiKey
|
||||||
}">
|
}">
|
||||||
|
<button id="loginButton" style="margin-top: 8px; width: 100%; height:40px; box-sizing: border-box; padding: 0px 6px;">
|
||||||
|
${
|
||||||
|
data.apiKey ? "Re-login with ComfyDeploy" : "Login with ComfyDeploy"
|
||||||
|
}
|
||||||
|
</button>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const button = this.container.querySelector("#loginButton");
|
||||||
|
button.onclick = () => {
|
||||||
|
const uuid =
|
||||||
|
Math.random().toString(36).substring(2, 15) +
|
||||||
|
Math.random().toString(36).substring(2, 15);
|
||||||
|
window.open(data.endpoint + "/auth-request/" + uuid, "_blank");
|
||||||
|
|
||||||
|
this.timeout = setTimeout(() => {
|
||||||
|
clearInterval(poll);
|
||||||
|
infoDialog.showMessage(
|
||||||
|
"Timeout",
|
||||||
|
"Wait too long for the response, please try re-login",
|
||||||
|
);
|
||||||
|
}, 30000); // Stop polling after 30 seconds
|
||||||
|
|
||||||
|
this.poll = setInterval(() => {
|
||||||
|
fetch(data.endpoint + "/api/auth-response/" + uuid)
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((json) => {
|
||||||
|
if (json.api_key) {
|
||||||
|
this.save(json.api_key, json.name);
|
||||||
|
this.container.querySelector("#apiKey").value = json.api_key;
|
||||||
|
infoDialog.show();
|
||||||
|
clearInterval(this.poll);
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
infoDialog.showMessage(
|
||||||
|
"Authenticated",
|
||||||
|
"You will be able to upload workflow to " + json.name,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Error:", error);
|
||||||
|
clearInterval(this.poll);
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
infoDialog.showMessage("Error", error);
|
||||||
|
});
|
||||||
|
}, 2000);
|
||||||
|
};
|
||||||
|
|
||||||
const apiKeyInput = this.container.querySelector("#apiKey");
|
const apiKeyInput = this.container.querySelector("#apiKey");
|
||||||
apiKeyInput.addEventListener("paste", function (e) {
|
apiKeyInput.addEventListener("paste", (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
BIN
web/bun.lockb
BIN
web/bun.lockb
Binary file not shown.
8
web/drizzle/0033_awesome_human_fly.sql
Normal file
8
web/drizzle/0033_awesome_human_fly.sql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS "comfyui_deploy"."auth_requests" (
|
||||||
|
"request_id" text PRIMARY KEY NOT NULL,
|
||||||
|
"user_id" text,
|
||||||
|
"org_id" text,
|
||||||
|
"api_hash" text,
|
||||||
|
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||||
|
"updated_at" timestamp DEFAULT now() NOT NULL
|
||||||
|
);
|
1
web/drizzle/0034_even_lady_ursula.sql
Normal file
1
web/drizzle/0034_even_lady_ursula.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "comfyui_deploy"."auth_requests" ADD COLUMN "expired_date" timestamp;
|
824
web/drizzle/meta/0033_snapshot.json
Normal file
824
web/drizzle/meta/0033_snapshot.json
Normal file
@ -0,0 +1,824 @@
|
|||||||
|
{
|
||||||
|
"id": "97662b25-3992-4859-9bdc-560e2a70daea",
|
||||||
|
"prevId": "1425ee00-66fb-4541-8da7-19b217944545",
|
||||||
|
"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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"auth_requests": {
|
||||||
|
"name": "auth_requests",
|
||||||
|
"schema": "comfyui_deploy",
|
||||||
|
"columns": {
|
||||||
|
"request_id": {
|
||||||
|
"name": "request_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"org_id": {
|
||||||
|
"name": "org_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"api_hash": {
|
||||||
|
"name": "api_hash",
|
||||||
|
"type": "text",
|
||||||
|
"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": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"deployments": {
|
||||||
|
"name": "deployments",
|
||||||
|
"schema": "comfyui_deploy",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"org_id": {
|
||||||
|
"name": "org_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"workflow_version_id": {
|
||||||
|
"name": "workflow_version_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"workflow_id": {
|
||||||
|
"name": "workflow_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"machine_id": {
|
||||||
|
"name": "machine_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"share_slug": {
|
||||||
|
"name": "share_slug",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"showcase_media": {
|
||||||
|
"name": "showcase_media",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"environment": {
|
||||||
|
"name": "environment",
|
||||||
|
"type": "deployment_environment",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"deployments_user_id_users_id_fk": {
|
||||||
|
"name": "deployments_user_id_users_id_fk",
|
||||||
|
"tableFrom": "deployments",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"deployments_workflow_version_id_workflow_versions_id_fk": {
|
||||||
|
"name": "deployments_workflow_version_id_workflow_versions_id_fk",
|
||||||
|
"tableFrom": "deployments",
|
||||||
|
"tableTo": "workflow_versions",
|
||||||
|
"columnsFrom": [
|
||||||
|
"workflow_version_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"deployments_workflow_id_workflows_id_fk": {
|
||||||
|
"name": "deployments_workflow_id_workflows_id_fk",
|
||||||
|
"tableFrom": "deployments",
|
||||||
|
"tableTo": "workflows",
|
||||||
|
"columnsFrom": [
|
||||||
|
"workflow_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"deployments_machine_id_machines_id_fk": {
|
||||||
|
"name": "deployments_machine_id_machines_id_fk",
|
||||||
|
"tableFrom": "deployments",
|
||||||
|
"tableTo": "machines",
|
||||||
|
"columnsFrom": [
|
||||||
|
"machine_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"deployments_share_slug_unique": {
|
||||||
|
"name": "deployments_share_slug_unique",
|
||||||
|
"nullsNotDistinct": false,
|
||||||
|
"columns": [
|
||||||
|
"share_slug"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"machines": {
|
||||||
|
"name": "machines",
|
||||||
|
"schema": "comfyui_deploy",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"org_id": {
|
||||||
|
"name": "org_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"endpoint": {
|
||||||
|
"name": "endpoint",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"name": "disabled",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"auth_token": {
|
||||||
|
"name": "auth_token",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"name": "type",
|
||||||
|
"type": "machine_type",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'classic'"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "machine_status",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'ready'"
|
||||||
|
},
|
||||||
|
"snapshot": {
|
||||||
|
"name": "snapshot",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"models": {
|
||||||
|
"name": "models",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"gpu": {
|
||||||
|
"name": "gpu",
|
||||||
|
"type": "machine_gpu",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"build_machine_instance_id": {
|
||||||
|
"name": "build_machine_instance_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"build_log": {
|
||||||
|
"name": "build_log",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"machines_user_id_users_id_fk": {
|
||||||
|
"name": "machines_user_id_users_id_fk",
|
||||||
|
"tableFrom": "machines",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"users": {
|
||||||
|
"name": "users",
|
||||||
|
"schema": "comfyui_deploy",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"name": "username",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"workflow_run_outputs": {
|
||||||
|
"name": "workflow_run_outputs",
|
||||||
|
"schema": "comfyui_deploy",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"run_id": {
|
||||||
|
"name": "run_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"name": "data",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"workflow_run_outputs_run_id_workflow_runs_id_fk": {
|
||||||
|
"name": "workflow_run_outputs_run_id_workflow_runs_id_fk",
|
||||||
|
"tableFrom": "workflow_run_outputs",
|
||||||
|
"tableTo": "workflow_runs",
|
||||||
|
"columnsFrom": [
|
||||||
|
"run_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"workflow_runs": {
|
||||||
|
"name": "workflow_runs",
|
||||||
|
"schema": "comfyui_deploy",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"workflow_version_id": {
|
||||||
|
"name": "workflow_version_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"workflow_inputs": {
|
||||||
|
"name": "workflow_inputs",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"workflow_id": {
|
||||||
|
"name": "workflow_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"machine_id": {
|
||||||
|
"name": "machine_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"origin": {
|
||||||
|
"name": "origin",
|
||||||
|
"type": "workflow_run_origin",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'api'"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "workflow_run_status",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'not-started'"
|
||||||
|
},
|
||||||
|
"ended_at": {
|
||||||
|
"name": "ended_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"started_at": {
|
||||||
|
"name": "started_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"workflow_runs_workflow_version_id_workflow_versions_id_fk": {
|
||||||
|
"name": "workflow_runs_workflow_version_id_workflow_versions_id_fk",
|
||||||
|
"tableFrom": "workflow_runs",
|
||||||
|
"tableTo": "workflow_versions",
|
||||||
|
"columnsFrom": [
|
||||||
|
"workflow_version_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "set null",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"workflow_runs_workflow_id_workflows_id_fk": {
|
||||||
|
"name": "workflow_runs_workflow_id_workflows_id_fk",
|
||||||
|
"tableFrom": "workflow_runs",
|
||||||
|
"tableTo": "workflows",
|
||||||
|
"columnsFrom": [
|
||||||
|
"workflow_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"workflow_runs_machine_id_machines_id_fk": {
|
||||||
|
"name": "workflow_runs_machine_id_machines_id_fk",
|
||||||
|
"tableFrom": "workflow_runs",
|
||||||
|
"tableTo": "machines",
|
||||||
|
"columnsFrom": [
|
||||||
|
"machine_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "set null",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"workflows": {
|
||||||
|
"name": "workflows",
|
||||||
|
"schema": "comfyui_deploy",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"org_id": {
|
||||||
|
"name": "org_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"workflows_user_id_users_id_fk": {
|
||||||
|
"name": "workflows_user_id_users_id_fk",
|
||||||
|
"tableFrom": "workflows",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"workflow_versions": {
|
||||||
|
"name": "workflow_versions",
|
||||||
|
"schema": "comfyui_deploy",
|
||||||
|
"columns": {
|
||||||
|
"workflow_id": {
|
||||||
|
"name": "workflow_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"workflow": {
|
||||||
|
"name": "workflow",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"workflow_api": {
|
||||||
|
"name": "workflow_api",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"name": "version",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"snapshot": {
|
||||||
|
"name": "snapshot",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"workflow_versions_workflow_id_workflows_id_fk": {
|
||||||
|
"name": "workflow_versions_workflow_id_workflows_id_fk",
|
||||||
|
"tableFrom": "workflow_versions",
|
||||||
|
"tableTo": "workflows",
|
||||||
|
"columnsFrom": [
|
||||||
|
"workflow_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"enums": {
|
||||||
|
"deployment_environment": {
|
||||||
|
"name": "deployment_environment",
|
||||||
|
"values": {
|
||||||
|
"staging": "staging",
|
||||||
|
"production": "production",
|
||||||
|
"public-share": "public-share"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"machine_gpu": {
|
||||||
|
"name": "machine_gpu",
|
||||||
|
"values": {
|
||||||
|
"T4": "T4",
|
||||||
|
"A10G": "A10G",
|
||||||
|
"A100": "A100"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"machine_status": {
|
||||||
|
"name": "machine_status",
|
||||||
|
"values": {
|
||||||
|
"ready": "ready",
|
||||||
|
"building": "building",
|
||||||
|
"error": "error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"machine_type": {
|
||||||
|
"name": "machine_type",
|
||||||
|
"values": {
|
||||||
|
"classic": "classic",
|
||||||
|
"runpod-serverless": "runpod-serverless",
|
||||||
|
"modal-serverless": "modal-serverless",
|
||||||
|
"comfy-deploy-serverless": "comfy-deploy-serverless"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"workflow_run_origin": {
|
||||||
|
"name": "workflow_run_origin",
|
||||||
|
"values": {
|
||||||
|
"manual": "manual",
|
||||||
|
"api": "api",
|
||||||
|
"public-share": "public-share"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"workflow_run_status": {
|
||||||
|
"name": "workflow_run_status",
|
||||||
|
"values": {
|
||||||
|
"not-started": "not-started",
|
||||||
|
"running": "running",
|
||||||
|
"uploading": "uploading",
|
||||||
|
"success": "success",
|
||||||
|
"failed": "failed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schemas": {
|
||||||
|
"comfyui_deploy": "comfyui_deploy"
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {},
|
||||||
|
"columns": {}
|
||||||
|
}
|
||||||
|
}
|
830
web/drizzle/meta/0034_snapshot.json
Normal file
830
web/drizzle/meta/0034_snapshot.json
Normal file
@ -0,0 +1,830 @@
|
|||||||
|
{
|
||||||
|
"id": "8d654f92-7f7e-420f-bbd3-73b6b27adf35",
|
||||||
|
"prevId": "97662b25-3992-4859-9bdc-560e2a70daea",
|
||||||
|
"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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"auth_requests": {
|
||||||
|
"name": "auth_requests",
|
||||||
|
"schema": "comfyui_deploy",
|
||||||
|
"columns": {
|
||||||
|
"request_id": {
|
||||||
|
"name": "request_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"org_id": {
|
||||||
|
"name": "org_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"api_hash": {
|
||||||
|
"name": "api_hash",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"expired_date": {
|
||||||
|
"name": "expired_date",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"deployments": {
|
||||||
|
"name": "deployments",
|
||||||
|
"schema": "comfyui_deploy",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"org_id": {
|
||||||
|
"name": "org_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"workflow_version_id": {
|
||||||
|
"name": "workflow_version_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"workflow_id": {
|
||||||
|
"name": "workflow_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"machine_id": {
|
||||||
|
"name": "machine_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"share_slug": {
|
||||||
|
"name": "share_slug",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"showcase_media": {
|
||||||
|
"name": "showcase_media",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"environment": {
|
||||||
|
"name": "environment",
|
||||||
|
"type": "deployment_environment",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"deployments_user_id_users_id_fk": {
|
||||||
|
"name": "deployments_user_id_users_id_fk",
|
||||||
|
"tableFrom": "deployments",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"deployments_workflow_version_id_workflow_versions_id_fk": {
|
||||||
|
"name": "deployments_workflow_version_id_workflow_versions_id_fk",
|
||||||
|
"tableFrom": "deployments",
|
||||||
|
"tableTo": "workflow_versions",
|
||||||
|
"columnsFrom": [
|
||||||
|
"workflow_version_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"deployments_workflow_id_workflows_id_fk": {
|
||||||
|
"name": "deployments_workflow_id_workflows_id_fk",
|
||||||
|
"tableFrom": "deployments",
|
||||||
|
"tableTo": "workflows",
|
||||||
|
"columnsFrom": [
|
||||||
|
"workflow_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"deployments_machine_id_machines_id_fk": {
|
||||||
|
"name": "deployments_machine_id_machines_id_fk",
|
||||||
|
"tableFrom": "deployments",
|
||||||
|
"tableTo": "machines",
|
||||||
|
"columnsFrom": [
|
||||||
|
"machine_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"deployments_share_slug_unique": {
|
||||||
|
"name": "deployments_share_slug_unique",
|
||||||
|
"nullsNotDistinct": false,
|
||||||
|
"columns": [
|
||||||
|
"share_slug"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"machines": {
|
||||||
|
"name": "machines",
|
||||||
|
"schema": "comfyui_deploy",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"org_id": {
|
||||||
|
"name": "org_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"endpoint": {
|
||||||
|
"name": "endpoint",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"name": "disabled",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"auth_token": {
|
||||||
|
"name": "auth_token",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"name": "type",
|
||||||
|
"type": "machine_type",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'classic'"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "machine_status",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'ready'"
|
||||||
|
},
|
||||||
|
"snapshot": {
|
||||||
|
"name": "snapshot",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"models": {
|
||||||
|
"name": "models",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"gpu": {
|
||||||
|
"name": "gpu",
|
||||||
|
"type": "machine_gpu",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"build_machine_instance_id": {
|
||||||
|
"name": "build_machine_instance_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"build_log": {
|
||||||
|
"name": "build_log",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"machines_user_id_users_id_fk": {
|
||||||
|
"name": "machines_user_id_users_id_fk",
|
||||||
|
"tableFrom": "machines",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"users": {
|
||||||
|
"name": "users",
|
||||||
|
"schema": "comfyui_deploy",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"name": "username",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"workflow_run_outputs": {
|
||||||
|
"name": "workflow_run_outputs",
|
||||||
|
"schema": "comfyui_deploy",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"run_id": {
|
||||||
|
"name": "run_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"name": "data",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"workflow_run_outputs_run_id_workflow_runs_id_fk": {
|
||||||
|
"name": "workflow_run_outputs_run_id_workflow_runs_id_fk",
|
||||||
|
"tableFrom": "workflow_run_outputs",
|
||||||
|
"tableTo": "workflow_runs",
|
||||||
|
"columnsFrom": [
|
||||||
|
"run_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"workflow_runs": {
|
||||||
|
"name": "workflow_runs",
|
||||||
|
"schema": "comfyui_deploy",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"workflow_version_id": {
|
||||||
|
"name": "workflow_version_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"workflow_inputs": {
|
||||||
|
"name": "workflow_inputs",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"workflow_id": {
|
||||||
|
"name": "workflow_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"machine_id": {
|
||||||
|
"name": "machine_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"origin": {
|
||||||
|
"name": "origin",
|
||||||
|
"type": "workflow_run_origin",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'api'"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "workflow_run_status",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'not-started'"
|
||||||
|
},
|
||||||
|
"ended_at": {
|
||||||
|
"name": "ended_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"started_at": {
|
||||||
|
"name": "started_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"workflow_runs_workflow_version_id_workflow_versions_id_fk": {
|
||||||
|
"name": "workflow_runs_workflow_version_id_workflow_versions_id_fk",
|
||||||
|
"tableFrom": "workflow_runs",
|
||||||
|
"tableTo": "workflow_versions",
|
||||||
|
"columnsFrom": [
|
||||||
|
"workflow_version_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "set null",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"workflow_runs_workflow_id_workflows_id_fk": {
|
||||||
|
"name": "workflow_runs_workflow_id_workflows_id_fk",
|
||||||
|
"tableFrom": "workflow_runs",
|
||||||
|
"tableTo": "workflows",
|
||||||
|
"columnsFrom": [
|
||||||
|
"workflow_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"workflow_runs_machine_id_machines_id_fk": {
|
||||||
|
"name": "workflow_runs_machine_id_machines_id_fk",
|
||||||
|
"tableFrom": "workflow_runs",
|
||||||
|
"tableTo": "machines",
|
||||||
|
"columnsFrom": [
|
||||||
|
"machine_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "set null",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"workflows": {
|
||||||
|
"name": "workflows",
|
||||||
|
"schema": "comfyui_deploy",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"org_id": {
|
||||||
|
"name": "org_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"workflows_user_id_users_id_fk": {
|
||||||
|
"name": "workflows_user_id_users_id_fk",
|
||||||
|
"tableFrom": "workflows",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"workflow_versions": {
|
||||||
|
"name": "workflow_versions",
|
||||||
|
"schema": "comfyui_deploy",
|
||||||
|
"columns": {
|
||||||
|
"workflow_id": {
|
||||||
|
"name": "workflow_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"workflow": {
|
||||||
|
"name": "workflow",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"workflow_api": {
|
||||||
|
"name": "workflow_api",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"name": "version",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"snapshot": {
|
||||||
|
"name": "snapshot",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"workflow_versions_workflow_id_workflows_id_fk": {
|
||||||
|
"name": "workflow_versions_workflow_id_workflows_id_fk",
|
||||||
|
"tableFrom": "workflow_versions",
|
||||||
|
"tableTo": "workflows",
|
||||||
|
"columnsFrom": [
|
||||||
|
"workflow_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"enums": {
|
||||||
|
"deployment_environment": {
|
||||||
|
"name": "deployment_environment",
|
||||||
|
"values": {
|
||||||
|
"staging": "staging",
|
||||||
|
"production": "production",
|
||||||
|
"public-share": "public-share"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"machine_gpu": {
|
||||||
|
"name": "machine_gpu",
|
||||||
|
"values": {
|
||||||
|
"T4": "T4",
|
||||||
|
"A10G": "A10G",
|
||||||
|
"A100": "A100"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"machine_status": {
|
||||||
|
"name": "machine_status",
|
||||||
|
"values": {
|
||||||
|
"ready": "ready",
|
||||||
|
"building": "building",
|
||||||
|
"error": "error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"machine_type": {
|
||||||
|
"name": "machine_type",
|
||||||
|
"values": {
|
||||||
|
"classic": "classic",
|
||||||
|
"runpod-serverless": "runpod-serverless",
|
||||||
|
"modal-serverless": "modal-serverless",
|
||||||
|
"comfy-deploy-serverless": "comfy-deploy-serverless"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"workflow_run_origin": {
|
||||||
|
"name": "workflow_run_origin",
|
||||||
|
"values": {
|
||||||
|
"manual": "manual",
|
||||||
|
"api": "api",
|
||||||
|
"public-share": "public-share"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"workflow_run_status": {
|
||||||
|
"name": "workflow_run_status",
|
||||||
|
"values": {
|
||||||
|
"not-started": "not-started",
|
||||||
|
"running": "running",
|
||||||
|
"uploading": "uploading",
|
||||||
|
"success": "success",
|
||||||
|
"failed": "failed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schemas": {
|
||||||
|
"comfyui_deploy": "comfyui_deploy"
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {},
|
||||||
|
"columns": {}
|
||||||
|
}
|
||||||
|
}
|
@ -232,6 +232,20 @@
|
|||||||
"when": 1705806921697,
|
"when": 1705806921697,
|
||||||
"tag": "0032_shallow_vermin",
|
"tag": "0032_shallow_vermin",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 33,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1705853314500,
|
||||||
|
"tag": "0033_awesome_human_fly",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 34,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1705902960991,
|
||||||
|
"tag": "0034_even_lady_ursula",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -14,8 +14,12 @@ if (sslMode === "false") sslMode = false;
|
|||||||
|
|
||||||
let connectionString = process.env.POSTGRES_URL!;
|
let connectionString = process.env.POSTGRES_URL!;
|
||||||
|
|
||||||
const isDevContainer = process.env.VSCODE_DEV_CONTAINER !== undefined;
|
const isDevContainer = process.env.REMOTE_CONTAINERS !== undefined;
|
||||||
if (isDevContainer) connectionString = connectionString.replace("localhost","host.docker.internal")
|
if (isDevContainer)
|
||||||
|
connectionString = connectionString.replace(
|
||||||
|
"localhost",
|
||||||
|
"host.docker.internal",
|
||||||
|
);
|
||||||
|
|
||||||
const sql = postgres(connectionString, { max: 1, ssl: sslMode as any });
|
const sql = postgres(connectionString, { max: 1, ssl: sslMode as any });
|
||||||
const db = drizzle(sql, {
|
const db = drizzle(sql, {
|
||||||
@ -23,16 +27,16 @@ const db = drizzle(sql, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let retries = 5;
|
let retries = 5;
|
||||||
while(retries) {
|
while (retries) {
|
||||||
try {
|
try {
|
||||||
await sql`SELECT NOW()`;
|
await sql`SELECT NOW()`;
|
||||||
console.log('Database is live');
|
console.log("Database is live");
|
||||||
break;
|
break;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Database is not live yet', error);
|
console.error("Database is not live yet", error);
|
||||||
retries -= 1;
|
retries -= 1;
|
||||||
console.log(`Retries left: ${retries}`);
|
console.log(`Retries left: ${retries}`);
|
||||||
await new Promise(res => setTimeout(res, 1000));
|
await new Promise((res) => setTimeout(res, 1000));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +72,7 @@
|
|||||||
"mdx-annotations": "^0.1.4",
|
"mdx-annotations": "^0.1.4",
|
||||||
"million": "latest",
|
"million": "latest",
|
||||||
"mitata": "^0.1.6",
|
"mitata": "^0.1.6",
|
||||||
|
"ms": "^2.1.3",
|
||||||
"nanoid": "^5.0.4",
|
"nanoid": "^5.0.4",
|
||||||
"next": "14.1",
|
"next": "14.1",
|
||||||
"next-plausible": "^3.12.0",
|
"next-plausible": "^3.12.0",
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { app } from "../../../../routes/app";
|
|
||||||
import { registerCreateRunRoute } from "@/routes/registerCreateRunRoute";
|
import { registerCreateRunRoute } from "@/routes/registerCreateRunRoute";
|
||||||
import { registerGetOutputRoute } from "@/routes/registerGetOutputRoute";
|
import { registerGetOutputRoute } from "@/routes/registerGetOutputRoute";
|
||||||
import { registerUploadRoute } from "@/routes/registerUploadRoute";
|
import { registerUploadRoute } from "@/routes/registerUploadRoute";
|
||||||
@ -6,6 +5,9 @@ import { isKeyRevoked } from "@/server/curdApiKeys";
|
|||||||
import { parseJWT } from "@/server/parseJWT";
|
import { parseJWT } from "@/server/parseJWT";
|
||||||
import type { Context, Next } from "hono";
|
import type { Context, Next } from "hono";
|
||||||
import { handle } from "hono/vercel";
|
import { handle } from "hono/vercel";
|
||||||
|
import { app } from "../../../../routes/app";
|
||||||
|
import { registerWorkflowUploadRoute } from "@/routes/registerWorkflowUploadRoute";
|
||||||
|
import { registerGetAuthResponse } from "@/routes/registerGetAuthResponse";
|
||||||
|
|
||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
export const maxDuration = 300; // 5 minutes
|
export const maxDuration = 300; // 5 minutes
|
||||||
@ -21,7 +23,10 @@ async function checkAuth(c: Context, next: Next) {
|
|||||||
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", 401);
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
// If the key has expiration, this is a temporary key and not in our db, so we can skip checking
|
||||||
|
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", 401);
|
||||||
}
|
}
|
||||||
@ -31,18 +36,20 @@ async function checkAuth(c: Context, next: Next) {
|
|||||||
await next();
|
await next();
|
||||||
}
|
}
|
||||||
|
|
||||||
app.use("/run", async (c, next) => {
|
app.use("/run", checkAuth);
|
||||||
return checkAuth(c, next);
|
app.use("/upload-url", checkAuth);
|
||||||
});
|
app.use("/upload-workflow", checkAuth);
|
||||||
|
|
||||||
app.use("/upload-url", async (c, next) => {
|
|
||||||
return checkAuth(c, next);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// create run endpoint
|
||||||
registerCreateRunRoute(app);
|
registerCreateRunRoute(app);
|
||||||
registerGetOutputRoute(app);
|
registerGetOutputRoute(app);
|
||||||
|
|
||||||
|
// file upload endpoint
|
||||||
registerUploadRoute(app);
|
registerUploadRoute(app);
|
||||||
|
|
||||||
|
registerWorkflowUploadRoute(app);
|
||||||
|
registerGetAuthResponse(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",
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { parseDataSafe } from "../../../../lib/parseDataSafe";
|
|
||||||
import { handleResourceUpload } from "@/server/resource";
|
import { handleResourceUpload } from "@/server/resource";
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { parseDataSafe } from "../../../../lib/parseDataSafe";
|
||||||
|
|
||||||
const Request = z.object({
|
const Request = z.object({
|
||||||
file_name: z.string(),
|
file_name: z.string(),
|
||||||
run_id: z.string(),
|
run_id: z.string(),
|
||||||
|
|
||||||
type: z.string(),
|
type: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ export async function GET(request: Request) {
|
|||||||
{
|
{
|
||||||
url: uploadUrl,
|
url: uploadUrl,
|
||||||
},
|
},
|
||||||
{ status: 200 }
|
{ status: 200 },
|
||||||
);
|
);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
@ -38,7 +39,7 @@ export async function GET(request: Request) {
|
|||||||
{
|
{
|
||||||
error: errorMessage,
|
error: errorMessage,
|
||||||
},
|
},
|
||||||
{ status: 500 }
|
{ status: 500 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,14 @@
|
|||||||
import { createNewWorkflow } from "../../../../server/createNewWorkflow";
|
import { snapshotType, workflowAPIType, workflowType } from "@/db/schema";
|
||||||
import { parseJWT } from "../../../../server/parseJWT";
|
|
||||||
import { db } from "@/db/db";
|
|
||||||
import {
|
|
||||||
snapshotType,
|
|
||||||
workflowAPIType,
|
|
||||||
workflowTable,
|
|
||||||
workflowType,
|
|
||||||
workflowVersionTable,
|
|
||||||
} from "@/db/schema";
|
|
||||||
import { parseDataSafe } from "@/lib/parseDataSafe";
|
import { parseDataSafe } from "@/lib/parseDataSafe";
|
||||||
import { eq, sql } from "drizzle-orm";
|
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import {
|
||||||
|
createNewWorkflow,
|
||||||
|
createNewWorkflowVersion,
|
||||||
|
} from "../../../../server/createNewWorkflow";
|
||||||
|
import { parseJWT } from "../../../../server/parseJWT";
|
||||||
|
|
||||||
|
// This is will be deprecated
|
||||||
|
|
||||||
const corsHeaders = {
|
const corsHeaders = {
|
||||||
"Access-Control-Allow-Origin": "*",
|
"Access-Control-Allow-Origin": "*",
|
||||||
@ -55,7 +52,7 @@ export async function POST(request: Request) {
|
|||||||
const [data, error] = await parseDataSafe(
|
const [data, error] = await parseDataSafe(
|
||||||
UploadRequest,
|
UploadRequest,
|
||||||
request,
|
request,
|
||||||
corsHeaders
|
corsHeaders,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!data || error) return error;
|
if (!data || error) return error;
|
||||||
@ -75,7 +72,7 @@ export async function POST(request: Request) {
|
|||||||
|
|
||||||
// Case 1 new workflow
|
// Case 1 new workflow
|
||||||
try {
|
try {
|
||||||
if ((!workflow_id || workflow_id.length == 0) && workflow_name) {
|
if ((!workflow_id || workflow_id.length === 0) && workflow_name) {
|
||||||
// Create a new parent workflow
|
// Create a new parent workflow
|
||||||
const { workflow_id: _workflow_id, version: _version } =
|
const { workflow_id: _workflow_id, version: _version } =
|
||||||
await createNewWorkflow({
|
await createNewWorkflow({
|
||||||
@ -91,56 +88,17 @@ export async function POST(request: Request) {
|
|||||||
|
|
||||||
workflow_id = _workflow_id;
|
workflow_id = _workflow_id;
|
||||||
version = _version;
|
version = _version;
|
||||||
|
|
||||||
// const workflow_parent = await db
|
|
||||||
// .insert(workflowTable)
|
|
||||||
// .values({
|
|
||||||
// user_id,
|
|
||||||
// name: workflow_name,
|
|
||||||
// org_id: org_id,
|
|
||||||
// })
|
|
||||||
// .returning();
|
|
||||||
|
|
||||||
// workflow_id = workflow_parent[0].id;
|
|
||||||
|
|
||||||
// // Create a new version
|
|
||||||
// const data = await db
|
|
||||||
// .insert(workflowVersionTable)
|
|
||||||
// .values({
|
|
||||||
// workflow_id: workflow_id,
|
|
||||||
// workflow,
|
|
||||||
// workflow_api,
|
|
||||||
// version: 1,
|
|
||||||
// snapshot: snapshot,
|
|
||||||
// })
|
|
||||||
// .returning();
|
|
||||||
// version = data[0].version;
|
|
||||||
} else if (workflow_id) {
|
} else if (workflow_id) {
|
||||||
// Case 2 update workflow
|
// Case 2 update workflow
|
||||||
const data = await db
|
const { version: _version } = await createNewWorkflowVersion({
|
||||||
.insert(workflowVersionTable)
|
workflow_id: workflow_id,
|
||||||
.values({
|
workflowData: {
|
||||||
workflow_id,
|
workflow,
|
||||||
workflow: workflow,
|
|
||||||
workflow_api,
|
workflow_api,
|
||||||
// version: sql`${workflowVersionTable.version} + 1`,
|
snapshot,
|
||||||
snapshot: snapshot,
|
},
|
||||||
version: sql`(
|
});
|
||||||
SELECT COALESCE(MAX(version), 0) + 1
|
version = _version;
|
||||||
FROM ${workflowVersionTable}
|
|
||||||
WHERE workflow_id = ${workflow_id}
|
|
||||||
)`,
|
|
||||||
})
|
|
||||||
.returning();
|
|
||||||
version = data[0].version;
|
|
||||||
|
|
||||||
await db
|
|
||||||
.update(workflowTable)
|
|
||||||
.set({
|
|
||||||
updated_at: new Date(),
|
|
||||||
})
|
|
||||||
.where(eq(workflowTable.id, workflow_id))
|
|
||||||
.returning();
|
|
||||||
} else {
|
} else {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{
|
{
|
||||||
@ -150,7 +108,7 @@ export async function POST(request: Request) {
|
|||||||
status: 500,
|
status: 500,
|
||||||
statusText: "Invalid request",
|
statusText: "Invalid request",
|
||||||
headers: corsHeaders,
|
headers: corsHeaders,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
@ -162,7 +120,7 @@ export async function POST(request: Request) {
|
|||||||
status: 500,
|
status: 500,
|
||||||
statusText: "Invalid request",
|
statusText: "Invalid request",
|
||||||
headers: corsHeaders,
|
headers: corsHeaders,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,6 +132,6 @@ export async function POST(request: Request) {
|
|||||||
{
|
{
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: corsHeaders,
|
headers: corsHeaders,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
54
web/src/app/(app)/auth-request/[request_id]/page.tsx
Normal file
54
web/src/app/(app)/auth-request/[request_id]/page.tsx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { ButtonAction } from "@/components/ButtonActionLoader";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { createAuthRequest } from "@/server/curdApiKeys";
|
||||||
|
import { auth } from "@clerk/nextjs";
|
||||||
|
import { redirect } from "next/navigation";
|
||||||
|
import { getOrgOrUserDisplayName } from "../../../../server/getOrgOrUserDisplayName";
|
||||||
|
import { db } from "@/db/db";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import { authRequestsTable } from "@/db/schema";
|
||||||
|
|
||||||
|
export default async function Home({
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
params: { request_id: string };
|
||||||
|
}) {
|
||||||
|
const { userId, orgId } = await auth();
|
||||||
|
|
||||||
|
if (!userId) redirect("/");
|
||||||
|
|
||||||
|
if (!params.request_id)
|
||||||
|
return (
|
||||||
|
<div className="h-full w-full flex flex-col gap-2 items-center justify-center">
|
||||||
|
No valid request_id
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const existingResult = await db.query.authRequestsTable.findFirst({
|
||||||
|
where: eq(authRequestsTable.request_id, params.request_id),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingResult?.api_hash) {
|
||||||
|
return (
|
||||||
|
<div className="h-full w-full flex flex-col gap-2 items-center justify-center">
|
||||||
|
Request already consumed.
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const userName = await getOrgOrUserDisplayName(orgId, userId);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full w-full flex flex-col gap-2 items-center justify-center">
|
||||||
|
<div className="text-lg">Grant API Access to {userName}</div>
|
||||||
|
<Button asChild>
|
||||||
|
<ButtonAction
|
||||||
|
routerAction="do-nothing"
|
||||||
|
action={createAuthRequest.bind(null, params.request_id)}
|
||||||
|
>
|
||||||
|
Grant Access
|
||||||
|
</ButtonAction>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -4,10 +4,10 @@ import { LoadingIcon } from "@/components/LoadingIcon";
|
|||||||
import { callServerPromise } from "@/components/callServerPromise";
|
import { callServerPromise } from "@/components/callServerPromise";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { useAuth, useClerk } from "@clerk/nextjs";
|
import { useAuth, useClerk } from "@clerk/nextjs";
|
||||||
import { MoreVertical } from "lucide-react";
|
import { MoreVertical } from "lucide-react";
|
||||||
@ -15,80 +15,79 @@ import { useRouter } from "next/navigation";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
export function ButtonAction({
|
export function ButtonAction({
|
||||||
action,
|
action,
|
||||||
children,
|
children,
|
||||||
routerAction = "back",
|
routerAction = "back",
|
||||||
...rest
|
...rest
|
||||||
}: {
|
}: {
|
||||||
action: () => Promise<any>;
|
action: () => Promise<any>;
|
||||||
routerAction?: "refresh" | "back";
|
routerAction?: "refresh" | "back" | "do-nothing";
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
const [pending, setPending] = useState(false);
|
const [pending, setPending] = useState(false);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (pending) return;
|
if (pending) return;
|
||||||
|
|
||||||
setPending(true);
|
setPending(true);
|
||||||
await callServerPromise(action());
|
await callServerPromise(action());
|
||||||
setPending(false);
|
setPending(false);
|
||||||
|
|
||||||
if (routerAction === "back") {
|
if (routerAction === "back") {
|
||||||
router.back();
|
router.back();
|
||||||
router.refresh();
|
router.refresh();
|
||||||
}
|
} else if (routerAction === "refresh") router.refresh();
|
||||||
else if (routerAction === "refresh") router.refresh();
|
}}
|
||||||
}}
|
{...rest}
|
||||||
{...rest}
|
>
|
||||||
>
|
{children} {pending && <LoadingIcon />}
|
||||||
{children} {pending && <LoadingIcon />}
|
</button>
|
||||||
</button>
|
);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ButtonActionMenu(props: {
|
export function ButtonActionMenu(props: {
|
||||||
title?: string;
|
title?: string;
|
||||||
actions: {
|
actions: {
|
||||||
title: string;
|
title: string;
|
||||||
action: () => Promise<any>;
|
action: () => Promise<any>;
|
||||||
}[];
|
}[];
|
||||||
}) {
|
}) {
|
||||||
const user = useAuth();
|
const user = useAuth();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const clerk = useClerk();
|
const clerk = useClerk();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button className="gap-2" variant="outline" disabled={isLoading}>
|
<Button className="gap-2" variant="outline" disabled={isLoading}>
|
||||||
{props.title}
|
{props.title}
|
||||||
{isLoading ? <LoadingIcon /> : <MoreVertical size={14} />}
|
{isLoading ? <LoadingIcon /> : <MoreVertical size={14} />}
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="w-56">
|
<DropdownMenuContent className="w-56">
|
||||||
{props.actions.map((action) => (
|
{props.actions.map((action) => (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
key={action.title}
|
key={action.title}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (!user.isSignedIn) {
|
if (!user.isSignedIn) {
|
||||||
clerk.openSignIn({
|
clerk.openSignIn({
|
||||||
redirectUrl: window.location.href,
|
redirectUrl: window.location.href,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
await callServerPromise(action.action());
|
await callServerPromise(action.action());
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{action.title}
|
{action.title}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
))}
|
))}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import * as schema from "./schema";
|
import { Pool, neonConfig } from "@neondatabase/serverless";
|
||||||
import { neonConfig, Pool } from "@neondatabase/serverless";
|
|
||||||
import { drizzle as neonDrizzle } from "drizzle-orm/neon-serverless";
|
import { drizzle as neonDrizzle } from "drizzle-orm/neon-serverless";
|
||||||
|
import * as schema from "./schema";
|
||||||
|
|
||||||
const isDevContainer = process.env.REMOTE_CONTAINERS !== undefined;
|
const isDevContainer = process.env.REMOTE_CONTAINERS !== undefined;
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ if (process.env.VERCEL_ENV !== "production") {
|
|||||||
// Set the WebSocket proxy to work with the local instance
|
// Set the WebSocket proxy to work with the local instance
|
||||||
if (isDevContainer) {
|
if (isDevContainer) {
|
||||||
// Running inside a VS Code devcontainer
|
// Running inside a VS Code devcontainer
|
||||||
neonConfig.wsProxy = (host) => `host.docker.internal:5481/v1`;
|
neonConfig.wsProxy = (host) => "host.docker.internal:5481/v1";
|
||||||
} else {
|
} else {
|
||||||
// Not running inside a VS Code devcontainer
|
// Not running inside a VS Code devcontainer
|
||||||
neonConfig.wsProxy = (host) => `${host}:5481/v1`;
|
neonConfig.wsProxy = (host) => `${host}:5481/v1`;
|
||||||
@ -26,5 +26,5 @@ export const db = neonDrizzle(
|
|||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
schema,
|
schema,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { type InferSelectModel, relations } from "drizzle-orm";
|
import { type InferSelectModel, relations } from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
boolean,
|
boolean,
|
||||||
integer,
|
integer,
|
||||||
jsonb,
|
jsonb,
|
||||||
pgEnum,
|
pgEnum,
|
||||||
pgSchema,
|
pgSchema,
|
||||||
text,
|
text,
|
||||||
timestamp,
|
timestamp,
|
||||||
uuid,
|
uuid,
|
||||||
} from "drizzle-orm/pg-core";
|
} from "drizzle-orm/pg-core";
|
||||||
import { createInsertSchema } from "drizzle-zod";
|
import { createInsertSchema } from "drizzle-zod";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@ -87,7 +87,7 @@ export const workflowVersionRelations = relations(
|
|||||||
fields: [workflowVersionTable.workflow_id],
|
fields: [workflowVersionTable.workflow_id],
|
||||||
references: [workflowTable.id],
|
references: [workflowTable.id],
|
||||||
}),
|
}),
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const workflowRunStatus = pgEnum("workflow_run_status", [
|
export const workflowRunStatus = pgEnum("workflow_run_status", [
|
||||||
@ -130,30 +130,30 @@ export const machinesStatus = pgEnum("machine_status", [
|
|||||||
|
|
||||||
// We still want to keep the workflow run record.
|
// We still want to keep the workflow run record.
|
||||||
export const workflowRunsTable = dbSchema.table("workflow_runs", {
|
export const workflowRunsTable = dbSchema.table("workflow_runs", {
|
||||||
id: uuid("id").primaryKey().defaultRandom().notNull(),
|
id: uuid("id").primaryKey().defaultRandom().notNull(),
|
||||||
// when workflow version deleted, still want to keep this record
|
// when workflow version deleted, still want to keep this record
|
||||||
workflow_version_id: uuid("workflow_version_id").references(
|
workflow_version_id: uuid("workflow_version_id").references(
|
||||||
() => workflowVersionTable.id,
|
() => workflowVersionTable.id,
|
||||||
{
|
{
|
||||||
onDelete: "set null",
|
onDelete: "set null",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
workflow_inputs:
|
workflow_inputs:
|
||||||
jsonb("workflow_inputs").$type<Record<string, string | number>>(),
|
jsonb("workflow_inputs").$type<Record<string, string | number>>(),
|
||||||
workflow_id: uuid("workflow_id")
|
workflow_id: uuid("workflow_id")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => workflowTable.id, {
|
.references(() => workflowTable.id, {
|
||||||
onDelete: "cascade",
|
onDelete: "cascade",
|
||||||
}),
|
}),
|
||||||
// when machine deleted, still want to keep this record
|
// when machine deleted, still want to keep this record
|
||||||
machine_id: uuid("machine_id").references(() => machinesTable.id, {
|
machine_id: uuid("machine_id").references(() => machinesTable.id, {
|
||||||
onDelete: "set null",
|
onDelete: "set null",
|
||||||
}),
|
}),
|
||||||
origin: workflowRunOrigin("origin").notNull().default("api"),
|
origin: workflowRunOrigin("origin").notNull().default("api"),
|
||||||
status: workflowRunStatus("status").notNull().default("not-started"),
|
status: workflowRunStatus("status").notNull().default("not-started"),
|
||||||
ended_at: timestamp("ended_at"),
|
ended_at: timestamp("ended_at"),
|
||||||
created_at: timestamp("created_at").defaultNow().notNull(),
|
created_at: timestamp("created_at").defaultNow().notNull(),
|
||||||
started_at: timestamp("started_at"),
|
started_at: timestamp("started_at"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const workflowRunRelations = relations(
|
export const workflowRunRelations = relations(
|
||||||
@ -172,7 +172,7 @@ export const workflowRunRelations = relations(
|
|||||||
fields: [workflowRunsTable.workflow_id],
|
fields: [workflowRunsTable.workflow_id],
|
||||||
references: [workflowTable.id],
|
references: [workflowTable.id],
|
||||||
}),
|
}),
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// We still want to keep the workflow run record.
|
// We still want to keep the workflow run record.
|
||||||
@ -196,7 +196,7 @@ export const workflowOutputRelations = relations(
|
|||||||
fields: [workflowRunOutputs.run_id],
|
fields: [workflowRunOutputs.run_id],
|
||||||
references: [workflowRunsTable.id],
|
references: [workflowRunsTable.id],
|
||||||
}),
|
}),
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// when user delete, also delete all the workflow versions
|
// when user delete, also delete all the workflow versions
|
||||||
@ -229,7 +229,7 @@ export const snapshotType = z.object({
|
|||||||
z.object({
|
z.object({
|
||||||
hash: z.string(),
|
hash: z.string(),
|
||||||
disabled: z.boolean(),
|
disabled: z.boolean(),
|
||||||
})
|
}),
|
||||||
),
|
),
|
||||||
file_custom_nodes: z.array(z.any()),
|
file_custom_nodes: z.array(z.any()),
|
||||||
});
|
});
|
||||||
@ -244,7 +244,7 @@ export const showcaseMedia = z.array(
|
|||||||
z.object({
|
z.object({
|
||||||
url: z.string(),
|
url: z.string(),
|
||||||
isCover: z.boolean().default(false),
|
isCover: z.boolean().default(false),
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const showcaseMediaNullable = z
|
export const showcaseMediaNullable = z
|
||||||
@ -252,36 +252,36 @@ export const showcaseMediaNullable = z
|
|||||||
z.object({
|
z.object({
|
||||||
url: z.string(),
|
url: z.string(),
|
||||||
isCover: z.boolean().default(false),
|
isCover: z.boolean().default(false),
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.nullable();
|
.nullable();
|
||||||
|
|
||||||
export const deploymentsTable = dbSchema.table("deployments", {
|
export const deploymentsTable = dbSchema.table("deployments", {
|
||||||
id: uuid("id").primaryKey().defaultRandom().notNull(),
|
id: uuid("id").primaryKey().defaultRandom().notNull(),
|
||||||
user_id: text("user_id")
|
user_id: text("user_id")
|
||||||
.references(() => usersTable.id, {
|
.references(() => usersTable.id, {
|
||||||
onDelete: "cascade",
|
onDelete: "cascade",
|
||||||
})
|
})
|
||||||
.notNull(),
|
.notNull(),
|
||||||
org_id: text("org_id"),
|
org_id: text("org_id"),
|
||||||
workflow_version_id: uuid("workflow_version_id")
|
workflow_version_id: uuid("workflow_version_id")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => workflowVersionTable.id),
|
.references(() => workflowVersionTable.id),
|
||||||
workflow_id: uuid("workflow_id")
|
workflow_id: uuid("workflow_id")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => workflowTable.id, {
|
.references(() => workflowTable.id, {
|
||||||
onDelete: "cascade",
|
onDelete: "cascade",
|
||||||
}),
|
}),
|
||||||
machine_id: uuid("machine_id")
|
machine_id: uuid("machine_id")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => machinesTable.id),
|
.references(() => machinesTable.id),
|
||||||
share_slug: text("share_slug").unique(),
|
share_slug: text("share_slug").unique(),
|
||||||
description: text("description"),
|
description: text("description"),
|
||||||
showcase_media:
|
showcase_media:
|
||||||
jsonb("showcase_media").$type<z.infer<typeof showcaseMedia>>(),
|
jsonb("showcase_media").$type<z.infer<typeof showcaseMedia>>(),
|
||||||
environment: deploymentEnvironment("environment").notNull(),
|
environment: deploymentEnvironment("environment").notNull(),
|
||||||
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 publicShareDeployment = z.object({
|
export const publicShareDeployment = z.object({
|
||||||
@ -331,6 +331,16 @@ export const apiKeyTable = dbSchema.table("api_keys", {
|
|||||||
updated_at: timestamp("updated_at").defaultNow().notNull(),
|
updated_at: timestamp("updated_at").defaultNow().notNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const authRequestsTable = dbSchema.table("auth_requests", {
|
||||||
|
request_id: text("request_id").primaryKey().notNull(),
|
||||||
|
user_id: text("user_id"),
|
||||||
|
org_id: text("org_id"),
|
||||||
|
api_hash: text("api_hash"),
|
||||||
|
created_at: timestamp("created_at").defaultNow().notNull(),
|
||||||
|
expired_date: timestamp("expired_date"),
|
||||||
|
updated_at: timestamp("updated_at").defaultNow().notNull(),
|
||||||
|
});
|
||||||
|
|
||||||
export type UserType = InferSelectModel<typeof usersTable>;
|
export type UserType = InferSelectModel<typeof usersTable>;
|
||||||
export type WorkflowType = InferSelectModel<typeof workflowTable>;
|
export type WorkflowType = InferSelectModel<typeof workflowTable>;
|
||||||
export type MachineType = InferSelectModel<typeof machinesTable>;
|
export type MachineType = InferSelectModel<typeof machinesTable>;
|
||||||
|
13
web/src/routes/newId.ts
Normal file
13
web/src/routes/newId.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { customAlphabet } from "nanoid";
|
||||||
|
|
||||||
|
export const nanoid = customAlphabet(
|
||||||
|
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
|
||||||
|
);
|
||||||
|
const prefixes = {
|
||||||
|
img: "img",
|
||||||
|
vid: "vid",
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export function newId(prefix: keyof typeof prefixes): string {
|
||||||
|
return [prefixes[prefix], nanoid(16)].join("_");
|
||||||
|
}
|
@ -1,10 +1,10 @@
|
|||||||
import { createRun } from "../server/createRun";
|
|
||||||
import { db } from "@/db/db";
|
import { db } from "@/db/db";
|
||||||
import { deploymentsTable } from "@/db/schema";
|
import { deploymentsTable } 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 { z, createRoute } from "@hono/zod-openapi";
|
import { createRoute, z } from "@hono/zod-openapi";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
import { createRun } from "../server/createRun";
|
||||||
|
|
||||||
const createRunRoute = createRoute({
|
const createRunRoute = createRoute({
|
||||||
method: "post",
|
method: "post",
|
||||||
@ -99,7 +99,7 @@ export const registerCreateRunRoute = (app: App) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
status: 500,
|
status: 500,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
150
web/src/routes/registerGetAuthResponse.ts
Normal file
150
web/src/routes/registerGetAuthResponse.ts
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
import { db } from "@/db/db";
|
||||||
|
import { authRequestsTable } from "@/db/schema";
|
||||||
|
import type { App } from "@/routes/app";
|
||||||
|
import { authError } from "@/routes/authError";
|
||||||
|
import { z, createRoute } from "@hono/zod-openapi";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import jwt from "jsonwebtoken";
|
||||||
|
import crypto from "crypto";
|
||||||
|
import { getOrgOrUserDisplayName } from "@/server/getOrgOrUserDisplayName";
|
||||||
|
import ms from "ms";
|
||||||
|
|
||||||
|
const route = createRoute({
|
||||||
|
method: "get",
|
||||||
|
path: "/auth-response/:request_id",
|
||||||
|
tags: ["comfyui"],
|
||||||
|
summary: "Get an API Key with code",
|
||||||
|
description:
|
||||||
|
"This endpoints is specifically built for ComfyUI workflow upload.",
|
||||||
|
request: {
|
||||||
|
params: z.object({
|
||||||
|
request_id: z.string(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: z.object({
|
||||||
|
api_key: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: "The returned API Key",
|
||||||
|
},
|
||||||
|
201: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: z.object({
|
||||||
|
message: z.string(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: "The API key is not yet ready",
|
||||||
|
},
|
||||||
|
500: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: z.object({
|
||||||
|
error: z.string(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: "Error when fetching the API Key with code",
|
||||||
|
},
|
||||||
|
...authError,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const corsHeaders = {
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
"Access-Control-Allow-Methods": "GET, OPTIONS",
|
||||||
|
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const registerGetAuthResponse = (app: App) => {
|
||||||
|
return app.openapi(route, async (c) => {
|
||||||
|
const { request_id } = c.req.valid("param");
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await db.query.authRequestsTable.findFirst({
|
||||||
|
where: eq(authRequestsTable.request_id, request_id),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result?.api_hash) {
|
||||||
|
return c.json(
|
||||||
|
{
|
||||||
|
message: "Already used.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: 201,
|
||||||
|
headers: corsHeaders,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result && result.user_id) {
|
||||||
|
const expireTime = "1w";
|
||||||
|
const token = jwt.sign(
|
||||||
|
{ user_id: result.user_id, org_id: result.org_id },
|
||||||
|
process.env.JWT_SECRET!,
|
||||||
|
{
|
||||||
|
expiresIn: expireTime,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const hash = crypto.createHash("sha256").update(token).digest("hex");
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
const expiryDate = new Date(now.getTime() + ms(expireTime));
|
||||||
|
|
||||||
|
await db
|
||||||
|
.update(authRequestsTable)
|
||||||
|
.set({
|
||||||
|
api_hash: hash,
|
||||||
|
expired_date: expiryDate,
|
||||||
|
})
|
||||||
|
.where(eq(authRequestsTable.request_id, request_id));
|
||||||
|
|
||||||
|
const userName = await getOrgOrUserDisplayName(
|
||||||
|
result.org_id,
|
||||||
|
result.user_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
return c.json(
|
||||||
|
{
|
||||||
|
api_key: token,
|
||||||
|
name: userName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: 200,
|
||||||
|
headers: corsHeaders,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error: unknown) {
|
||||||
|
const errorMessage =
|
||||||
|
error instanceof Error ? error.message : "Unknown error";
|
||||||
|
return c.json(
|
||||||
|
{
|
||||||
|
error: errorMessage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statusText: "Invalid request",
|
||||||
|
status: 500,
|
||||||
|
headers: corsHeaders,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return c.json(
|
||||||
|
{
|
||||||
|
message: "Not ready yet.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: 201,
|
||||||
|
headers: corsHeaders,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
@ -3,20 +3,7 @@ import { authError } from "@/routes/authError";
|
|||||||
import { getFileDownloadUrl } from "@/server/getFileDownloadUrl";
|
import { getFileDownloadUrl } from "@/server/getFileDownloadUrl";
|
||||||
import { handleResourceUpload } from "@/server/resource";
|
import { handleResourceUpload } from "@/server/resource";
|
||||||
import { z, createRoute } from "@hono/zod-openapi";
|
import { z, createRoute } from "@hono/zod-openapi";
|
||||||
import { customAlphabet } from "nanoid";
|
import { newId } from "./newId";
|
||||||
|
|
||||||
export const nanoid = customAlphabet(
|
|
||||||
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
|
||||||
);
|
|
||||||
|
|
||||||
const prefixes = {
|
|
||||||
img: "img",
|
|
||||||
vid: "vid",
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export function newId(prefix: keyof typeof prefixes): string {
|
|
||||||
return [prefixes[prefix], nanoid(16)].join("_");
|
|
||||||
}
|
|
||||||
|
|
||||||
const uploadUrlRoute = createRoute({
|
const uploadUrlRoute = createRoute({
|
||||||
method: "get",
|
method: "get",
|
||||||
@ -96,7 +83,7 @@ export const registerUploadRoute = (app: App) => {
|
|||||||
file_id: id,
|
file_id: id,
|
||||||
download_url: await getFileDownloadUrl(filePath),
|
download_url: await getFileDownloadUrl(filePath),
|
||||||
},
|
},
|
||||||
200
|
200,
|
||||||
);
|
);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
@ -107,7 +94,7 @@ export const registerUploadRoute = (app: App) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
status: 500,
|
status: 500,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
164
web/src/routes/registerWorkflowUploadRoute.ts
Normal file
164
web/src/routes/registerWorkflowUploadRoute.ts
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
import { snapshotType, workflowAPIType, workflowType } from "@/db/schema";
|
||||||
|
import type { App } from "@/routes/app";
|
||||||
|
import { authError } from "@/routes/authError";
|
||||||
|
import {
|
||||||
|
createNewWorkflow,
|
||||||
|
createNewWorkflowVersion,
|
||||||
|
} from "@/server/createNewWorkflow";
|
||||||
|
import { z, createRoute } from "@hono/zod-openapi";
|
||||||
|
|
||||||
|
const route = createRoute({
|
||||||
|
method: "post",
|
||||||
|
path: "/upload-workflow",
|
||||||
|
tags: ["comfyui"],
|
||||||
|
summary: "Upload workflow from ComfyUI",
|
||||||
|
description:
|
||||||
|
"This endpoints is specifically built for ComfyUI workflow upload.",
|
||||||
|
request: {
|
||||||
|
body: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: z.object({
|
||||||
|
workflow_id: z.string().optional(),
|
||||||
|
workflow_name: z.string().min(1).optional(),
|
||||||
|
workflow: workflowType,
|
||||||
|
workflow_api: workflowAPIType,
|
||||||
|
snapshot: snapshotType,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: z.object({
|
||||||
|
workflow_id: z.string(),
|
||||||
|
version: z.string(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: "Retrieve the output",
|
||||||
|
},
|
||||||
|
500: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: z.object({
|
||||||
|
error: z.string(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: "Error when uploading the workflow",
|
||||||
|
},
|
||||||
|
...authError,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const corsHeaders = {
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
||||||
|
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const registerWorkflowUploadRoute = (app: App) => {
|
||||||
|
app.openapi(route, async (c) => {
|
||||||
|
const {
|
||||||
|
// user_id,
|
||||||
|
workflow,
|
||||||
|
workflow_api,
|
||||||
|
workflow_id: _workflow_id,
|
||||||
|
workflow_name,
|
||||||
|
snapshot,
|
||||||
|
} = c.req.valid("json");
|
||||||
|
const { org_id, user_id } = c.get("apiKeyTokenData")!;
|
||||||
|
|
||||||
|
if (!user_id)
|
||||||
|
return c.json(
|
||||||
|
{
|
||||||
|
error: "Invalid user_id",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: corsHeaders,
|
||||||
|
status: 500,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let workflow_id = _workflow_id;
|
||||||
|
|
||||||
|
let version = -1;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ((!workflow_id || workflow_id.length === 0) && workflow_name) {
|
||||||
|
// Create a new parent workflow
|
||||||
|
const { workflow_id: _workflow_id, version: _version } =
|
||||||
|
await createNewWorkflow({
|
||||||
|
user_id: user_id,
|
||||||
|
org_id: org_id,
|
||||||
|
workflow_name: workflow_name,
|
||||||
|
workflowData: {
|
||||||
|
workflow,
|
||||||
|
workflow_api,
|
||||||
|
snapshot,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
workflow_id = _workflow_id;
|
||||||
|
version = _version;
|
||||||
|
} else if (workflow_id) {
|
||||||
|
// Case 2 update workflow
|
||||||
|
const { version: _version } = await createNewWorkflowVersion({
|
||||||
|
workflow_id: workflow_id,
|
||||||
|
workflowData: {
|
||||||
|
workflow,
|
||||||
|
workflow_api,
|
||||||
|
snapshot,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
version = _version;
|
||||||
|
} else {
|
||||||
|
return c.json(
|
||||||
|
{
|
||||||
|
error: "Invalid request, missing either workflow_id or name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: 500,
|
||||||
|
statusText: "Invalid request",
|
||||||
|
headers: corsHeaders,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error: unknown) {
|
||||||
|
const errorMessage =
|
||||||
|
error instanceof Error ? error.message : "Unknown error";
|
||||||
|
return c.json(
|
||||||
|
{
|
||||||
|
error: errorMessage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statusText: "Invalid request",
|
||||||
|
status: 500,
|
||||||
|
headers: corsHeaders,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.json(
|
||||||
|
{
|
||||||
|
workflow_id: workflow_id,
|
||||||
|
version: version,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: 200,
|
||||||
|
headers: corsHeaders,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.route("/upload-workflow").options(async (c) => {
|
||||||
|
return new Response(null, {
|
||||||
|
status: 204,
|
||||||
|
headers: corsHeaders,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
@ -1,9 +1,10 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
export const APIKeyBodyRequest = z.object({
|
export const APIKeyBodyRequest = z.object({
|
||||||
user_id: z.string().optional(),
|
user_id: z.string().optional().nullable(),
|
||||||
org_id: z.string().optional(),
|
org_id: z.string().optional().nullable(),
|
||||||
iat: z.number(),
|
iat: z.number(),
|
||||||
|
exp: z.number().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type APIKeyUserType = z.infer<typeof APIKeyBodyRequest>;
|
export type APIKeyUserType = z.infer<typeof APIKeyBodyRequest>;
|
||||||
|
@ -1,6 +1,46 @@
|
|||||||
import { db } from "@/db/db";
|
import { db } from "@/db/db";
|
||||||
import type { WorkflowVersionType } from "@/db/schema";
|
import type { WorkflowVersionType } from "@/db/schema";
|
||||||
import { workflowTable, workflowVersionTable } from "@/db/schema";
|
import { workflowTable, workflowVersionTable } from "@/db/schema";
|
||||||
|
import { eq, sql } from "drizzle-orm";
|
||||||
|
|
||||||
|
export async function createNewWorkflowVersion({
|
||||||
|
workflow_id,
|
||||||
|
workflowData,
|
||||||
|
}: {
|
||||||
|
workflow_id: string;
|
||||||
|
workflowData: Pick<
|
||||||
|
WorkflowVersionType,
|
||||||
|
"workflow" | "workflow_api" | "snapshot"
|
||||||
|
>;
|
||||||
|
}) {
|
||||||
|
// Add a new version
|
||||||
|
const data = await db
|
||||||
|
.insert(workflowVersionTable)
|
||||||
|
.values({
|
||||||
|
workflow_id,
|
||||||
|
...workflowData,
|
||||||
|
version: sql`(
|
||||||
|
SELECT COALESCE(MAX(version), 0) + 1
|
||||||
|
FROM ${workflowVersionTable}
|
||||||
|
WHERE workflow_id = ${workflow_id}
|
||||||
|
)`,
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
const version = data[0].version;
|
||||||
|
|
||||||
|
// Touch up the last updated time
|
||||||
|
await db
|
||||||
|
.update(workflowTable)
|
||||||
|
.set({
|
||||||
|
updated_at: new Date(),
|
||||||
|
})
|
||||||
|
.where(eq(workflowTable.id, workflow_id))
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
return {
|
||||||
|
version,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export async function createNewWorkflow({
|
export async function createNewWorkflow({
|
||||||
workflow_name,
|
workflow_name,
|
||||||
@ -10,7 +50,7 @@ export async function createNewWorkflow({
|
|||||||
}: {
|
}: {
|
||||||
workflow_name: string;
|
workflow_name: string;
|
||||||
user_id: string;
|
user_id: string;
|
||||||
org_id?: string;
|
org_id?: string | null;
|
||||||
workflowData: Pick<
|
workflowData: Pick<
|
||||||
WorkflowVersionType,
|
WorkflowVersionType,
|
||||||
"workflow" | "workflow_api" | "snapshot"
|
"workflow" | "workflow_api" | "snapshot"
|
||||||
|
@ -1,23 +1,28 @@
|
|||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
import { db } from "@/db/db";
|
import { db } from "@/db/db";
|
||||||
import { apiKeyTable } from "@/db/schema";
|
import { apiKeyTable, authRequestsTable } from "@/db/schema";
|
||||||
|
import { withServerPromise } from "@/server/withServerPromise";
|
||||||
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 jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import { revalidatePath } from "next/cache";
|
import { revalidatePath } from "next/cache";
|
||||||
|
|
||||||
// export const nanoid = customAlphabet(
|
export const createAuthRequest = withServerPromise(
|
||||||
// "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
async (request_id: string) => {
|
||||||
// );
|
const { userId, orgId } = auth();
|
||||||
|
|
||||||
// const prefixes = {
|
const result = await db.insert(authRequestsTable).values({
|
||||||
// cd: "cd",
|
request_id: request_id,
|
||||||
// } as const;
|
user_id: userId,
|
||||||
|
org_id: orgId,
|
||||||
|
});
|
||||||
|
|
||||||
// function newId(prefix: keyof typeof prefixes): string {
|
return {
|
||||||
// return [prefixes[prefix], nanoid(16)].join("_");
|
message: "Auth request created, you may now return to your application.",
|
||||||
// }
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export async function addNewAPIKey(name: string) {
|
export async function addNewAPIKey(name: string) {
|
||||||
const { userId, orgId } = auth();
|
const { userId, orgId } = auth();
|
||||||
@ -29,7 +34,7 @@ export async function addNewAPIKey(name: string) {
|
|||||||
if (orgId) {
|
if (orgId) {
|
||||||
token = jwt.sign(
|
token = jwt.sign(
|
||||||
{ user_id: userId, org_id: orgId },
|
{ user_id: userId, org_id: orgId },
|
||||||
process.env.JWT_SECRET!
|
process.env.JWT_SECRET!,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
token = jwt.sign({ user_id: userId }, process.env.JWT_SECRET!);
|
token = jwt.sign({ user_id: userId }, process.env.JWT_SECRET!);
|
||||||
@ -93,7 +98,7 @@ export async function getAPIKeys() {
|
|||||||
where: and(
|
where: and(
|
||||||
eq(apiKeyTable.user_id, userId),
|
eq(apiKeyTable.user_id, userId),
|
||||||
isNull(apiKeyTable.org_id),
|
isNull(apiKeyTable.org_id),
|
||||||
eq(apiKeyTable.revoked, false)
|
eq(apiKeyTable.revoked, false),
|
||||||
),
|
),
|
||||||
orderBy: desc(apiKeyTable.created_at),
|
orderBy: desc(apiKeyTable.created_at),
|
||||||
});
|
});
|
||||||
|
18
web/src/server/getOrgOrUserDisplayName.tsx
Normal file
18
web/src/server/getOrgOrUserDisplayName.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { db } from "@/db/db";
|
||||||
|
import { usersTable } from "@/db/schema";
|
||||||
|
import { clerkClient } from "@clerk/nextjs";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
|
||||||
|
export async function getOrgOrUserDisplayName(
|
||||||
|
orgId: string | undefined | null,
|
||||||
|
userId: string,
|
||||||
|
) {
|
||||||
|
return orgId
|
||||||
|
? await clerkClient.organizations
|
||||||
|
.getOrganization({
|
||||||
|
organizationId: orgId,
|
||||||
|
})
|
||||||
|
.then((x) => x.name)
|
||||||
|
: (await db.select().from(usersTable).where(eq(usersTable.id, userId)))[0]
|
||||||
|
.name;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user