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 ( + + + + {deployment.environment} + + {deployment.version?.version} + + + {deployment.machine?.name} + + + {getRelativeTime(deployment.updated_at)} + + + + + + + {deployment.environment} Deployment + + Code for your deployment client + + + + js + curl + + + + + + + + + + + + ); +} + +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!