diff --git a/routes.py b/routes.py
index 6ce05db..68d5b89 100644
--- a/routes.py
+++ b/routes.py
@@ -213,7 +213,7 @@ async def upload_file(prompt_id, filename, subfolder=None):
filename = os.path.basename(filename)
file = os.path.join(output_dir, filename)
- print("uploading file", file)
+ # print("uploading file", file)
file_upload_endpoint = prompt_metadata[prompt_id]['file_upload_endpoint']
diff --git a/web/bun.lockb b/web/bun.lockb
index 6229656..350b797 100755
Binary files a/web/bun.lockb and b/web/bun.lockb differ
diff --git a/web/drizzle/0005_worthless_dakota_north.sql b/web/drizzle/0005_worthless_dakota_north.sql
new file mode 100644
index 0000000..abf330e
--- /dev/null
+++ b/web/drizzle/0005_worthless_dakota_north.sql
@@ -0,0 +1,40 @@
+DO $$ BEGIN
+ CREATE TYPE "deployment_environment" AS ENUM('staging', 'production');
+EXCEPTION
+ WHEN duplicate_object THEN null;
+END $$;
+--> statement-breakpoint
+CREATE TABLE IF NOT EXISTS "comfy_deploy"."deployments" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "user_id" text NOT NULL,
+ "workflow_version_id" uuid NOT NULL,
+ "workflow_id" uuid NOT NULL,
+ "machine_id" uuid,
+ "environment" "deployment_environment" NOT NULL,
+ "created_at" timestamp DEFAULT now() NOT NULL,
+ "updated_at" timestamp DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+DO $$ BEGIN
+ ALTER TABLE "comfy_deploy"."deployments" ADD CONSTRAINT "deployments_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "comfy_deploy"."users"("id") ON DELETE cascade ON UPDATE no action;
+EXCEPTION
+ WHEN duplicate_object THEN null;
+END $$;
+--> statement-breakpoint
+DO $$ BEGIN
+ ALTER TABLE "comfy_deploy"."deployments" ADD CONSTRAINT "deployments_workflow_version_id_workflow_versions_id_fk" FOREIGN KEY ("workflow_version_id") REFERENCES "comfy_deploy"."workflow_versions"("id") ON DELETE no action ON UPDATE no action;
+EXCEPTION
+ WHEN duplicate_object THEN null;
+END $$;
+--> statement-breakpoint
+DO $$ BEGIN
+ ALTER TABLE "comfy_deploy"."deployments" ADD CONSTRAINT "deployments_workflow_id_workflows_id_fk" FOREIGN KEY ("workflow_id") REFERENCES "comfy_deploy"."workflows"("id") ON DELETE no action ON UPDATE no action;
+EXCEPTION
+ WHEN duplicate_object THEN null;
+END $$;
+--> statement-breakpoint
+DO $$ BEGIN
+ ALTER TABLE "comfy_deploy"."deployments" ADD CONSTRAINT "deployments_machine_id_machines_id_fk" FOREIGN KEY ("machine_id") REFERENCES "comfy_deploy"."machines"("id") ON DELETE no action ON UPDATE no action;
+EXCEPTION
+ WHEN duplicate_object THEN null;
+END $$;
diff --git a/web/drizzle/0006_chief_mariko_yashida.sql b/web/drizzle/0006_chief_mariko_yashida.sql
new file mode 100644
index 0000000..235cf36
--- /dev/null
+++ b/web/drizzle/0006_chief_mariko_yashida.sql
@@ -0,0 +1 @@
+ALTER TABLE "comfy_deploy"."deployments" ALTER COLUMN "machine_id" SET NOT NULL;
\ No newline at end of file
diff --git a/web/drizzle/meta/0005_snapshot.json b/web/drizzle/meta/0005_snapshot.json
new file mode 100644
index 0000000..c2b1475
--- /dev/null
+++ b/web/drizzle/meta/0005_snapshot.json
@@ -0,0 +1,531 @@
+{
+ "id": "af90a047-fec9-4d35-be52-82f8e0ee1cf4",
+ "prevId": "07a389e2-3713-4047-93e7-bf1da2333b16",
+ "version": "5",
+ "dialect": "pg",
+ "tables": {
+ "deployments": {
+ "name": "deployments",
+ "schema": "comfy_deploy",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "workflow_version_id": {
+ "name": "workflow_version_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "workflow_id": {
+ "name": "workflow_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "machine_id": {
+ "name": "machine_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": 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": "no action",
+ "onUpdate": "no action"
+ },
+ "deployments_machine_id_machines_id_fk": {
+ "name": "deployments_machine_id_machines_id_fk",
+ "tableFrom": "deployments",
+ "tableTo": "machines",
+ "columnsFrom": [
+ "machine_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "machines": {
+ "name": "machines",
+ "schema": "comfy_deploy",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "endpoint": {
+ "name": "endpoint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "machines_user_id_users_id_fk": {
+ "name": "machines_user_id_users_id_fk",
+ "tableFrom": "machines",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "users": {
+ "name": "users",
+ "schema": "comfy_deploy",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "workflow_run_outputs": {
+ "name": "workflow_run_outputs",
+ "schema": "comfy_deploy",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "run_id": {
+ "name": "run_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "data": {
+ "name": "data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "workflow_run_outputs_run_id_workflow_runs_id_fk": {
+ "name": "workflow_run_outputs_run_id_workflow_runs_id_fk",
+ "tableFrom": "workflow_run_outputs",
+ "tableTo": "workflow_runs",
+ "columnsFrom": [
+ "run_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "workflow_runs": {
+ "name": "workflow_runs",
+ "schema": "comfy_deploy",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "workflow_version_id": {
+ "name": "workflow_version_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "workflow_id": {
+ "name": "workflow_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "machine_id": {
+ "name": "machine_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "workflow_run_status",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'not-started'"
+ },
+ "ended_at": {
+ "name": "ended_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "workflow_runs_workflow_version_id_workflow_versions_id_fk": {
+ "name": "workflow_runs_workflow_version_id_workflow_versions_id_fk",
+ "tableFrom": "workflow_runs",
+ "tableTo": "workflow_versions",
+ "columnsFrom": [
+ "workflow_version_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "workflow_runs_workflow_id_workflows_id_fk": {
+ "name": "workflow_runs_workflow_id_workflows_id_fk",
+ "tableFrom": "workflow_runs",
+ "tableTo": "workflows",
+ "columnsFrom": [
+ "workflow_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "workflow_runs_machine_id_machines_id_fk": {
+ "name": "workflow_runs_machine_id_machines_id_fk",
+ "tableFrom": "workflow_runs",
+ "tableTo": "machines",
+ "columnsFrom": [
+ "machine_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "workflows": {
+ "name": "workflows",
+ "schema": "comfy_deploy",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "workflows_user_id_users_id_fk": {
+ "name": "workflows_user_id_users_id_fk",
+ "tableFrom": "workflows",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "workflow_versions": {
+ "name": "workflow_versions",
+ "schema": "comfy_deploy",
+ "columns": {
+ "workflow_id": {
+ "name": "workflow_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "workflow": {
+ "name": "workflow",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "workflow_api": {
+ "name": "workflow_api",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "version": {
+ "name": "version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "workflow_versions_workflow_id_workflows_id_fk": {
+ "name": "workflow_versions_workflow_id_workflows_id_fk",
+ "tableFrom": "workflow_versions",
+ "tableTo": "workflows",
+ "columnsFrom": [
+ "workflow_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ }
+ },
+ "enums": {
+ "deployment_environment": {
+ "name": "deployment_environment",
+ "values": {
+ "staging": "staging",
+ "production": "production"
+ }
+ },
+ "workflow_run_status": {
+ "name": "workflow_run_status",
+ "values": {
+ "not-started": "not-started",
+ "running": "running",
+ "success": "success",
+ "failed": "failed"
+ }
+ }
+ },
+ "schemas": {
+ "comfy_deploy": "comfy_deploy"
+ },
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ }
+}
\ No newline at end of file
diff --git a/web/drizzle/meta/0006_snapshot.json b/web/drizzle/meta/0006_snapshot.json
new file mode 100644
index 0000000..e93fdf1
--- /dev/null
+++ b/web/drizzle/meta/0006_snapshot.json
@@ -0,0 +1,531 @@
+{
+ "id": "c68b3727-5154-41eb-b44e-d5f26b72be44",
+ "prevId": "af90a047-fec9-4d35-be52-82f8e0ee1cf4",
+ "version": "5",
+ "dialect": "pg",
+ "tables": {
+ "deployments": {
+ "name": "deployments",
+ "schema": "comfy_deploy",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "workflow_version_id": {
+ "name": "workflow_version_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "workflow_id": {
+ "name": "workflow_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "machine_id": {
+ "name": "machine_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "environment": {
+ "name": "environment",
+ "type": "deployment_environment",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "deployments_user_id_users_id_fk": {
+ "name": "deployments_user_id_users_id_fk",
+ "tableFrom": "deployments",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "deployments_workflow_version_id_workflow_versions_id_fk": {
+ "name": "deployments_workflow_version_id_workflow_versions_id_fk",
+ "tableFrom": "deployments",
+ "tableTo": "workflow_versions",
+ "columnsFrom": [
+ "workflow_version_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "deployments_workflow_id_workflows_id_fk": {
+ "name": "deployments_workflow_id_workflows_id_fk",
+ "tableFrom": "deployments",
+ "tableTo": "workflows",
+ "columnsFrom": [
+ "workflow_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "deployments_machine_id_machines_id_fk": {
+ "name": "deployments_machine_id_machines_id_fk",
+ "tableFrom": "deployments",
+ "tableTo": "machines",
+ "columnsFrom": [
+ "machine_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "machines": {
+ "name": "machines",
+ "schema": "comfy_deploy",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "endpoint": {
+ "name": "endpoint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "machines_user_id_users_id_fk": {
+ "name": "machines_user_id_users_id_fk",
+ "tableFrom": "machines",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "users": {
+ "name": "users",
+ "schema": "comfy_deploy",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "workflow_run_outputs": {
+ "name": "workflow_run_outputs",
+ "schema": "comfy_deploy",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "run_id": {
+ "name": "run_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "data": {
+ "name": "data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "workflow_run_outputs_run_id_workflow_runs_id_fk": {
+ "name": "workflow_run_outputs_run_id_workflow_runs_id_fk",
+ "tableFrom": "workflow_run_outputs",
+ "tableTo": "workflow_runs",
+ "columnsFrom": [
+ "run_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "workflow_runs": {
+ "name": "workflow_runs",
+ "schema": "comfy_deploy",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "workflow_version_id": {
+ "name": "workflow_version_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "workflow_id": {
+ "name": "workflow_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "machine_id": {
+ "name": "machine_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "workflow_run_status",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'not-started'"
+ },
+ "ended_at": {
+ "name": "ended_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "workflow_runs_workflow_version_id_workflow_versions_id_fk": {
+ "name": "workflow_runs_workflow_version_id_workflow_versions_id_fk",
+ "tableFrom": "workflow_runs",
+ "tableTo": "workflow_versions",
+ "columnsFrom": [
+ "workflow_version_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "workflow_runs_workflow_id_workflows_id_fk": {
+ "name": "workflow_runs_workflow_id_workflows_id_fk",
+ "tableFrom": "workflow_runs",
+ "tableTo": "workflows",
+ "columnsFrom": [
+ "workflow_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "workflow_runs_machine_id_machines_id_fk": {
+ "name": "workflow_runs_machine_id_machines_id_fk",
+ "tableFrom": "workflow_runs",
+ "tableTo": "machines",
+ "columnsFrom": [
+ "machine_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "workflows": {
+ "name": "workflows",
+ "schema": "comfy_deploy",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "workflows_user_id_users_id_fk": {
+ "name": "workflows_user_id_users_id_fk",
+ "tableFrom": "workflows",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "workflow_versions": {
+ "name": "workflow_versions",
+ "schema": "comfy_deploy",
+ "columns": {
+ "workflow_id": {
+ "name": "workflow_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "workflow": {
+ "name": "workflow",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "workflow_api": {
+ "name": "workflow_api",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "version": {
+ "name": "version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "workflow_versions_workflow_id_workflows_id_fk": {
+ "name": "workflow_versions_workflow_id_workflows_id_fk",
+ "tableFrom": "workflow_versions",
+ "tableTo": "workflows",
+ "columnsFrom": [
+ "workflow_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ }
+ },
+ "enums": {
+ "deployment_environment": {
+ "name": "deployment_environment",
+ "values": {
+ "staging": "staging",
+ "production": "production"
+ }
+ },
+ "workflow_run_status": {
+ "name": "workflow_run_status",
+ "values": {
+ "not-started": "not-started",
+ "running": "running",
+ "success": "success",
+ "failed": "failed"
+ }
+ }
+ },
+ "schemas": {
+ "comfy_deploy": "comfy_deploy"
+ },
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ }
+}
\ No newline at end of file
diff --git a/web/drizzle/meta/_journal.json b/web/drizzle/meta/_journal.json
index c5c9eea..c11df69 100644
--- a/web/drizzle/meta/_journal.json
+++ b/web/drizzle/meta/_journal.json
@@ -36,6 +36,20 @@
"when": 1702357291227,
"tag": "0004_zippy_freak",
"breakpoints": true
+ },
+ {
+ "idx": 5,
+ "version": "5",
+ "when": 1702545286004,
+ "tag": "0005_worthless_dakota_north",
+ "breakpoints": true
+ },
+ {
+ "idx": 6,
+ "version": "5",
+ "when": 1702556119574,
+ "tag": "0006_chief_mariko_yashida",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/web/package.json b/web/package.json
index 0d25ba4..84eb503 100644
--- a/web/package.json
+++ b/web/package.json
@@ -39,6 +39,7 @@
"react-dom": "^18",
"react-hook-form": "^7.48.2",
"react-use-websocket": "^4.5.0",
+ "shiki": "^0.14.6",
"sonner": "^1.2.4",
"tailwind-merge": "^2.1.0",
"tailwindcss-animate": "^1.0.7",
diff --git a/web/src/app/[workflow_id]/page.tsx b/web/src/app/[workflow_id]/page.tsx
index 2e651ed..e54ef39 100644
--- a/web/src/app/[workflow_id]/page.tsx
+++ b/web/src/app/[workflow_id]/page.tsx
@@ -1,7 +1,8 @@
-import { RunsTable } from "../../components/RunsTable";
+import { DeploymentsTable, RunsTable } from "../../components/RunsTable";
import { findFirstTableWithVersion } from "../../server/findFirstTableWithVersion";
import { MachinesWSMain } from "@/components/MachinesWS";
import {
+ CreateDeploymentButton,
MachineSelect,
RunWorkflowButton,
VersionSelect,
@@ -28,24 +29,36 @@ export default async function Page({
return (
-
-
- {workflow?.name}
-
- {getRelativeTime(workflow?.updated_at)}
-
-
+
+
+
+ {workflow?.name}
+
+ {getRelativeTime(workflow?.updated_at)}
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+ Deployments
+
+
+
+
+
+
+
diff --git a/web/src/app/api/create-run/route.ts b/web/src/app/api/create-run/route.ts
deleted file mode 100644
index 5e62f60..0000000
--- a/web/src/app/api/create-run/route.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { parseDataSafe } from "../../../lib/parseDataSafe";
-import { createRun } from "../../../server/createRun";
-import { NextResponse } from "next/server";
-import { z } from "zod";
-
-const Request = z.object({
- workflow_version_id: z.string(),
- // workflow_version: z.number().optional(),
- machine_id: z.string(),
-});
-
-export async function POST(request: Request) {
- const [data, error] = await parseDataSafe(Request, request);
- if (!data || error) return error;
-
- const origin = new URL(request.url).origin;
-
- const { workflow_version_id, machine_id } = data;
-
- try {
- const workflow_run_id = await createRun(
- origin,
- workflow_version_id,
- machine_id
- );
-
- return NextResponse.json(
- {
- workflow_run_id: workflow_run_id,
- },
- {
- status: 200,
- }
- );
- } catch (error: any) {
- return NextResponse.json(
- {
- error: error.message,
- },
- {
- status: 500,
- }
- );
- }
-}
diff --git a/web/src/app/api/run/route.ts b/web/src/app/api/run/route.ts
new file mode 100644
index 0000000..2aa4cdd
--- /dev/null
+++ b/web/src/app/api/run/route.ts
@@ -0,0 +1,84 @@
+import { parseDataSafe } from "../../../lib/parseDataSafe";
+import { createRun } from "../../../server/createRun";
+import { db } from "@/db/db";
+import { deploymentsTable } from "@/db/schema";
+import { getRunsData } from "@/server/getRunsOutput";
+import { replaceCDNUrl } from "@/server/resource";
+import { eq } from "drizzle-orm";
+import { NextResponse } from "next/server";
+import { z } from "zod";
+
+const Request = z.object({
+ deployment_id: z.string(),
+});
+
+const Request2 = z.object({
+ run_id: z.string(),
+});
+
+export async function GET(request: Request) {
+ const [data, error] = await parseDataSafe(Request2, request);
+ if (!data || error) return error;
+
+ const run = await getRunsData(data.run_id);
+
+ if (run?.status === "success" && run?.outputs?.length > 0) {
+ for (let i = 0; i < run.outputs.length; i++) {
+ const output = run.outputs[i];
+
+ if (output.data?.images === undefined) continue;
+
+ for (let j = 0; j < output.data?.images.length; j++) {
+ const element = output.data?.images[j];
+ element.url = replaceCDNUrl(
+ `${process.env.SPACES_ENDPOINT}/comfyui-deploy/outputs/runs/${run.id}/${element.filename}`
+ );
+ }
+ }
+ }
+
+ return NextResponse.json(run, {
+ status: 200,
+ });
+}
+
+export async function POST(request: Request) {
+ const [data, error] = await parseDataSafe(Request, request);
+ if (!data || error) return error;
+
+ const origin = new URL(request.url).origin;
+
+ const { deployment_id } = data;
+
+ try {
+ const deploymentData = await db.query.deploymentsTable.findFirst({
+ where: eq(deploymentsTable.id, deployment_id),
+ });
+
+ if (!deploymentData) throw new Error("Deployment not found");
+
+ const run_id = await createRun(
+ origin,
+ deploymentData.workflow_version_id,
+ deploymentData.machine_id
+ );
+
+ return NextResponse.json(
+ {
+ run_id: run_id,
+ },
+ {
+ status: 200,
+ }
+ );
+ } catch (error: any) {
+ return NextResponse.json(
+ {
+ error: error.message,
+ },
+ {
+ status: 500,
+ }
+ );
+ }
+}
diff --git a/web/src/app/globals.css b/web/src/app/globals.css
index 6a75725..d3f4ab8 100644
--- a/web/src/app/globals.css
+++ b/web/src/app/globals.css
@@ -65,6 +65,18 @@
--ring: 212.7 26.8% 83.9%;
}
}
+
+.shiki>code>span {
+ /* text-wrap: wrap; */
+ /* word-wrap: break-word; */
+ /* @apply break-all ; */
+ text-wrap: wrap;
+}
+
+.shiki {
+ /* @apply rounded-lg p-2 overflow-x-scroll */
+ @apply rounded-lg p-2 overflow-hidden
+}
@layer base {
* {
diff --git a/web/src/components/CodeBlock.tsx b/web/src/components/CodeBlock.tsx
new file mode 100644
index 0000000..9989f76
--- /dev/null
+++ b/web/src/components/CodeBlock.tsx
@@ -0,0 +1,30 @@
+import { CopyButton } from "@/components/CopyButton";
+import type { Lang } from "shiki";
+import shiki from "shiki";
+
+export async function CodeBlock(props: { code: string; lang: Lang }) {
+ const highlighter = await shiki.getHighlighter({
+ theme: "one-dark-pro",
+ });
+
+ return (
+
+ {/* max-w-[calc(32rem-1.5rem-1.5rem)] */}
+ {/*
*/}
+
+
+ );
+}
diff --git a/web/src/components/CopyButton.tsx b/web/src/components/CopyButton.tsx
new file mode 100644
index 0000000..ca0ea8c
--- /dev/null
+++ b/web/src/components/CopyButton.tsx
@@ -0,0 +1,24 @@
+"use client";
+
+import { Button } from "@/components/ui/button";
+import { cn } from "@/lib/utils";
+import { Copy } from "lucide-react";
+
+export function CopyButton({
+ className,
+ ...props
+}: {
+ text: string;
+ className?: string;
+}) {
+ return (
+
+ );
+}
diff --git a/web/src/components/DeploymentDisplay.tsx b/web/src/components/DeploymentDisplay.tsx
new file mode 100644
index 0000000..0866099
--- /dev/null
+++ b/web/src/components/DeploymentDisplay.tsx
@@ -0,0 +1,113 @@
+import { CodeBlock } from "@/components/CodeBlock";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog";
+import { TableCell, TableRow } from "@/components/ui/table";
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
+import { getRelativeTime } from "@/lib/getRelativeTime";
+import type { findAllDeployments } from "@/server/findAllRuns";
+
+const curlTemplate = `
+curl --request POST \
+ --url \
+ --header 'Content-Type: application/json' \
+ --data '{
+ "deployment_id": ""
+}'
+`;
+
+const jsTemplate = `
+const options = {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: '{"deployment_id":""}'
+};
+
+fetch('', options)
+ .then(response => response.json())
+ .then(response => console.log(response))
+ .catch(err => console.error(err));
+`;
+
+const jsTemplate_checkStatus = `
+const options = {
+ method: 'GET',
+ headers: {'Content-Type': 'application/json'},
+};
+
+const run_id = '';
+
+fetch('?run_id=' + run_id, options)
+ .then(response => response.json())
+ .then(response => console.log(response))
+ .catch(err => console.error(err));
+`;
+
+export function DeploymentDisplay({
+ deployment,
+}: {
+ deployment: Awaited>[0];
+}) {
+ return (
+
+ );
+}
+
+function formatCode(
+ codeTemplate: string,
+ deployment: Awaited>[0]
+) {
+ return codeTemplate
+ .replace(
+ "",
+ `${process.env.VERCEL_URL ?? "http://localhost:3000"}/api/run`
+ )
+ .replace("", deployment.id);
+}
diff --git a/web/src/components/MachineList.tsx b/web/src/components/MachineList.tsx
index 858becd..9d6f2fb 100644
--- a/web/src/components/MachineList.tsx
+++ b/web/src/components/MachineList.tsx
@@ -161,7 +161,7 @@ export const columns: ColumnDef[] = [
{
- callServerWithToast(await deleteMachine(workflow.id));
+ callServerPromise(deleteMachine(workflow.id));
}}
>
Delete Machine
@@ -176,15 +176,16 @@ export const columns: ColumnDef[] = [
},
];
-async function callServerWithToast(result: {
- message: string;
- error?: boolean;
-}) {
- if (result.error) {
- toast.error(result.message);
- } else {
- toast.success(result.message);
- }
+export async function callServerPromise(result: Promise) {
+ return result
+ .then((x) => {
+ if ((x as { message: string })?.message !== undefined) {
+ toast.success((x as { message: string }).message);
+ }
+ })
+ .catch((error) => {
+ toast.error(error.message);
+ });
}
export function MachineList({ data }: { data: Machine[] }) {
diff --git a/web/src/components/RunDisplay.tsx b/web/src/components/RunDisplay.tsx
index ce25594..a8ad2b3 100644
--- a/web/src/components/RunDisplay.tsx
+++ b/web/src/components/RunDisplay.tsx
@@ -1,5 +1,6 @@
"use client";
+import { RunOutputs } from "./RunOutputs";
import { useStore } from "@/components/MachinesWS";
import { StatusBadge } from "@/components/StatusBadge";
import {
@@ -10,18 +11,9 @@ import {
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
-import {
- Table,
- TableBody,
- TableCell,
- TableHead,
- TableHeader,
- TableRow,
-} from "@/components/ui/table";
+import { TableCell, TableRow } from "@/components/ui/table";
import { getRelativeTime } from "@/lib/getRelativeTime";
import { type findAllRuns } from "@/server/findAllRuns";
-import { getRunsOutput } from "@/server/getRunsOutput";
-import { useEffect, useState } from "react";
export function RunDisplay({
run,
@@ -73,42 +65,6 @@ export function RunDisplay({
);
}
-export function RunOutputs({ run_id }: { run_id: string }) {
- const [outputs, setOutputs] = useState<
- Awaited>
- >([]);
-
- useEffect(() => {
- getRunsOutput(run_id).then((x) => setOutputs(x));
- }, [run_id]);
-
- return (
-
- {/* A list of your recent runs. */}
-
-
- File
- Output
-
-
-
- {outputs?.map((run) => {
- const fileName = run.data.images[0].filename;
- // const filePath
- return (
-
- {fileName}
-
-
-
-
- );
- })}
-
-
- );
-}
-
export function OutputRender(props: { run_id: string; filename: string }) {
if (props.filename.endsWith(".png")) {
return (
diff --git a/web/src/components/RunOutputs.tsx b/web/src/components/RunOutputs.tsx
new file mode 100644
index 0000000..f957f33
--- /dev/null
+++ b/web/src/components/RunOutputs.tsx
@@ -0,0 +1,53 @@
+"use client";
+
+import { OutputRender } from "./RunDisplay";
+import { callServerPromise } from "@/components/MachineList";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table";
+import { getRunsOutput } from "@/server/getRunsOutput";
+import { useEffect, useState } from "react";
+
+export function RunOutputs({ run_id }: { run_id: string }) {
+ const [outputs, setOutputs] =
+ useState>>();
+
+ useEffect(() => {
+ if (!run_id) return;
+ // fetch(`/api/run?run_id=${run_id}`)
+ // .then((x) => x.json())
+ // .then((x) => setOutputs(x));
+ callServerPromise(getRunsOutput(run_id).then((x) => setOutputs(x)));
+ }, [run_id, outputs]);
+
+ return (
+
+ {/* A list of your recent runs. */}
+
+
+ File
+ Output
+
+
+
+ {outputs?.map((run) => {
+ const fileName = run.data.images[0].filename;
+ // const filePath
+ return (
+
+ {fileName}
+
+
+
+
+ );
+ })}
+
+
+ );
+}
diff --git a/web/src/components/RunsTable.tsx b/web/src/components/RunsTable.tsx
index f96a72b..fd0effd 100644
--- a/web/src/components/RunsTable.tsx
+++ b/web/src/components/RunsTable.tsx
@@ -1,4 +1,5 @@
-import { findAllRuns } from "../server/findAllRuns";
+import { findAllDeployments, findAllRuns } from "../server/findAllRuns";
+import { DeploymentDisplay } from "./DeploymentDisplay";
import { RunDisplay } from "./RunDisplay";
import {
Table,
@@ -33,3 +34,27 @@ export async function RunsTable(props: { workflow_id: string }) {
);
}
+
+export async function DeploymentsTable(props: { workflow_id: string }) {
+ const allRuns = await findAllDeployments(props.workflow_id);
+ return (
+
+
+ A list of your deployments
+
+
+ Environment
+ Version
+ Machine
+ Updated At
+
+
+
+ {allRuns.map((run) => (
+
+ ))}
+
+
+
+ );
+}
diff --git a/web/src/components/VersionSelect.tsx b/web/src/components/VersionSelect.tsx
index c02b05f..9e45c0e 100644
--- a/web/src/components/VersionSelect.tsx
+++ b/web/src/components/VersionSelect.tsx
@@ -1,7 +1,14 @@
"use client";
import { LoadingIcon } from "@/components/LoadingIcon";
+import { callServerPromise } from "@/components/MachineList";
import { Button } from "@/components/ui/button";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
import {
Select,
SelectContent,
@@ -12,9 +19,10 @@ import {
SelectValue,
} from "@/components/ui/select";
import { createRun } from "@/server/createRun";
+import { createDeployments } from "@/server/curdDeploments";
import type { getMachines } from "@/server/curdMachine";
import type { findFirstTableWithVersion } from "@/server/findFirstTableWithVersion";
-import { Play } from "lucide-react";
+import { MoreVertical, Play } from "lucide-react";
import { parseAsInteger, useQueryState } from "next-usequerystate";
import { useState } from "react";
@@ -122,3 +130,70 @@ export function RunWorkflowButton({
);
}
+
+export function CreateDeploymentButton({
+ workflow,
+ machines,
+}: {
+ workflow: Awaited>;
+ machines: Awaited>;
+}) {
+ const [version] = useQueryState("version", {
+ defaultValue: workflow?.versions[0].version ?? 1,
+ ...parseAsInteger,
+ });
+ const [machine] = useQueryState("machine", {
+ defaultValue: machines[0].id ?? "",
+ });
+ const [isLoading, setIsLoading] = useState(false);
+ const workflow_version_id = workflow?.versions.find(
+ (x) => x.version === version
+ )?.id;
+ return (
+
+
+
+
+
+ {
+ if (!workflow_version_id) return;
+
+ setIsLoading(true);
+ await callServerPromise(
+ createDeployments(
+ workflow.id,
+ workflow_version_id,
+ machine,
+ "production"
+ )
+ );
+ setIsLoading(false);
+ }}
+ >
+ Production
+
+ {
+ if (!workflow_version_id) return;
+
+ setIsLoading(true);
+ await callServerPromise(
+ createDeployments(
+ workflow.id,
+ workflow_version_id,
+ machine,
+ "staging"
+ )
+ );
+ setIsLoading(false);
+ }}
+ >
+ Staging
+
+
+
+ );
+}
diff --git a/web/src/components/ui/card.tsx b/web/src/components/ui/card.tsx
index afa13ec..1460271 100644
--- a/web/src/components/ui/card.tsx
+++ b/web/src/components/ui/card.tsx
@@ -1,6 +1,5 @@
-import * as React from "react"
-
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
+import * as React from "react";
const Card = React.forwardRef<
HTMLDivElement,
@@ -14,8 +13,8 @@ const Card = React.forwardRef<
)}
{...props}
/>
-))
-Card.displayName = "Card"
+));
+Card.displayName = "Card";
const CardHeader = React.forwardRef<
HTMLDivElement,
@@ -26,8 +25,8 @@ const CardHeader = React.forwardRef<
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
-))
-CardHeader.displayName = "CardHeader"
+));
+CardHeader.displayName = "CardHeader";
const CardTitle = React.forwardRef<
HTMLParagraphElement,
@@ -41,8 +40,8 @@ const CardTitle = React.forwardRef<
)}
{...props}
/>
-))
-CardTitle.displayName = "CardTitle"
+));
+CardTitle.displayName = "CardTitle";
const CardDescription = React.forwardRef<
HTMLParagraphElement,
@@ -53,16 +52,16 @@ const CardDescription = React.forwardRef<
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
-))
-CardDescription.displayName = "CardDescription"
+));
+CardDescription.displayName = "CardDescription";
const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes
>(({ className, ...props }, ref) => (
-))
-CardContent.displayName = "CardContent"
+));
+CardContent.displayName = "CardContent";
const CardFooter = React.forwardRef<
HTMLDivElement,
@@ -73,7 +72,14 @@ const CardFooter = React.forwardRef<
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
-))
-CardFooter.displayName = "CardFooter"
+));
+CardFooter.displayName = "CardFooter";
-export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
+export {
+ Card,
+ CardHeader,
+ CardFooter,
+ CardTitle,
+ CardDescription,
+ CardContent,
+};
diff --git a/web/src/components/ui/dialog.tsx b/web/src/components/ui/dialog.tsx
index cad6f58..78ca5d8 100644
--- a/web/src/components/ui/dialog.tsx
+++ b/web/src/components/ui/dialog.tsx
@@ -1,18 +1,17 @@
-"use client"
+"use client";
-import * as React from "react"
-import * as DialogPrimitive from "@radix-ui/react-dialog"
-import { X } from "lucide-react"
+import { cn } from "@/lib/utils";
+import * as DialogPrimitive from "@radix-ui/react-dialog";
+import { X } from "lucide-react";
+import * as React from "react";
-import { cn } from "@/lib/utils"
+const Dialog = DialogPrimitive.Root;
-const Dialog = DialogPrimitive.Root
+const DialogTrigger = DialogPrimitive.Trigger;
-const DialogTrigger = DialogPrimitive.Trigger
+const DialogPortal = DialogPrimitive.Portal;
-const DialogPortal = DialogPrimitive.Portal
-
-const DialogClose = DialogPrimitive.Close
+const DialogClose = DialogPrimitive.Close;
const DialogOverlay = React.forwardRef<
React.ElementRef,
@@ -26,8 +25,8 @@ const DialogOverlay = React.forwardRef<
)}
{...props}
/>
-))
-DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
+));
+DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
const DialogContent = React.forwardRef<
React.ElementRef,
@@ -37,8 +36,9 @@ const DialogContent = React.forwardRef<
-))
-DialogContent.displayName = DialogPrimitive.Content.displayName
+));
+DialogContent.displayName = DialogPrimitive.Content.displayName;
const DialogHeader = ({
className,
@@ -64,8 +64,8 @@ const DialogHeader = ({
)}
{...props}
/>
-)
-DialogHeader.displayName = "DialogHeader"
+);
+DialogHeader.displayName = "DialogHeader";
const DialogFooter = ({
className,
@@ -78,8 +78,8 @@ const DialogFooter = ({
)}
{...props}
/>
-)
-DialogFooter.displayName = "DialogFooter"
+);
+DialogFooter.displayName = "DialogFooter";
const DialogTitle = React.forwardRef<
React.ElementRef,
@@ -93,8 +93,8 @@ const DialogTitle = React.forwardRef<
)}
{...props}
/>
-))
-DialogTitle.displayName = DialogPrimitive.Title.displayName
+));
+DialogTitle.displayName = DialogPrimitive.Title.displayName;
const DialogDescription = React.forwardRef<
React.ElementRef,
@@ -105,8 +105,8 @@ const DialogDescription = React.forwardRef<
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
-))
-DialogDescription.displayName = DialogPrimitive.Description.displayName
+));
+DialogDescription.displayName = DialogPrimitive.Description.displayName;
export {
Dialog,
@@ -119,4 +119,4 @@ export {
DialogFooter,
DialogTitle,
DialogDescription,
-}
+};
diff --git a/web/src/db/schema.ts b/web/src/db/schema.ts
index e920396..62f80d2 100644
--- a/web/src/db/schema.ts
+++ b/web/src/db/schema.ts
@@ -67,6 +67,11 @@ export const workflowRunStatus = pgEnum("workflow_run_status", [
"failed",
]);
+export const deploymentEnvironment = pgEnum("deployment_environment", [
+ "staging",
+ "production",
+]);
+
// We still want to keep the workflow run record.
export const workflowRunsTable = dbSchema.table("workflow_runs", {
id: uuid("id").primaryKey().defaultRandom().notNull(),
@@ -91,16 +96,20 @@ export const workflowRunsTable = dbSchema.table("workflow_runs", {
created_at: timestamp("created_at").defaultNow().notNull(),
});
-export const workflowRunRelations = relations(workflowRunsTable, ({ one }) => ({
- machine: one(machinesTable, {
- fields: [workflowRunsTable.machine_id],
- references: [machinesTable.id],
- }),
- version: one(workflowVersionTable, {
- fields: [workflowRunsTable.workflow_version_id],
- references: [workflowVersionTable.id],
- }),
-}));
+export const workflowRunRelations = relations(
+ workflowRunsTable,
+ ({ one, many }) => ({
+ machine: one(machinesTable, {
+ fields: [workflowRunsTable.machine_id],
+ references: [machinesTable.id],
+ }),
+ version: one(workflowVersionTable, {
+ fields: [workflowRunsTable.workflow_version_id],
+ references: [workflowVersionTable.id],
+ }),
+ outputs: many(workflowRunOutputs),
+ })
+);
// We still want to keep the workflow run record.
export const workflowRunOutputs = dbSchema.table("workflow_run_outputs", {
@@ -116,6 +125,16 @@ export const workflowRunOutputs = dbSchema.table("workflow_run_outputs", {
updated_at: timestamp("updated_at").defaultNow().notNull(),
});
+export const workflowOutputRelations = relations(
+ workflowRunOutputs,
+ ({ one }) => ({
+ run: one(workflowRunsTable, {
+ fields: [workflowRunOutputs.run_id],
+ references: [workflowRunsTable.id],
+ }),
+ })
+);
+
// when user delete, also delete all the workflow versions
export const machinesTable = dbSchema.table("machines", {
id: uuid("id").primaryKey().defaultRandom().notNull(),
@@ -130,5 +149,37 @@ export const machinesTable = dbSchema.table("machines", {
updated_at: timestamp("updated_at").defaultNow().notNull(),
});
+export const deploymentsTable = dbSchema.table("deployments", {
+ id: uuid("id").primaryKey().defaultRandom().notNull(),
+ user_id: text("user_id")
+ .references(() => usersTable.id, {
+ onDelete: "cascade",
+ })
+ .notNull(),
+ workflow_version_id: uuid("workflow_version_id")
+ .notNull()
+ .references(() => workflowVersionTable.id),
+ workflow_id: uuid("workflow_id")
+ .notNull()
+ .references(() => workflowTable.id),
+ machine_id: uuid("machine_id")
+ .notNull()
+ .references(() => machinesTable.id),
+ environment: deploymentEnvironment("environment").notNull(),
+ created_at: timestamp("created_at").defaultNow().notNull(),
+ updated_at: timestamp("updated_at").defaultNow().notNull(),
+});
+
+export const deploymentsRelations = relations(deploymentsTable, ({ one }) => ({
+ machine: one(machinesTable, {
+ fields: [deploymentsTable.machine_id],
+ references: [machinesTable.id],
+ }),
+ version: one(workflowVersionTable, {
+ fields: [deploymentsTable.workflow_version_id],
+ references: [workflowVersionTable.id],
+ }),
+}));
+
export type UserType = InferSelectModel;
export type WorkflowType = InferSelectModel;
diff --git a/web/src/middleware.ts b/web/src/middleware.ts
index cfc83a6..f7375b8 100644
--- a/web/src/middleware.ts
+++ b/web/src/middleware.ts
@@ -1,13 +1,10 @@
-import { db } from "./db/db";
-import { usersTable } from "./db/schema";
-import { authMiddleware, redirectToSignIn, clerkClient } from "@clerk/nextjs";
-import { eq } from "drizzle-orm";
-import { NextResponse } from "next/server";
+import { authMiddleware, redirectToSignIn } from "@clerk/nextjs";
// This example protects all routes including api/trpc routes
// Please edit this to allow other routes to be public as needed.
// See https://clerk.com/docs/references/nextjs/auth-middleware for more information about configuring your Middleware
export default authMiddleware({
+ // debug: true,
publicRoutes: ["/api/(.*)"],
// publicRoutes: ["/", "/(.*)"],
async afterAuth(auth, req, evt) {
@@ -33,6 +30,6 @@ export default authMiddleware({
});
export const config = {
- matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/" , "/(api|trpc)(.*)"],
+ matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
// matcher: ['/','/create', '/api/(twitter|generation|init|voice-cloning)'],
};
diff --git a/web/src/server/curdDeploments.ts b/web/src/server/curdDeploments.ts
new file mode 100644
index 0000000..03742c0
--- /dev/null
+++ b/web/src/server/curdDeploments.ts
@@ -0,0 +1,46 @@
+"use server";
+
+import { db } from "@/db/db";
+import { deploymentsTable } from "@/db/schema";
+import { auth } from "@clerk/nextjs";
+import { and, eq } from "drizzle-orm";
+import { revalidatePath } from "next/cache";
+import "server-only";
+
+export async function createDeployments(
+ workflow_id: string,
+ version_id: string,
+ machine_id: string,
+ environment: "production" | "staging"
+) {
+ const { userId } = auth();
+ if (!userId) throw new Error("No user id");
+
+ // Same environment and same workflow
+ const existingDeployment = await db.query.deploymentsTable.findFirst({
+ where: and(
+ eq(deploymentsTable.workflow_id, workflow_id),
+ eq(deploymentsTable.environment, environment)
+ ),
+ });
+
+ if (existingDeployment) {
+ await db
+ .update(deploymentsTable)
+ .set({
+ workflow_id,
+ workflow_version_id: version_id,
+ machine_id,
+ })
+ .where(eq(deploymentsTable.id, existingDeployment.id));
+ } else {
+ await db.insert(deploymentsTable).values({
+ user_id: userId,
+ workflow_id,
+ workflow_version_id: version_id,
+ machine_id,
+ environment,
+ });
+ }
+ revalidatePath(`/${workflow_id}`);
+}
diff --git a/web/src/server/curdMachine.ts b/web/src/server/curdMachine.ts
index 571f152..03f9dae 100644
--- a/web/src/server/curdMachine.ts
+++ b/web/src/server/curdMachine.ts
@@ -43,14 +43,7 @@ export async function addMachine(name: string, endpoint: string) {
export async function deleteMachine(
machine_id: string
): Promise<{ message: string; error?: boolean }> {
- try {
- await db.delete(machinesTable).where(eq(machinesTable.id, machine_id));
- revalidatePath("/machines");
- return { message: "Machine Deleted" };
- } catch (error: unknown) {
- return {
- message: `Error: ${error.detail}`,
- error: true,
- };
- }
+ await db.delete(machinesTable).where(eq(machinesTable.id, machine_id));
+ revalidatePath("/machines");
+ return { message: "Machine Deleted" };
}
diff --git a/web/src/server/findAllRuns.tsx b/web/src/server/findAllRuns.tsx
index 49a10c4..af2da80 100644
--- a/web/src/server/findAllRuns.tsx
+++ b/web/src/server/findAllRuns.tsx
@@ -1,5 +1,5 @@
import { db } from "@/db/db";
-import { workflowRunsTable } from "@/db/schema";
+import { deploymentsTable, workflowRunsTable } from "@/db/schema";
import { desc, eq } from "drizzle-orm";
export async function findAllRuns(workflow_id: string) {
@@ -21,3 +21,22 @@ export async function findAllRuns(workflow_id: string) {
},
});
}
+
+export async function findAllDeployments(workflow_id: string) {
+ return await db.query.deploymentsTable.findMany({
+ where: eq(deploymentsTable.workflow_id, workflow_id),
+ orderBy: desc(deploymentsTable.environment),
+ with: {
+ machine: {
+ columns: {
+ name: true,
+ },
+ },
+ version: {
+ columns: {
+ version: true,
+ },
+ },
+ },
+ });
+}
diff --git a/web/src/server/getRunsOutput.tsx b/web/src/server/getRunsOutput.tsx
index 44bbae4..79d0210 100644
--- a/web/src/server/getRunsOutput.tsx
+++ b/web/src/server/getRunsOutput.tsx
@@ -1,12 +1,27 @@
"use server";
import { db } from "@/db/db";
-import { workflowRunOutputs } from "@/db/schema";
+import { workflowRunOutputs, workflowRunsTable } from "@/db/schema";
import { eq } from "drizzle-orm";
export async function getRunsOutput(run_id: string) {
+ // throw new Error("Not implemented");
return await db
.select()
.from(workflowRunOutputs)
.where(eq(workflowRunOutputs.run_id, run_id));
}
+
+export async function getRunsData(run_id: string) {
+ // throw new Error("Not implemented");
+ return await db.query.workflowRunsTable.findFirst({
+ where: eq(workflowRunsTable.id, run_id),
+ with: {
+ outputs: {
+ columns: {
+ data: true,
+ },
+ },
+ },
+ });
+}
diff --git a/web/src/server/resource.ts b/web/src/server/resource.ts
index 44b1d3e..fe194b8 100644
--- a/web/src/server/resource.ts
+++ b/web/src/server/resource.ts
@@ -17,7 +17,7 @@ const s3Client = new S3({
forcePathStyle: true,
});
-function replaceCDNUrl(url: string) {
+export function replaceCDNUrl(url: string) {
url = url.replace(
process.env.SPACES_ENDPOINT!,
process.env.SPACES_ENDPOINT_CDN!