feat: share page slug
This commit is contained in:
parent
ca1b05fff5
commit
6de7bf3f20
2
web/drizzle/0032_shallow_vermin.sql
Normal file
2
web/drizzle/0032_shallow_vermin.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE "comfyui_deploy"."deployments" ADD COLUMN "share_slug" text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "comfyui_deploy"."deployments" ADD CONSTRAINT "deployments_share_slug_unique" UNIQUE("share_slug");
|
776
web/drizzle/meta/0032_snapshot.json
Normal file
776
web/drizzle/meta/0032_snapshot.json
Normal file
@ -0,0 +1,776 @@
|
|||||||
|
{
|
||||||
|
"id": "1425ee00-66fb-4541-8da7-19b217944545",
|
||||||
|
"prevId": "1ca4fdb7-c0c4-4c39-8b47-f40282293da0",
|
||||||
|
"version": "5",
|
||||||
|
"dialect": "pg",
|
||||||
|
"tables": {
|
||||||
|
"api_keys": {
|
||||||
|
"name": "api_keys",
|
||||||
|
"schema": "comfyui_deploy",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"key": {
|
||||||
|
"name": "key",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"org_id": {
|
||||||
|
"name": "org_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"revoked": {
|
||||||
|
"name": "revoked",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"api_keys_user_id_users_id_fk": {
|
||||||
|
"name": "api_keys_user_id_users_id_fk",
|
||||||
|
"tableFrom": "api_keys",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"api_keys_key_unique": {
|
||||||
|
"name": "api_keys_key_unique",
|
||||||
|
"nullsNotDistinct": false,
|
||||||
|
"columns": [
|
||||||
|
"key"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deployments": {
|
||||||
|
"name": "deployments",
|
||||||
|
"schema": "comfyui_deploy",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"org_id": {
|
||||||
|
"name": "org_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"workflow_version_id": {
|
||||||
|
"name": "workflow_version_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"workflow_id": {
|
||||||
|
"name": "workflow_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"machine_id": {
|
||||||
|
"name": "machine_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"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": {}
|
||||||
|
}
|
||||||
|
}
|
@ -225,6 +225,13 @@
|
|||||||
"when": 1705763980972,
|
"when": 1705763980972,
|
||||||
"tag": "0031_fast_lyja",
|
"tag": "0031_fast_lyja",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 32,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1705806921697,
|
||||||
|
"tag": "0032_shallow_vermin",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -2,11 +2,11 @@ import { ButtonActionMenu } from "@/components/ButtonActionLoader";
|
|||||||
import { RunWorkflowInline } from "@/components/RunWorkflowInline";
|
import { RunWorkflowInline } from "@/components/RunWorkflowInline";
|
||||||
import { PublicRunOutputs } from "@/components/VersionSelect";
|
import { PublicRunOutputs } from "@/components/VersionSelect";
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
CardDescription,
|
CardDescription,
|
||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card";
|
} from "@/components/ui/card";
|
||||||
import { db } from "@/db/db";
|
import { db } from "@/db/db";
|
||||||
import { usersTable } from "@/db/schema";
|
import { usersTable } from "@/db/schema";
|
||||||
@ -14,9 +14,9 @@ import { getInputsFromWorkflow } from "@/lib/getInputsFromWorkflow";
|
|||||||
import { getRelativeTime } from "@/lib/getRelativeTime";
|
import { getRelativeTime } from "@/lib/getRelativeTime";
|
||||||
import { setInitialUserData } from "@/lib/setInitialUserData";
|
import { setInitialUserData } from "@/lib/setInitialUserData";
|
||||||
import {
|
import {
|
||||||
cloneMachine,
|
cloneMachine,
|
||||||
cloneWorkflow,
|
cloneWorkflow,
|
||||||
findSharedDeployment,
|
findSharedDeployment,
|
||||||
} from "@/server/curdDeploments";
|
} from "@/server/curdDeploments";
|
||||||
import { auth, clerkClient } from "@clerk/nextjs/server";
|
import { auth, clerkClient } from "@clerk/nextjs/server";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
@ -25,89 +25,87 @@ import { redirect } from "next/navigation";
|
|||||||
export const maxDuration = 300; // 5 minutes
|
export const maxDuration = 300; // 5 minutes
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
params,
|
params,
|
||||||
}: {
|
}: {
|
||||||
params: { share_id: string };
|
params: { share_id: string };
|
||||||
}) {
|
}) {
|
||||||
const { userId } = await auth();
|
const { userId } = await auth();
|
||||||
|
|
||||||
// If there is user, check if the user data is present
|
// If there is user, check if the user data is present
|
||||||
if (userId) {
|
if (userId) {
|
||||||
const user = await db.query.usersTable.findFirst({
|
const user = await db.query.usersTable.findFirst({
|
||||||
where: eq(usersTable.id, userId),
|
where: eq(usersTable.id, userId),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
await setInitialUserData(userId);
|
await setInitialUserData(userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const sharedDeployment = await findSharedDeployment(params.share_id);
|
const sharedDeployment = await findSharedDeployment(params.share_id);
|
||||||
|
|
||||||
if (!sharedDeployment) return redirect("/");
|
if (!sharedDeployment) return redirect("/");
|
||||||
|
|
||||||
const userName = sharedDeployment.workflow.org_id
|
const userName = sharedDeployment.workflow.org_id
|
||||||
? await clerkClient.organizations
|
? await clerkClient.organizations
|
||||||
.getOrganization({
|
.getOrganization({
|
||||||
organizationId: sharedDeployment.workflow.org_id,
|
organizationId: sharedDeployment.workflow.org_id,
|
||||||
})
|
})
|
||||||
.then((x) => x.name)
|
.then((x) => x.name)
|
||||||
: sharedDeployment.user.name;
|
: sharedDeployment.user.name;
|
||||||
|
|
||||||
const inputs = getInputsFromWorkflow(sharedDeployment.version);
|
const inputs = getInputsFromWorkflow(sharedDeployment.version);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-4 w-full grid grid-rows-[1fr,1fr] lg:grid-cols-[minmax(auto,500px),1fr] gap-4 max-h-[calc(100dvh-100px)]">
|
<div className="mt-4 w-full grid grid-rows-[1fr,1fr] lg:grid-cols-[minmax(auto,500px),1fr] gap-4 max-h-[calc(100dvh-100px)]">
|
||||||
<Card className="w-full h-fit mt-4">
|
<Card className="w-full h-fit mt-4">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="flex justify-between items-center">
|
<CardTitle className="flex justify-between items-center">
|
||||||
<div>
|
<div>
|
||||||
{userName}
|
{userName}
|
||||||
{" / "}
|
{" / "}
|
||||||
{sharedDeployment.workflow.name}
|
{sharedDeployment.workflow.name}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ButtonActionMenu
|
<ButtonActionMenu
|
||||||
title="Clone"
|
title="Clone"
|
||||||
actions={[
|
actions={[
|
||||||
{
|
{
|
||||||
title: "Workflow",
|
title: "Workflow",
|
||||||
action: cloneWorkflow.bind(null, sharedDeployment.id),
|
action: cloneWorkflow.bind(null, sharedDeployment.id),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Machine",
|
title: "Machine",
|
||||||
action: cloneMachine.bind(null, sharedDeployment.id),
|
action: cloneMachine.bind(null, sharedDeployment.id),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription suppressHydrationWarning={true}>
|
<CardDescription suppressHydrationWarning={true}>
|
||||||
{getRelativeTime(sharedDeployment?.updated_at)}
|
{getRelativeTime(sharedDeployment?.updated_at)}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div>
|
<div>
|
||||||
{sharedDeployment?.description && (
|
{sharedDeployment?.description && sharedDeployment?.description}
|
||||||
<>{sharedDeployment?.description}</>
|
</div>
|
||||||
)}
|
<RunWorkflowInline
|
||||||
</div>
|
inputs={inputs}
|
||||||
<RunWorkflowInline
|
machine_id={sharedDeployment.machine_id}
|
||||||
inputs={inputs}
|
workflow_version_id={sharedDeployment.workflow_version_id}
|
||||||
machine_id={sharedDeployment.machine_id}
|
/>
|
||||||
workflow_version_id={sharedDeployment.workflow_version_id}
|
</CardContent>
|
||||||
/>
|
</Card>
|
||||||
</CardContent>
|
<Card className="w-full h-fit mt-4">
|
||||||
</Card>
|
<CardHeader>
|
||||||
<Card className="w-full h-fit mt-4">
|
<CardDescription>Run outputs</CardDescription>
|
||||||
<CardHeader>
|
</CardHeader>
|
||||||
<CardDescription>Run outputs</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
|
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<PublicRunOutputs preview={sharedDeployment.showcase_media} />
|
<PublicRunOutputs preview={sharedDeployment.showcase_media} />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
|
import { CreateShareButton } from "@/components/CreateShareButton";
|
||||||
import { MachinesWSMain } from "@/components/MachinesWS";
|
import { MachinesWSMain } from "@/components/MachinesWS";
|
||||||
import { VersionDetails } from "@/components/VersionDetails";
|
import { VersionDetails } from "@/components/VersionDetails";
|
||||||
import {
|
import {
|
||||||
CopyWorkflowVersion,
|
CopyWorkflowVersion,
|
||||||
CreateDeploymentButton,
|
CreateDeploymentButton,
|
||||||
CreateShareButton,
|
MachineSelect,
|
||||||
MachineSelect,
|
RunWorkflowButton,
|
||||||
RunWorkflowButton,
|
VersionSelect,
|
||||||
VersionSelect,
|
ViewWorkflowDetailsButton,
|
||||||
ViewWorkflowDetailsButton,
|
|
||||||
} from "@/components/VersionSelect";
|
} from "@/components/VersionSelect";
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
|
@ -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,74 +15,80 @@ import { useRouter } from "next/navigation";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
export function ButtonAction({
|
export function ButtonAction({
|
||||||
action,
|
action,
|
||||||
children,
|
children,
|
||||||
...rest
|
routerAction = "back",
|
||||||
|
...rest
|
||||||
}: {
|
}: {
|
||||||
action: () => Promise<any>;
|
action: () => Promise<any>;
|
||||||
children: React.ReactNode;
|
routerAction?: "refresh" | "back";
|
||||||
|
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);
|
||||||
|
|
||||||
router.refresh();
|
if (routerAction === "back") {
|
||||||
}}
|
router.back();
|
||||||
{...rest}
|
router.refresh();
|
||||||
>
|
}
|
||||||
{children} {pending && <LoadingIcon />}
|
else if (routerAction === "refresh") router.refresh();
|
||||||
</button>
|
}}
|
||||||
);
|
{...rest}
|
||||||
|
>
|
||||||
|
{children} {pending && <LoadingIcon />}
|
||||||
|
</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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
66
web/src/components/CreateShareButton.tsx
Normal file
66
web/src/components/CreateShareButton.tsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
"use client";
|
||||||
|
import { LoadingIcon } from "@/components/LoadingIcon";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { createDeployments } from "@/server/curdDeploments";
|
||||||
|
import type { getMachines } from "@/server/curdMachine";
|
||||||
|
import type { findFirstTableWithVersion } from "@/server/findFirstTableWithVersion";
|
||||||
|
import { Share } from "lucide-react";
|
||||||
|
import { parseAsInteger, useQueryState } from "next-usequerystate";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useSelectedMachine } from "./VersionSelect";
|
||||||
|
import { callServerPromise } from "./callServerPromise";
|
||||||
|
|
||||||
|
export function CreateShareButton({
|
||||||
|
workflow,
|
||||||
|
machines,
|
||||||
|
}: {
|
||||||
|
workflow: Awaited<ReturnType<typeof findFirstTableWithVersion>>;
|
||||||
|
machines: Awaited<ReturnType<typeof getMachines>>;
|
||||||
|
}) {
|
||||||
|
const [version] = useQueryState("version", {
|
||||||
|
defaultValue: workflow?.versions[0].version ?? 1,
|
||||||
|
...parseAsInteger,
|
||||||
|
});
|
||||||
|
const [machine] = useSelectedMachine(machines);
|
||||||
|
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const workflow_version_id = workflow?.versions.find(
|
||||||
|
(x) => x.version === version,
|
||||||
|
)?.id;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button className="gap-2" disabled={isLoading} variant="outline">
|
||||||
|
Share {isLoading ? <LoadingIcon /> : <Share size={14} />}
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="w-56">
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={async () => {
|
||||||
|
if (!workflow_version_id) return;
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
await callServerPromise(
|
||||||
|
createDeployments(
|
||||||
|
workflow.id,
|
||||||
|
workflow_version_id,
|
||||||
|
machine,
|
||||||
|
"public-share",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
setIsLoading(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Public
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
}
|
@ -1,18 +1,18 @@
|
|||||||
import { DeploymentRow, SharePageDeploymentRow } from "./DeploymentRow";
|
|
||||||
import { CodeBlock } from "@/components/CodeBlock";
|
import { CodeBlock } from "@/components/CodeBlock";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogDescription,
|
DialogDescription,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
import { TableRow } from "@/components/ui/table";
|
import { TableRow } from "@/components/ui/table";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { getInputsFromWorkflow } from "@/lib/getInputsFromWorkflow";
|
import { getInputsFromWorkflow } from "@/lib/getInputsFromWorkflow";
|
||||||
import type { findAllDeployments } from "@/server/findAllRuns";
|
import type { findAllDeployments } from "@/server/findAllRuns";
|
||||||
|
import { DeploymentRow, SharePageDeploymentRow } from "./DeploymentRow";
|
||||||
|
|
||||||
const curlTemplate = `
|
const curlTemplate = `
|
||||||
curl --request POST \
|
curl --request POST \
|
||||||
@ -83,144 +83,145 @@ const run = await client.getRun(run_id);
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export function DeploymentDisplay({
|
export function DeploymentDisplay({
|
||||||
deployment,
|
deployment,
|
||||||
domain,
|
domain,
|
||||||
}: {
|
}: {
|
||||||
deployment: Awaited<ReturnType<typeof findAllDeployments>>[0];
|
deployment: Awaited<ReturnType<typeof findAllDeployments>>[0];
|
||||||
domain: string;
|
domain: string;
|
||||||
}) {
|
}) {
|
||||||
const workflowInput = getInputsFromWorkflow(deployment.version);
|
const workflowInput = getInputsFromWorkflow(deployment.version);
|
||||||
|
|
||||||
if (deployment.environment == "public-share") {
|
if (deployment.environment === "public-share") {
|
||||||
return <SharePageDeploymentRow deployment={deployment} />;
|
return <SharePageDeploymentRow deployment={deployment} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger asChild className="appearance-none hover:cursor-pointer">
|
<DialogTrigger asChild className="appearance-none hover:cursor-pointer">
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<DeploymentRow deployment={deployment} />
|
<DeploymentRow deployment={deployment} />
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="max-w-3xl">
|
<DialogContent className="max-w-3xl">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="capitalize">
|
<DialogTitle className="capitalize">
|
||||||
{deployment.environment} Deployment
|
{deployment.environment} Deployment
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription>Code for your deployment client</DialogDescription>
|
<DialogDescription>Code for your deployment client</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<ScrollArea className="max-h-[600px] pr-4">
|
<ScrollArea className="max-h-[600px] pr-4">
|
||||||
<Tabs defaultValue="client" className="w-full gap-2 text-sm">
|
<Tabs defaultValue="client" className="w-full gap-2 text-sm">
|
||||||
<TabsList className="grid w-fit grid-cols-3 mb-2">
|
<TabsList className="grid w-fit grid-cols-3 mb-2">
|
||||||
<TabsTrigger value="client">Server Client</TabsTrigger>
|
<TabsTrigger value="client">Server Client</TabsTrigger>
|
||||||
<TabsTrigger value="js">NodeJS Fetch</TabsTrigger>
|
<TabsTrigger value="js">NodeJS Fetch</TabsTrigger>
|
||||||
<TabsTrigger value="curl">CURL</TabsTrigger>
|
<TabsTrigger value="curl">CURL</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<TabsContent className="flex flex-col gap-2 !mt-0" value="client">
|
<TabsContent className="flex flex-col gap-2 !mt-0" value="client">
|
||||||
<div>
|
<div>
|
||||||
Copy and paste the ComfyDeployClient form
|
Copy and paste the ComfyDeployClient form
|
||||||
<a
|
<a
|
||||||
href="https://github.com/BennyKok/comfyui-deploy-next-example/blob/main/src/lib/comfy-deploy.ts"
|
href="https://github.com/BennyKok/comfyui-deploy-next-example/blob/main/src/lib/comfy-deploy.ts"
|
||||||
className="text-blue-500 hover:underline"
|
className="text-blue-500 hover:underline"
|
||||||
target="_blank" rel="noreferrer"
|
target="_blank"
|
||||||
>
|
rel="noreferrer"
|
||||||
here
|
>
|
||||||
</a>
|
here
|
||||||
</div>
|
</a>
|
||||||
<CodeBlock
|
</div>
|
||||||
lang="js"
|
<CodeBlock
|
||||||
code={formatCode(
|
lang="js"
|
||||||
domain == "https://www.comfydeploy.com"
|
code={formatCode(
|
||||||
? jsClientSetupTemplateHostedVersion
|
domain == "https://www.comfydeploy.com"
|
||||||
: jsClientSetupTemplate,
|
? jsClientSetupTemplateHostedVersion
|
||||||
deployment,
|
: jsClientSetupTemplate,
|
||||||
domain,
|
deployment,
|
||||||
workflowInput
|
domain,
|
||||||
)}
|
workflowInput,
|
||||||
/>
|
)}
|
||||||
Create a run via deployment id
|
/>
|
||||||
<CodeBlock
|
Create a run via deployment id
|
||||||
lang="js"
|
<CodeBlock
|
||||||
code={formatCode(
|
lang="js"
|
||||||
workflowInput && workflowInput.length > 0
|
code={formatCode(
|
||||||
? jsClientCreateRunTemplate
|
workflowInput && workflowInput.length > 0
|
||||||
: jsClientCreateRunNoInputsTemplate,
|
? jsClientCreateRunTemplate
|
||||||
deployment,
|
: jsClientCreateRunNoInputsTemplate,
|
||||||
domain,
|
deployment,
|
||||||
workflowInput
|
domain,
|
||||||
)}
|
workflowInput,
|
||||||
/>
|
)}
|
||||||
Check the status of the run, and retrieve the outputs
|
/>
|
||||||
<CodeBlock
|
Check the status of the run, and retrieve the outputs
|
||||||
lang="js"
|
<CodeBlock
|
||||||
code={formatCode(
|
lang="js"
|
||||||
clientTemplate_checkStatus,
|
code={formatCode(
|
||||||
deployment,
|
clientTemplate_checkStatus,
|
||||||
domain
|
deployment,
|
||||||
)}
|
domain,
|
||||||
/>
|
)}
|
||||||
</TabsContent>
|
/>
|
||||||
<TabsContent className="flex flex-col gap-2 !mt-0" value="js">
|
</TabsContent>
|
||||||
Trigger the workflow
|
<TabsContent className="flex flex-col gap-2 !mt-0" value="js">
|
||||||
<CodeBlock
|
Trigger the workflow
|
||||||
lang="js"
|
<CodeBlock
|
||||||
code={formatCode(jsTemplate, deployment, domain, workflowInput)}
|
lang="js"
|
||||||
/>
|
code={formatCode(jsTemplate, deployment, domain, workflowInput)}
|
||||||
Check the status of the run, and retrieve the outputs
|
/>
|
||||||
<CodeBlock
|
Check the status of the run, and retrieve the outputs
|
||||||
lang="js"
|
<CodeBlock
|
||||||
code={formatCode(jsTemplate_checkStatus, deployment, domain)}
|
lang="js"
|
||||||
/>
|
code={formatCode(jsTemplate_checkStatus, deployment, domain)}
|
||||||
</TabsContent>
|
/>
|
||||||
<TabsContent className="flex flex-col gap-2 !mt-2" value="curl">
|
</TabsContent>
|
||||||
<CodeBlock
|
<TabsContent className="flex flex-col gap-2 !mt-2" value="curl">
|
||||||
lang="bash"
|
<CodeBlock
|
||||||
code={formatCode(curlTemplate, deployment, domain)}
|
lang="bash"
|
||||||
/>
|
code={formatCode(curlTemplate, deployment, domain)}
|
||||||
<CodeBlock
|
/>
|
||||||
lang="bash"
|
<CodeBlock
|
||||||
code={formatCode(curlTemplate_checkStatus, deployment, domain)}
|
lang="bash"
|
||||||
/>
|
code={formatCode(curlTemplate_checkStatus, deployment, domain)}
|
||||||
</TabsContent>
|
/>
|
||||||
</Tabs>
|
</TabsContent>
|
||||||
</ScrollArea>
|
</Tabs>
|
||||||
</DialogContent>
|
</ScrollArea>
|
||||||
</Dialog>
|
</DialogContent>
|
||||||
);
|
</Dialog>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatCode(
|
function formatCode(
|
||||||
codeTemplate: string,
|
codeTemplate: string,
|
||||||
deployment: Awaited<ReturnType<typeof findAllDeployments>>[0],
|
deployment: Awaited<ReturnType<typeof findAllDeployments>>[0],
|
||||||
domain: string,
|
domain: string,
|
||||||
inputs?: ReturnType<typeof getInputsFromWorkflow>,
|
inputs?: ReturnType<typeof getInputsFromWorkflow>,
|
||||||
inputsTabs?: number
|
inputsTabs?: number,
|
||||||
) {
|
) {
|
||||||
if (inputs && inputs.length > 0) {
|
if (inputs && inputs.length > 0) {
|
||||||
codeTemplate = codeTemplate.replace(
|
codeTemplate = codeTemplate.replace(
|
||||||
"inputs: {}",
|
"inputs: {}",
|
||||||
`inputs: ${JSON.stringify(
|
`inputs: ${JSON.stringify(
|
||||||
Object.fromEntries(
|
Object.fromEntries(
|
||||||
inputs.map((x) => {
|
inputs.map((x) => {
|
||||||
return [x?.input_id, ""];
|
return [x?.input_id, ""];
|
||||||
})
|
}),
|
||||||
),
|
),
|
||||||
null,
|
null,
|
||||||
2
|
2,
|
||||||
)
|
)
|
||||||
.split("\n")
|
.split("\n")
|
||||||
.map((line, index) => (index === 0 ? line : ` ${line}`)) // Add two spaces indentation except for the first line
|
.map((line, index) => (index === 0 ? line : ` ${line}`)) // Add two spaces indentation except for the first line
|
||||||
.join("\n")}`
|
.join("\n")}`,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
codeTemplate = codeTemplate.replace(
|
codeTemplate = codeTemplate.replace(
|
||||||
`
|
`
|
||||||
inputs: {}`,
|
inputs: {}`,
|
||||||
""
|
"",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return codeTemplate
|
return codeTemplate
|
||||||
.replace("<URL>", `${domain ?? "http://localhost:3000"}/api/run`)
|
.replace("<URL>", `${domain ?? "http://localhost:3000"}/api/run`)
|
||||||
.replace("<ID>", deployment.id)
|
.replace("<ID>", deployment.id)
|
||||||
.replace("<URLONLY>", domain ?? "http://localhost:3000");
|
.replace("<URLONLY>", domain ?? "http://localhost:3000");
|
||||||
}
|
}
|
||||||
|
@ -6,55 +6,57 @@ import type { findAllDeployments } from "@/server/findAllRuns";
|
|||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
export function SharePageDeploymentRow({
|
export function SharePageDeploymentRow({
|
||||||
deployment,
|
deployment,
|
||||||
}: {
|
}: {
|
||||||
deployment: Awaited<ReturnType<typeof findAllDeployments>>[0];
|
deployment: Awaited<ReturnType<typeof findAllDeployments>>[0];
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
return (
|
return (
|
||||||
<TableRow
|
<TableRow
|
||||||
className="appearance-none hover:cursor-pointer"
|
className="appearance-none hover:cursor-pointer"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (deployment.environment == "public-share") {
|
if (deployment.environment === "public-share") {
|
||||||
router.push(`/share/${deployment.id}/settings`);
|
router.push(
|
||||||
}
|
`/share/${deployment.share_slug ?? deployment.id}/settings`,
|
||||||
}}
|
);
|
||||||
>
|
}
|
||||||
<TableCell className="capitalize truncate">
|
}}
|
||||||
{deployment.environment}
|
>
|
||||||
</TableCell>
|
<TableCell className="capitalize truncate">
|
||||||
<TableCell className="font-medium truncate">
|
{deployment.environment}
|
||||||
{deployment.version?.version}
|
</TableCell>
|
||||||
</TableCell>
|
<TableCell className="font-medium truncate">
|
||||||
<TableCell className="font-medium truncate">
|
{deployment.version?.version}
|
||||||
{deployment.machine?.name}
|
</TableCell>
|
||||||
</TableCell>
|
<TableCell className="font-medium truncate">
|
||||||
<TableCell className="text-right truncate">
|
{deployment.machine?.name}
|
||||||
{getRelativeTime(deployment.updated_at)}
|
</TableCell>
|
||||||
</TableCell>
|
<TableCell className="text-right truncate">
|
||||||
</TableRow>
|
{getRelativeTime(deployment.updated_at)}
|
||||||
);
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DeploymentRow({
|
export function DeploymentRow({
|
||||||
deployment,
|
deployment,
|
||||||
}: {
|
}: {
|
||||||
deployment: Awaited<ReturnType<typeof findAllDeployments>>[0];
|
deployment: Awaited<ReturnType<typeof findAllDeployments>>[0];
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TableCell className="capitalize truncate">
|
<TableCell className="capitalize truncate">
|
||||||
{deployment.environment}
|
{deployment.environment}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="font-medium truncate">
|
<TableCell className="font-medium truncate">
|
||||||
{deployment.version?.version}
|
{deployment.version?.version}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="font-medium truncate">
|
<TableCell className="font-medium truncate">
|
||||||
{deployment.machine?.name}
|
{deployment.machine?.name}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-right truncate">
|
<TableCell className="text-right truncate">
|
||||||
{getRelativeTime(deployment.updated_at)}
|
{getRelativeTime(deployment.updated_at)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -58,13 +58,14 @@ export function SharePageSettings({
|
|||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<ButtonAction
|
<ButtonAction
|
||||||
|
routerAction="back"
|
||||||
action={removePublicShareDeployment.bind(null, deployment.id)}
|
action={removePublicShareDeployment.bind(null, deployment.id)}
|
||||||
>
|
>
|
||||||
Remove
|
Remove
|
||||||
</ButtonAction>
|
</ButtonAction>
|
||||||
</Button>
|
</Button>
|
||||||
<Button asChild className="gap-2 truncate" type="button">
|
<Button asChild className="gap-2 truncate" type="button">
|
||||||
<Link href={`/share/${deployment.id}`} target="_blank">
|
<Link href={`/share/${deployment.share_slug ?? deployment.id}`} target="_blank">
|
||||||
View Share Page <ExternalLink size={14} />
|
View Share Page <ExternalLink size={14} />
|
||||||
</Link>
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { workflowVersionInputsToZod } from "../lib/workflowVersionInputsToZod";
|
|
||||||
import { callServerPromise } from "./callServerPromise";
|
|
||||||
import fetcher from "./fetcher";
|
|
||||||
import { LoadingIcon } from "@/components/LoadingIcon";
|
import { LoadingIcon } from "@/components/LoadingIcon";
|
||||||
import AutoForm, { AutoFormSubmit } from "@/components/ui/auto-form";
|
import AutoForm, { AutoFormSubmit } from "@/components/ui/auto-form";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
@ -44,20 +41,16 @@ import { checkStatus, createRun } from "@/server/createRun";
|
|||||||
import { createDeployments } from "@/server/curdDeploments";
|
import { createDeployments } from "@/server/curdDeploments";
|
||||||
import type { getMachines } from "@/server/curdMachine";
|
import type { getMachines } from "@/server/curdMachine";
|
||||||
import type { findFirstTableWithVersion } from "@/server/findFirstTableWithVersion";
|
import type { findFirstTableWithVersion } from "@/server/findFirstTableWithVersion";
|
||||||
import {
|
import { Copy, ExternalLink, Info, MoreVertical, Play } from "lucide-react";
|
||||||
Copy,
|
|
||||||
ExternalLink,
|
|
||||||
Info,
|
|
||||||
MoreVertical,
|
|
||||||
Play,
|
|
||||||
Share,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { parseAsInteger, useQueryState } from "next-usequerystate";
|
import { parseAsInteger, useQueryState } from "next-usequerystate";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
|
import { workflowVersionInputsToZod } from "../lib/workflowVersionInputsToZod";
|
||||||
|
import { callServerPromise } from "./callServerPromise";
|
||||||
|
import fetcher from "./fetcher";
|
||||||
|
|
||||||
export function VersionSelect({
|
export function VersionSelect({
|
||||||
workflow,
|
workflow,
|
||||||
@ -122,12 +115,14 @@ export function MachineSelect({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function useSelectedMachine(machines: Awaited<ReturnType<typeof getMachines>>) {
|
export function useSelectedMachine(
|
||||||
const a = useQueryState("machine", {
|
machines: Awaited<ReturnType<typeof getMachines>>,
|
||||||
defaultValue: machines?.[0]?.id ?? "",
|
) {
|
||||||
});
|
const a = useQueryState("machine", {
|
||||||
|
defaultValue: machines?.[0]?.id ?? "",
|
||||||
|
});
|
||||||
|
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
type PublicRunStore = {
|
type PublicRunStore = {
|
||||||
@ -373,55 +368,6 @@ export function CreateDeploymentButton({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CreateShareButton({
|
|
||||||
workflow,
|
|
||||||
machines,
|
|
||||||
}: {
|
|
||||||
workflow: Awaited<ReturnType<typeof findFirstTableWithVersion>>;
|
|
||||||
machines: Awaited<ReturnType<typeof getMachines>>;
|
|
||||||
}) {
|
|
||||||
const [version] = useQueryState("version", {
|
|
||||||
defaultValue: workflow?.versions[0].version ?? 1,
|
|
||||||
...parseAsInteger,
|
|
||||||
});
|
|
||||||
const [machine] = useSelectedMachine(machines);
|
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
|
||||||
const workflow_version_id = workflow?.versions.find(
|
|
||||||
(x) => x.version === version
|
|
||||||
)?.id;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button className="gap-2" disabled={isLoading} variant="outline">
|
|
||||||
Share {isLoading ? <LoadingIcon /> : <Share size={14} />}
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent className="w-56">
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={async () => {
|
|
||||||
if (!workflow_version_id) return;
|
|
||||||
|
|
||||||
setIsLoading(true);
|
|
||||||
await callServerPromise(
|
|
||||||
createDeployments(
|
|
||||||
workflow.id,
|
|
||||||
workflow_version_id,
|
|
||||||
machine,
|
|
||||||
"public-share"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
setIsLoading(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Public
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CopyWorkflowVersion({
|
export function CopyWorkflowVersion({
|
||||||
workflow,
|
workflow,
|
||||||
}: {
|
}: {
|
||||||
|
@ -257,30 +257,31 @@ export const showcaseMediaNullable = z
|
|||||||
.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),
|
||||||
description: text("description"),
|
share_slug: text("share_slug").unique(),
|
||||||
showcase_media:
|
description: text("description"),
|
||||||
jsonb("showcase_media").$type<z.infer<typeof showcaseMedia>>(),
|
showcase_media:
|
||||||
environment: deploymentEnvironment("environment").notNull(),
|
jsonb("showcase_media").$type<z.infer<typeof showcaseMedia>>(),
|
||||||
created_at: timestamp("created_at").defaultNow().notNull(),
|
environment: deploymentEnvironment("environment").notNull(),
|
||||||
updated_at: timestamp("updated_at").defaultNow().notNull(),
|
created_at: timestamp("created_at").defaultNow().notNull(),
|
||||||
|
updated_at: timestamp("updated_at").defaultNow().notNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const publicShareDeployment = z.object({
|
export const publicShareDeployment = z.object({
|
||||||
|
@ -7,247 +7,279 @@ import { createNewWorkflow } from "@/server/createNewWorkflow";
|
|||||||
import { addCustomMachine } from "@/server/curdMachine";
|
import { addCustomMachine } from "@/server/curdMachine";
|
||||||
import { withServerPromise } from "@/server/withServerPromise";
|
import { withServerPromise } from "@/server/withServerPromise";
|
||||||
import { auth } from "@clerk/nextjs";
|
import { auth } from "@clerk/nextjs";
|
||||||
import { and, eq, isNull } from "drizzle-orm";
|
import { clerkClient } from "@clerk/nextjs/server";
|
||||||
|
import slugify from "@sindresorhus/slugify";
|
||||||
|
import { and, eq, isNull, or } from "drizzle-orm";
|
||||||
import { revalidatePath } from "next/cache";
|
import { revalidatePath } from "next/cache";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import "server-only";
|
import "server-only";
|
||||||
|
import { validate as isValidUUID } from "uuid";
|
||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
|
|
||||||
export async function createDeployments(
|
export async function createDeployments(
|
||||||
workflow_id: string,
|
workflow_id: string,
|
||||||
version_id: string,
|
version_id: string,
|
||||||
machine_id: string,
|
machine_id: string,
|
||||||
environment: DeploymentType["environment"]
|
environment: DeploymentType["environment"],
|
||||||
) {
|
) {
|
||||||
const { userId, orgId } = auth();
|
const { userId, orgId } = auth();
|
||||||
if (!userId) throw new Error("No user id");
|
if (!userId) throw new Error("No user id");
|
||||||
|
|
||||||
if (!machine_id) {
|
if (!machine_id) {
|
||||||
throw new Error("No machine id provided");
|
throw new Error("No machine id provided");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same environment and same workflow
|
// Same environment and same workflow
|
||||||
const existingDeployment = await db.query.deploymentsTable.findFirst({
|
const existingDeployment = await db.query.deploymentsTable.findFirst({
|
||||||
where: and(
|
where: and(
|
||||||
eq(deploymentsTable.workflow_id, workflow_id),
|
eq(deploymentsTable.workflow_id, workflow_id),
|
||||||
eq(deploymentsTable.environment, environment)
|
eq(deploymentsTable.environment, environment),
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (existingDeployment) {
|
if (existingDeployment) {
|
||||||
await db
|
await db
|
||||||
.update(deploymentsTable)
|
.update(deploymentsTable)
|
||||||
.set({
|
.set({
|
||||||
workflow_id,
|
workflow_id,
|
||||||
workflow_version_id: version_id,
|
workflow_version_id: version_id,
|
||||||
machine_id,
|
machine_id,
|
||||||
org_id: orgId,
|
org_id: orgId,
|
||||||
})
|
})
|
||||||
.where(eq(deploymentsTable.id, existingDeployment.id));
|
.where(eq(deploymentsTable.id, existingDeployment.id));
|
||||||
} else {
|
} else {
|
||||||
await db.insert(deploymentsTable).values({
|
const workflow = await db.query.workflowTable.findFirst({
|
||||||
user_id: userId,
|
where: eq(workflowTable.id, workflow_id),
|
||||||
workflow_id,
|
with: {
|
||||||
workflow_version_id: version_id,
|
user: {
|
||||||
machine_id,
|
columns: {
|
||||||
environment,
|
name: true,
|
||||||
org_id: orgId,
|
},
|
||||||
});
|
},
|
||||||
}
|
},
|
||||||
revalidatePath(`/${workflow_id}`);
|
});
|
||||||
return {
|
|
||||||
message: `Successfully created deployment for ${environment}`,
|
if (!workflow) throw new Error("No workflow found");
|
||||||
};
|
|
||||||
|
const userName = workflow.org_id
|
||||||
|
? await clerkClient.organizations
|
||||||
|
.getOrganization({
|
||||||
|
organizationId: workflow.org_id,
|
||||||
|
})
|
||||||
|
.then((x) => x.name)
|
||||||
|
: workflow.user.name;
|
||||||
|
|
||||||
|
await db.insert(deploymentsTable).values({
|
||||||
|
user_id: userId,
|
||||||
|
workflow_id,
|
||||||
|
workflow_version_id: version_id,
|
||||||
|
machine_id,
|
||||||
|
environment,
|
||||||
|
org_id: orgId,
|
||||||
|
share_slug: slugify(`${userName} ${workflow.name}`),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
revalidatePath(`/${workflow_id}`);
|
||||||
|
return {
|
||||||
|
message: `Successfully created deployment for ${environment}`,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function findAllDeployments() {
|
export async function findAllDeployments() {
|
||||||
const { userId, orgId } = auth();
|
const { userId, orgId } = auth();
|
||||||
if (!userId) throw new Error("No user id");
|
if (!userId) throw new Error("No user id");
|
||||||
|
|
||||||
const deployments = await db.query.workflowTable.findMany({
|
const deployments = await db.query.workflowTable.findMany({
|
||||||
where: and(
|
where: and(
|
||||||
orgId
|
orgId
|
||||||
? eq(workflowTable.org_id, orgId)
|
? eq(workflowTable.org_id, orgId)
|
||||||
: and(eq(workflowTable.user_id, userId), isNull(workflowTable.org_id))
|
: and(eq(workflowTable.user_id, userId), isNull(workflowTable.org_id)),
|
||||||
),
|
),
|
||||||
columns: {
|
columns: {
|
||||||
name: true,
|
name: true,
|
||||||
},
|
},
|
||||||
with: {
|
with: {
|
||||||
deployments: {
|
deployments: {
|
||||||
columns: {
|
columns: {
|
||||||
environment: true,
|
environment: true,
|
||||||
},
|
},
|
||||||
with: {
|
with: {
|
||||||
version: {
|
version: {
|
||||||
columns: {
|
columns: {
|
||||||
id: true,
|
id: true,
|
||||||
snapshot: true,
|
snapshot: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return deployments;
|
return deployments;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function findSharedDeployment(workflow_id: string) {
|
export async function findSharedDeployment(workflow_id: string) {
|
||||||
const deploymentData = await db.query.deploymentsTable.findFirst({
|
const deploymentData = await db.query.deploymentsTable.findFirst({
|
||||||
where: and(
|
where: and(
|
||||||
eq(deploymentsTable.environment, "public-share"),
|
eq(deploymentsTable.environment, "public-share"),
|
||||||
eq(deploymentsTable.id, workflow_id)
|
isValidUUID(workflow_id)
|
||||||
),
|
? eq(deploymentsTable.id, workflow_id)
|
||||||
with: {
|
: eq(deploymentsTable.share_slug, workflow_id),
|
||||||
user: true,
|
),
|
||||||
machine: true,
|
with: {
|
||||||
workflow: {
|
user: true,
|
||||||
columns: {
|
machine: true,
|
||||||
name: true,
|
workflow: {
|
||||||
org_id: true,
|
columns: {
|
||||||
user_id: true,
|
name: true,
|
||||||
},
|
org_id: true,
|
||||||
},
|
user_id: true,
|
||||||
version: true,
|
},
|
||||||
},
|
},
|
||||||
});
|
version: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return deploymentData;
|
return deploymentData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const removePublicShareDeployment = withServerPromise(
|
export const removePublicShareDeployment = withServerPromise(
|
||||||
async (deployment_id: string) => {
|
async (deployment_id: string) => {
|
||||||
await db
|
const [removed] = await db
|
||||||
.delete(deploymentsTable)
|
.delete(deploymentsTable)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(deploymentsTable.environment, "public-share"),
|
eq(deploymentsTable.environment, "public-share"),
|
||||||
eq(deploymentsTable.id, deployment_id)
|
eq(deploymentsTable.id, deployment_id),
|
||||||
)
|
),
|
||||||
);
|
).returning();
|
||||||
}
|
|
||||||
|
// revalidatePath(
|
||||||
|
// `/workflows/${removed.workflow_id}`
|
||||||
|
// )
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export const cloneWorkflow = withServerPromise(
|
export const cloneWorkflow = withServerPromise(
|
||||||
async (deployment_id: string) => {
|
async (deployment_id: string) => {
|
||||||
const deployment = await db.query.deploymentsTable.findFirst({
|
const deployment = await db.query.deploymentsTable.findFirst({
|
||||||
where: and(
|
where: and(
|
||||||
eq(deploymentsTable.environment, "public-share"),
|
eq(deploymentsTable.environment, "public-share"),
|
||||||
eq(deploymentsTable.id, deployment_id)
|
eq(deploymentsTable.id, deployment_id),
|
||||||
),
|
),
|
||||||
with: {
|
with: {
|
||||||
version: true,
|
version: true,
|
||||||
workflow: true,
|
workflow: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!deployment) throw new Error("No deployment found");
|
if (!deployment) throw new Error("No deployment found");
|
||||||
|
|
||||||
const { userId, orgId } = auth();
|
const { userId, orgId } = auth();
|
||||||
|
|
||||||
if (!userId) throw new Error("No user id");
|
if (!userId) throw new Error("No user id");
|
||||||
|
|
||||||
await createNewWorkflow({
|
await createNewWorkflow({
|
||||||
user_id: userId,
|
user_id: userId,
|
||||||
org_id: orgId,
|
org_id: orgId,
|
||||||
workflow_name: `${deployment.workflow.name} (Cloned)`,
|
workflow_name: `${deployment.workflow.name} (Cloned)`,
|
||||||
workflowData: {
|
workflowData: {
|
||||||
workflow: deployment.version.workflow,
|
workflow: deployment.version.workflow,
|
||||||
workflow_api: deployment?.version.workflow_api,
|
workflow_api: deployment?.version.workflow_api,
|
||||||
snapshot: deployment?.version.snapshot,
|
snapshot: deployment?.version.snapshot,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
redirect(`/workflows/${deployment.workflow.id}`);
|
redirect(`/workflows/${deployment.workflow.id}`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: "Successfully cloned workflow",
|
message: "Successfully cloned workflow",
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export const cloneMachine = withServerPromise(async (deployment_id: string) => {
|
export const cloneMachine = withServerPromise(async (deployment_id: string) => {
|
||||||
const deployment = await db.query.deploymentsTable.findFirst({
|
const deployment = await db.query.deploymentsTable.findFirst({
|
||||||
where: and(
|
where: and(
|
||||||
eq(deploymentsTable.environment, "public-share"),
|
eq(deploymentsTable.environment, "public-share"),
|
||||||
eq(deploymentsTable.id, deployment_id)
|
eq(deploymentsTable.id, deployment_id),
|
||||||
),
|
),
|
||||||
with: {
|
with: {
|
||||||
machine: true,
|
machine: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!deployment) throw new Error("No deployment found");
|
if (!deployment) throw new Error("No deployment found");
|
||||||
if (deployment.machine.type !== "comfy-deploy-serverless")
|
if (deployment.machine.type !== "comfy-deploy-serverless")
|
||||||
throw new Error("Can only clone comfy-deploy-serverlesss");
|
throw new Error("Can only clone comfy-deploy-serverlesss");
|
||||||
|
|
||||||
const { userId, orgId } = auth();
|
const { userId, orgId } = auth();
|
||||||
|
|
||||||
if (!userId) throw new Error("No user id");
|
if (!userId) throw new Error("No user id");
|
||||||
|
|
||||||
await addCustomMachine({
|
await addCustomMachine({
|
||||||
gpu: deployment.machine.gpu,
|
gpu: deployment.machine.gpu,
|
||||||
models: deployment.machine.models,
|
models: deployment.machine.models,
|
||||||
snapshot: deployment.machine.snapshot,
|
snapshot: deployment.machine.snapshot,
|
||||||
name: `${deployment.machine.name} (Cloned)`,
|
name: `${deployment.machine.name} (Cloned)`,
|
||||||
type: "comfy-deploy-serverless",
|
type: "comfy-deploy-serverless",
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: "Successfully cloned workflow",
|
message: "Successfully cloned workflow",
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function findUserShareDeployment(share_id: string) {
|
export async function findUserShareDeployment(share_id: string) {
|
||||||
const { userId, orgId } = auth();
|
const { userId, orgId } = auth();
|
||||||
|
|
||||||
if (!userId) throw new Error("No user id");
|
if (!userId) throw new Error("No user id");
|
||||||
|
|
||||||
const [deployment] = await db
|
const [deployment] = await db
|
||||||
.select()
|
.select()
|
||||||
.from(deploymentsTable)
|
.from(deploymentsTable)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(deploymentsTable.id, share_id),
|
isValidUUID(share_id)
|
||||||
eq(deploymentsTable.environment, "public-share"),
|
? eq(deploymentsTable.id, share_id)
|
||||||
orgId
|
: eq(deploymentsTable.share_slug, share_id),
|
||||||
? eq(deploymentsTable.org_id, orgId)
|
eq(deploymentsTable.environment, "public-share"),
|
||||||
: and(
|
orgId
|
||||||
eq(deploymentsTable.user_id, userId),
|
? eq(deploymentsTable.org_id, orgId)
|
||||||
isNull(deploymentsTable.org_id)
|
: and(
|
||||||
)
|
eq(deploymentsTable.user_id, userId),
|
||||||
)
|
isNull(deploymentsTable.org_id),
|
||||||
);
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
if (!deployment) throw new Error("No deployment found");
|
if (!deployment) throw new Error("No deployment found");
|
||||||
|
|
||||||
return deployment;
|
return deployment;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateSharePageInfo = withServerPromise(
|
export const updateSharePageInfo = withServerPromise(
|
||||||
async ({
|
async ({
|
||||||
id,
|
id,
|
||||||
...data
|
...data
|
||||||
}: z.infer<typeof publicShareDeployment> & {
|
}: z.infer<typeof publicShareDeployment> & {
|
||||||
id: string;
|
id: string;
|
||||||
}) => {
|
}) => {
|
||||||
const { userId } = auth();
|
const { userId } = auth();
|
||||||
if (!userId) return { error: "No user id" };
|
if (!userId) return { error: "No user id" };
|
||||||
|
|
||||||
console.log(data);
|
console.log(data);
|
||||||
|
|
||||||
const [deployment] = await db
|
const [deployment] = await db
|
||||||
.update(deploymentsTable)
|
.update(deploymentsTable)
|
||||||
.set(data)
|
.set(data)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(deploymentsTable.environment, "public-share"),
|
eq(deploymentsTable.environment, "public-share"),
|
||||||
eq(deploymentsTable.id, id)
|
eq(deploymentsTable.id, id),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
return { message: "Info Updated" };
|
return { message: "Info Updated" };
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user