diff --git a/web/bun.lockb b/web/bun.lockb index 6f53ecc..cfc30ac 100755 Binary files a/web/bun.lockb and b/web/bun.lockb differ diff --git a/web/drizzle/0029_large_frightful_four.sql b/web/drizzle/0029_large_frightful_four.sql new file mode 100644 index 0000000..d5f61d7 --- /dev/null +++ b/web/drizzle/0029_large_frightful_four.sql @@ -0,0 +1,2 @@ +ALTER TABLE "comfyui_deploy"."deployments" ADD COLUMN "description" text;--> statement-breakpoint +ALTER TABLE "comfyui_deploy"."deployments" ADD COLUMN "showcase_media" jsonb; \ No newline at end of file diff --git a/web/drizzle/0030_kind_doorman.sql b/web/drizzle/0030_kind_doorman.sql new file mode 100644 index 0000000..a2a7a8d --- /dev/null +++ b/web/drizzle/0030_kind_doorman.sql @@ -0,0 +1 @@ +ALTER TABLE "comfyui_deploy"."deployments" ADD COLUMN "org_id" text; \ No newline at end of file diff --git a/web/drizzle/meta/0029_snapshot.json b/web/drizzle/meta/0029_snapshot.json new file mode 100644 index 0000000..0f8b479 --- /dev/null +++ b/web/drizzle/meta/0029_snapshot.json @@ -0,0 +1,750 @@ +{ + "id": "a7d6a8dd-0e15-4165-98a2-de2a334455dc", + "prevId": "7bdeb193-ee27-40cc-8252-59ddaf505ab8", + "version": "5", + "dialect": "pg", + "tables": { + "api_keys": { + "name": "api_keys", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revoked": { + "name": "revoked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "api_keys_user_id_users_id_fk": { + "name": "api_keys_user_id_users_id_fk", + "tableFrom": "api_keys", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_keys_key_unique": { + "name": "api_keys_key_unique", + "nullsNotDistinct": false, + "columns": [ + "key" + ] + } + } + }, + "deployments": { + "name": "deployments", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "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 + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "showcase_media": { + "name": "showcase_media", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "environment": { + "name": "environment", + "type": "deployment_environment", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "deployments_user_id_users_id_fk": { + "name": "deployments_user_id_users_id_fk", + "tableFrom": "deployments", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployments_workflow_version_id_workflow_versions_id_fk": { + "name": "deployments_workflow_version_id_workflow_versions_id_fk", + "tableFrom": "deployments", + "tableTo": "workflow_versions", + "columnsFrom": [ + "workflow_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "deployments_workflow_id_workflows_id_fk": { + "name": "deployments_workflow_id_workflows_id_fk", + "tableFrom": "deployments", + "tableTo": "workflows", + "columnsFrom": [ + "workflow_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployments_machine_id_machines_id_fk": { + "name": "deployments_machine_id_machines_id_fk", + "tableFrom": "deployments", + "tableTo": "machines", + "columnsFrom": [ + "machine_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "machines": { + "name": "machines", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "auth_token": { + "name": "auth_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "machine_type", + "primaryKey": false, + "notNull": true, + "default": "'classic'" + }, + "status": { + "name": "status", + "type": "machine_status", + "primaryKey": false, + "notNull": true, + "default": "'ready'" + }, + "snapshot": { + "name": "snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "models": { + "name": "models", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "gpu": { + "name": "gpu", + "type": "machine_gpu", + "primaryKey": false, + "notNull": false + }, + "build_machine_instance_id": { + "name": "build_machine_instance_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "build_log": { + "name": "build_log", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "machines_user_id_users_id_fk": { + "name": "machines_user_id_users_id_fk", + "tableFrom": "machines", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "users": { + "name": "users", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "workflow_run_outputs": { + "name": "workflow_run_outputs", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workflow_run_outputs_run_id_workflow_runs_id_fk": { + "name": "workflow_run_outputs_run_id_workflow_runs_id_fk", + "tableFrom": "workflow_run_outputs", + "tableTo": "workflow_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "workflow_runs": { + "name": "workflow_runs", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workflow_version_id": { + "name": "workflow_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "workflow_inputs": { + "name": "workflow_inputs", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "workflow_id": { + "name": "workflow_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "machine_id": { + "name": "machine_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "origin": { + "name": "origin", + "type": "workflow_run_origin", + "primaryKey": false, + "notNull": true, + "default": "'api'" + }, + "status": { + "name": "status", + "type": "workflow_run_status", + "primaryKey": false, + "notNull": true, + "default": "'not-started'" + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workflow_runs_workflow_version_id_workflow_versions_id_fk": { + "name": "workflow_runs_workflow_version_id_workflow_versions_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "workflow_versions", + "columnsFrom": [ + "workflow_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "workflow_runs_workflow_id_workflows_id_fk": { + "name": "workflow_runs_workflow_id_workflows_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "workflows", + "columnsFrom": [ + "workflow_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_runs_machine_id_machines_id_fk": { + "name": "workflow_runs_machine_id_machines_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "machines", + "columnsFrom": [ + "machine_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "workflows": { + "name": "workflows", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workflows_user_id_users_id_fk": { + "name": "workflows_user_id_users_id_fk", + "tableFrom": "workflows", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "workflow_versions": { + "name": "workflow_versions", + "schema": "comfyui_deploy", + "columns": { + "workflow_id": { + "name": "workflow_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workflow": { + "name": "workflow", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "workflow_api": { + "name": "workflow_api", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "snapshot": { + "name": "snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workflow_versions_workflow_id_workflows_id_fk": { + "name": "workflow_versions_workflow_id_workflows_id_fk", + "tableFrom": "workflow_versions", + "tableTo": "workflows", + "columnsFrom": [ + "workflow_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": { + "deployment_environment": { + "name": "deployment_environment", + "values": { + "staging": "staging", + "production": "production", + "public-share": "public-share" + } + }, + "machine_gpu": { + "name": "machine_gpu", + "values": { + "T4": "T4", + "A10G": "A10G", + "A100": "A100" + } + }, + "machine_status": { + "name": "machine_status", + "values": { + "ready": "ready", + "building": "building", + "error": "error" + } + }, + "machine_type": { + "name": "machine_type", + "values": { + "classic": "classic", + "runpod-serverless": "runpod-serverless", + "modal-serverless": "modal-serverless", + "comfy-deploy-serverless": "comfy-deploy-serverless" + } + }, + "workflow_run_origin": { + "name": "workflow_run_origin", + "values": { + "manual": "manual", + "api": "api", + "public-share": "public-share" + } + }, + "workflow_run_status": { + "name": "workflow_run_status", + "values": { + "not-started": "not-started", + "running": "running", + "uploading": "uploading", + "success": "success", + "failed": "failed" + } + } + }, + "schemas": { + "comfyui_deploy": "comfyui_deploy" + }, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/web/drizzle/meta/0030_snapshot.json b/web/drizzle/meta/0030_snapshot.json new file mode 100644 index 0000000..ed4a5c7 --- /dev/null +++ b/web/drizzle/meta/0030_snapshot.json @@ -0,0 +1,756 @@ +{ + "id": "db06ea66-92c2-4ebe-93c1-6cb8a90ccd8b", + "prevId": "a7d6a8dd-0e15-4165-98a2-de2a334455dc", + "version": "5", + "dialect": "pg", + "tables": { + "api_keys": { + "name": "api_keys", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revoked": { + "name": "revoked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "api_keys_user_id_users_id_fk": { + "name": "api_keys_user_id_users_id_fk", + "tableFrom": "api_keys", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_keys_key_unique": { + "name": "api_keys_key_unique", + "nullsNotDistinct": false, + "columns": [ + "key" + ] + } + } + }, + "deployments": { + "name": "deployments", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workflow_version_id": { + "name": "workflow_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "machine_id": { + "name": "machine_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "showcase_media": { + "name": "showcase_media", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "environment": { + "name": "environment", + "type": "deployment_environment", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "deployments_user_id_users_id_fk": { + "name": "deployments_user_id_users_id_fk", + "tableFrom": "deployments", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployments_workflow_version_id_workflow_versions_id_fk": { + "name": "deployments_workflow_version_id_workflow_versions_id_fk", + "tableFrom": "deployments", + "tableTo": "workflow_versions", + "columnsFrom": [ + "workflow_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "deployments_workflow_id_workflows_id_fk": { + "name": "deployments_workflow_id_workflows_id_fk", + "tableFrom": "deployments", + "tableTo": "workflows", + "columnsFrom": [ + "workflow_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployments_machine_id_machines_id_fk": { + "name": "deployments_machine_id_machines_id_fk", + "tableFrom": "deployments", + "tableTo": "machines", + "columnsFrom": [ + "machine_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "machines": { + "name": "machines", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "auth_token": { + "name": "auth_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "machine_type", + "primaryKey": false, + "notNull": true, + "default": "'classic'" + }, + "status": { + "name": "status", + "type": "machine_status", + "primaryKey": false, + "notNull": true, + "default": "'ready'" + }, + "snapshot": { + "name": "snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "models": { + "name": "models", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "gpu": { + "name": "gpu", + "type": "machine_gpu", + "primaryKey": false, + "notNull": false + }, + "build_machine_instance_id": { + "name": "build_machine_instance_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "build_log": { + "name": "build_log", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "machines_user_id_users_id_fk": { + "name": "machines_user_id_users_id_fk", + "tableFrom": "machines", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "users": { + "name": "users", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "workflow_run_outputs": { + "name": "workflow_run_outputs", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workflow_run_outputs_run_id_workflow_runs_id_fk": { + "name": "workflow_run_outputs_run_id_workflow_runs_id_fk", + "tableFrom": "workflow_run_outputs", + "tableTo": "workflow_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "workflow_runs": { + "name": "workflow_runs", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workflow_version_id": { + "name": "workflow_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "workflow_inputs": { + "name": "workflow_inputs", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "workflow_id": { + "name": "workflow_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "machine_id": { + "name": "machine_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "origin": { + "name": "origin", + "type": "workflow_run_origin", + "primaryKey": false, + "notNull": true, + "default": "'api'" + }, + "status": { + "name": "status", + "type": "workflow_run_status", + "primaryKey": false, + "notNull": true, + "default": "'not-started'" + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workflow_runs_workflow_version_id_workflow_versions_id_fk": { + "name": "workflow_runs_workflow_version_id_workflow_versions_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "workflow_versions", + "columnsFrom": [ + "workflow_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "workflow_runs_workflow_id_workflows_id_fk": { + "name": "workflow_runs_workflow_id_workflows_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "workflows", + "columnsFrom": [ + "workflow_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_runs_machine_id_machines_id_fk": { + "name": "workflow_runs_machine_id_machines_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "machines", + "columnsFrom": [ + "machine_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "workflows": { + "name": "workflows", + "schema": "comfyui_deploy", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workflows_user_id_users_id_fk": { + "name": "workflows_user_id_users_id_fk", + "tableFrom": "workflows", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "workflow_versions": { + "name": "workflow_versions", + "schema": "comfyui_deploy", + "columns": { + "workflow_id": { + "name": "workflow_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workflow": { + "name": "workflow", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "workflow_api": { + "name": "workflow_api", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "snapshot": { + "name": "snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workflow_versions_workflow_id_workflows_id_fk": { + "name": "workflow_versions_workflow_id_workflows_id_fk", + "tableFrom": "workflow_versions", + "tableTo": "workflows", + "columnsFrom": [ + "workflow_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": { + "deployment_environment": { + "name": "deployment_environment", + "values": { + "staging": "staging", + "production": "production", + "public-share": "public-share" + } + }, + "machine_gpu": { + "name": "machine_gpu", + "values": { + "T4": "T4", + "A10G": "A10G", + "A100": "A100" + } + }, + "machine_status": { + "name": "machine_status", + "values": { + "ready": "ready", + "building": "building", + "error": "error" + } + }, + "machine_type": { + "name": "machine_type", + "values": { + "classic": "classic", + "runpod-serverless": "runpod-serverless", + "modal-serverless": "modal-serverless", + "comfy-deploy-serverless": "comfy-deploy-serverless" + } + }, + "workflow_run_origin": { + "name": "workflow_run_origin", + "values": { + "manual": "manual", + "api": "api", + "public-share": "public-share" + } + }, + "workflow_run_status": { + "name": "workflow_run_status", + "values": { + "not-started": "not-started", + "running": "running", + "uploading": "uploading", + "success": "success", + "failed": "failed" + } + } + }, + "schemas": { + "comfyui_deploy": "comfyui_deploy" + }, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/web/drizzle/meta/_journal.json b/web/drizzle/meta/_journal.json index eb5f0f0..3bfcc76 100644 --- a/web/drizzle/meta/_journal.json +++ b/web/drizzle/meta/_journal.json @@ -204,6 +204,20 @@ "when": 1705642345817, "tag": "0028_futuristic_lady_deathstrike", "breakpoints": true + }, + { + "idx": 29, + "version": "5", + "when": 1705662714161, + "tag": "0029_large_frightful_four", + "breakpoints": true + }, + { + "idx": 30, + "version": "5", + "when": 1705716303820, + "tag": "0030_kind_doorman", + "breakpoints": true } ] } \ No newline at end of file diff --git a/web/package.json b/web/package.json index fd3fa9a..fe6e469 100644 --- a/web/package.json +++ b/web/package.json @@ -60,6 +60,7 @@ "dayjs": "^1.11.10", "drizzle-orm": "^0.29.1", "drizzle-zod": "^0.5.1", + "embla-carousel-react": "^8.0.0-rc19", "fast-glob": "^3.3.2", "flexsearch": "^0.7.31", "framer-motion": "^10.16.16", diff --git a/web/src/app/(app)/@modal/(.)share/[share_id]/settings/page.tsx b/web/src/app/(app)/@modal/(.)share/[share_id]/settings/page.tsx new file mode 100644 index 0000000..b13ec55 --- /dev/null +++ b/web/src/app/(app)/@modal/(.)share/[share_id]/settings/page.tsx @@ -0,0 +1,9 @@ +import { SharePageSettings } from "@/components/SharePageSettings"; + +export default async function Page({ + params, +}: { + params: { share_id: string }; +}) { + return ; +} diff --git a/web/src/app/(app)/@modal/default.tsx b/web/src/app/(app)/@modal/default.tsx new file mode 100644 index 0000000..dc6be5e --- /dev/null +++ b/web/src/app/(app)/@modal/default.tsx @@ -0,0 +1,7 @@ +import type { FC } from "react"; + +const Default: FC = () => { + return null; +}; + +export default Default; diff --git a/web/src/app/(app)/layout.tsx b/web/src/app/(app)/layout.tsx index 332b36c..e8ab6c9 100644 --- a/web/src/app/(app)/layout.tsx +++ b/web/src/app/(app)/layout.tsx @@ -1,15 +1,14 @@ import { Navbar } from "../../components/Navbar"; import "./globals.css"; +import { PHProvider } from "./providers"; import { TooltipProvider } from "@/components/ui/tooltip"; import { ClerkProvider } from "@clerk/nextjs"; import type { Metadata } from "next"; import meta from "next-gen/config"; import PlausibleProvider from "next-plausible"; +import dynamic from "next/dynamic"; import { Inter } from "next/font/google"; import { Toaster } from "sonner"; -import { PHProvider } from "./providers"; - -import dynamic from "next/dynamic"; const PostHogPageView = dynamic(() => import("./PostHogPageView"), { ssr: false, @@ -34,8 +33,10 @@ export const metadata: Metadata = { export default function RootLayout({ children, + modal, }: { children: React.ReactNode; + modal: React.ReactNode; }) { return ( @@ -60,6 +61,7 @@ export default function RootLayout({ {children} + {modal} diff --git a/web/src/app/(app)/share/[share_id]/page.tsx b/web/src/app/(app)/share/[share_id]/page.tsx index eddb7ab..e49922d 100644 --- a/web/src/app/(app)/share/[share_id]/page.tsx +++ b/web/src/app/(app)/share/[share_id]/page.tsx @@ -1,8 +1,6 @@ import { ButtonActionMenu } from "@/components/ButtonActionLoader"; -import { - PublicRunOutputs, -} from "@/components/VersionSelect"; import { RunWorkflowInline } from "@/components/RunWorkflowInline"; +import { PublicRunOutputs } from "@/components/VersionSelect"; import { Card, CardContent, @@ -89,6 +87,11 @@ export default async function Page({ +
+ {sharedDeployment?.description && ( + <>{sharedDeployment?.description} + )} +
- + diff --git a/web/src/app/(app)/share/[share_id]/settings/page.tsx b/web/src/app/(app)/share/[share_id]/settings/page.tsx new file mode 100644 index 0000000..b13ec55 --- /dev/null +++ b/web/src/app/(app)/share/[share_id]/settings/page.tsx @@ -0,0 +1,9 @@ +import { SharePageSettings } from "@/components/SharePageSettings"; + +export default async function Page({ + params, +}: { + params: { share_id: string }; +}) { + return ; +} diff --git a/web/src/app/(app)/workflows/[workflow_id]/@runs/page.tsx b/web/src/app/(app)/workflows/[workflow_id]/@runs/page.tsx index cf2952a..7d8d4d0 100644 --- a/web/src/app/(app)/workflows/[workflow_id]/@runs/page.tsx +++ b/web/src/app/(app)/workflows/[workflow_id]/@runs/page.tsx @@ -17,7 +17,7 @@ export default async function Page({ Run
- +
diff --git a/web/src/components/ButtonActionLoader.tsx b/web/src/components/ButtonActionLoader.tsx index 331a3bb..9f517d4 100644 --- a/web/src/components/ButtonActionLoader.tsx +++ b/web/src/components/ButtonActionLoader.tsx @@ -9,6 +9,7 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +import { useAuth, useClerk } from "@clerk/nextjs"; import { MoreVertical } from "lucide-react"; import { useRouter } from "next/navigation"; import { useState } from "react"; @@ -49,7 +50,9 @@ export function ButtonActionMenu(props: { action: () => Promise; }[]; }) { + const user = useAuth(); const [isLoading, setIsLoading] = useState(false); + const clerk = useClerk(); return ( @@ -64,6 +67,13 @@ export function ButtonActionMenu(props: { { + if (!user.isSignedIn) { + clerk.openSignIn({ + redirectUrl: window.location.href, + }); + return; + } + setIsLoading(true); await callServerPromise(action.action()); setIsLoading(false); diff --git a/web/src/components/DeploymentDisplay.tsx b/web/src/components/DeploymentDisplay.tsx index b3b6341..08d9625 100644 --- a/web/src/components/DeploymentDisplay.tsx +++ b/web/src/components/DeploymentDisplay.tsx @@ -1,6 +1,5 @@ -import { ButtonAction } from "@/components/ButtonActionLoader"; +import { DeploymentRow } from "./DeploymentRow"; import { CodeBlock } from "@/components/CodeBlock"; -import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, @@ -10,15 +9,9 @@ import { DialogTrigger, } from "@/components/ui/dialog"; import { ScrollArea } from "@/components/ui/scroll-area"; -import { TableCell, TableRow } from "@/components/ui/table"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { getInputsFromWorkflow } from "@/lib/getInputsFromWorkflow"; -import { getRelativeTime } from "@/lib/getRelativeTime"; -import { removePublicShareDeployment } from "@/server/curdDeploments"; import type { findAllDeployments } from "@/server/findAllRuns"; -import { ExternalLink } from "lucide-react"; -import { headers } from "next/headers"; -import Link from "next/link"; const curlTemplate = ` curl --request POST \ @@ -90,33 +83,21 @@ const run = await client.getRun(run_id); export function DeploymentDisplay({ deployment, + domain, }: { deployment: Awaited>[0]; + domain: string; }) { - const headersList = headers(); - const host = headersList.get("host") || ""; - const protocol = headersList.get("x-forwarded-proto") || ""; - const domain = `${protocol}://${host}`; - const workflowInput = getInputsFromWorkflow(deployment.version); + if (deployment.environment == "public-share") { + return ; + } + return ( - - - {deployment.environment} - - - {deployment.version?.version} - - - {deployment.machine?.name} - - - {getRelativeTime(deployment.updated_at)} - - + @@ -126,105 +107,79 @@ export function DeploymentDisplay({ Code for your deployment client - {deployment.environment !== "public-share" ? ( - - - Server Client - NodeJS Fetch - CURL - - -
- Copy and paste the ComfyDeployClient form  - - here - -
- - Create a run via deployment id - 0 - ? jsClientCreateRunTemplate - : jsClientCreateRunNoInputsTemplate, - deployment, - domain, - workflowInput - )} - /> - Check the status of the run, and retrieve the outputs - -
- - Trigger the workflow - - Check the status of the run, and retrieve the outputs - - - - - - -
- ) : ( -
- - -
- )} + here + + + + Create a run via deployment id + 0 + ? jsClientCreateRunTemplate + : jsClientCreateRunNoInputsTemplate, + deployment, + domain, + workflowInput + )} + /> + Check the status of the run, and retrieve the outputs + + + + Trigger the workflow + + Check the status of the run, and retrieve the outputs + + + + + + +
diff --git a/web/src/components/DeploymentRow.tsx b/web/src/components/DeploymentRow.tsx new file mode 100644 index 0000000..232cbeb --- /dev/null +++ b/web/src/components/DeploymentRow.tsx @@ -0,0 +1,37 @@ +"use client"; + +import { TableCell, TableRow } from "@/components/ui/table"; +import { getRelativeTime } from "@/lib/getRelativeTime"; +import type { findAllDeployments } from "@/server/findAllRuns"; +import { useRouter } from "next/navigation"; + +export function DeploymentRow({ + deployment, +}: { + deployment: Awaited>[0]; +}) { + const router = useRouter(); + return ( + { + if (deployment.environment == "public-share") { + router.push(`/share/${deployment.id}/settings`); + } + }} + > + + {deployment.environment} + + + {deployment.version?.version} + + + {deployment.machine?.name} + + + {getRelativeTime(deployment.updated_at)} + + + ); +} diff --git a/web/src/components/InsertModal.tsx b/web/src/components/InsertModal.tsx index 518a297..a6b3280 100644 --- a/web/src/components/InsertModal.tsx +++ b/web/src/components/InsertModal.tsx @@ -11,6 +11,7 @@ import { DialogDescription, DialogHeader, DialogTitle, + DialogTrigger, } from "@/components/ui/dialog"; import { Tooltip, @@ -106,12 +107,14 @@ export function UpdateModal< Y extends UnknownKeysParam, Z extends ZodObject >(props: { - open: boolean; - setOpen: (open: boolean) => void; + open?: boolean; + setOpen?: (open: boolean) => void; title: string; description: string; dialogClassName?: string; - data: z.infer; + data: z.infer & { + id: string; + }; serverAction: ( data: z.infer & { id: string; @@ -119,8 +122,13 @@ export function UpdateModal< ) => Promise; formSchema: Z; fieldConfig?: FieldConfig>; + trigger?: React.ReactNode; + extraButtons?: React.ReactNode; }) { - // const [open, setOpen] = React.useState(false); + const [_open, _setOpen] = React.useState(false); + const open = props.open ?? _open; + const setOpen = props.setOpen ?? _setOpen; + const [values, setValues] = useState>>({}); const [isLoading, setIsLoading] = React.useState(false); @@ -129,10 +137,18 @@ export function UpdateModal< }, [props.data]); return ( - - {/* - {props.title} - */} + + {props.trigger ?? ( + { + setOpen(true); + }} + > + {props.trigger} + + )} {props.title} @@ -152,13 +168,14 @@ export function UpdateModal< }) ); setIsLoading(false); - props.setOpen(false); + setOpen(false); }} > -
+
+ {props.extraButtons} Save Changes - {isLoading && } + {isLoading && }
diff --git a/web/src/components/Main.tsx b/web/src/components/Main.tsx index b7fc746..b5473e2 100644 --- a/web/src/components/Main.tsx +++ b/web/src/components/Main.tsx @@ -69,7 +69,7 @@ export default async function Main() { - + {meta.tagline} diff --git a/web/src/components/OutputPreviews.tsx b/web/src/components/OutputPreviews.tsx new file mode 100644 index 0000000..f22e85f --- /dev/null +++ b/web/src/components/OutputPreviews.tsx @@ -0,0 +1,52 @@ +import { Card, CardContent } from "@/components/ui/card"; +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious, + type CarouselApi, +} from "@/components/ui/carousel"; +import * as React from "react"; + +export function OutputPreview() { + const [api, setApi] = React.useState(); + const [current, setCurrent] = React.useState(0); + const [count, setCount] = React.useState(0); + + React.useEffect(() => { + if (!api) { + return; + } + + setCount(api.scrollSnapList().length); + setCurrent(api.selectedScrollSnap() + 1); + + api.on("select", () => { + setCurrent(api.selectedScrollSnap() + 1); + }); + }, [api]); + + return ( +
+ + + {Array.from({ length: 5 }).map((_, index) => ( + + + + {index + 1} + + + + ))} + + + + +
+ Slide {current} of {count} +
+
+ ); +} diff --git a/web/src/components/RouteRefresher.tsx b/web/src/components/RouteRefresher.tsx index b772986..f17cb28 100644 --- a/web/src/components/RouteRefresher.tsx +++ b/web/src/components/RouteRefresher.tsx @@ -1,14 +1,21 @@ "use client"; import { LoadingIcon } from "@/components/LoadingIcon"; +import { Button } from "@/components/ui/button"; +import { RefreshCcw } from "lucide-react"; import { useRouter } from "next/navigation"; import { useEffect, useTransition } from "react"; -export function RouteRefresher(props: { interval: number }) { +export function RouteRefresher(props: { + interval: number; + autoRefresh: boolean; +}) { const [isPending, startTransition] = useTransition(); const router = useRouter(); useEffect(() => { + if (!props.autoRefresh) return; + let timeout: NodeJS.Timeout; const refresh = () => { @@ -35,7 +42,24 @@ export function RouteRefresher(props: { interval: number }) { clearTimeout(timeout); window.removeEventListener("visibilitychange", handleVisibilityChange); }; - }, [props.interval, router]); + }, [props.interval, router, props.autoRefresh]); - return
{isPending && }
; + return ( +
+ {isPending && } + {!isPending && !props.autoRefresh && ( + + )} +
+ ); } diff --git a/web/src/components/RunDisplay.tsx b/web/src/components/RunDisplay.tsx index 5f0f7da..445b061 100644 --- a/web/src/components/RunDisplay.tsx +++ b/web/src/components/RunDisplay.tsx @@ -33,7 +33,9 @@ export async function RunDisplay({ {run.version?.version} - {run.origin} + + {run.origin} + diff --git a/web/src/components/RunWorkflowInline.tsx b/web/src/components/RunWorkflowInline.tsx index cf1f94b..ccb4242 100644 --- a/web/src/components/RunWorkflowInline.tsx +++ b/web/src/components/RunWorkflowInline.tsx @@ -40,13 +40,10 @@ export function RunWorkflowInline({ } = publicRunStore(); const runWorkflow = async () => { - console.log(); - if (!user.isSignedIn) { clerk.openSignIn({ redirectUrl: window.location.href, }); - console.log("hi"); return; } console.log(values); diff --git a/web/src/components/RunsTable.tsx b/web/src/components/RunsTable.tsx index d869d93..6eee49c 100644 --- a/web/src/components/RunsTable.tsx +++ b/web/src/components/RunsTable.tsx @@ -14,6 +14,7 @@ import { TableRow, } from "@/components/ui/table"; import { parseAsInteger } from "next-usequerystate"; +import { headers } from "next/headers"; const itemPerPage = 6; const pageParser = parseAsInteger.withDefault(1); @@ -69,8 +70,14 @@ export async function RunsTable(props: { export async function DeploymentsTable(props: { workflow_id: string }) { const allRuns = await findAllDeployments(props.workflow_id); + + const headersList = headers(); + const host = headersList.get("host") || ""; + const protocol = headersList.get("x-forwarded-proto") || ""; + const domain = `${protocol}://${host}`; + return ( -
+
A list of your deployments @@ -83,7 +90,7 @@ export async function DeploymentsTable(props: { workflow_id: string }) { {allRuns.map((run) => ( - + ))}
diff --git a/web/src/components/Section.tsx b/web/src/components/Section.tsx index 7ac27c9..ca24cff 100644 --- a/web/src/components/Section.tsx +++ b/web/src/components/Section.tsx @@ -1,33 +1,37 @@ -import { Button, buttonVariants } from '@/components/ui/button'; -type ButtonProps = React.ComponentProps; -type LinkProps = React.ComponentProps; -import { Card as BaseCard } from '@/components/ui/card'; -type CardProps = React.ComponentProps; -import { Tabs, TabsTrigger as Tab, TabsList } from '@/components/ui/tabs'; -type TabsProps = React.ComponentProps; import { Accordion, AccordionItem, AccordionContent, AccordionTrigger, -} from '@/components/ui/accordion'; -type AccordionProps = React.ComponentProps; -import { Badge as Chip } from '@/components/ui/badge'; -type ChipProps = React.ComponentProps; - -import Link from 'next/link'; +} from "@/components/ui/accordion"; +import { Badge as Chip } from "@/components/ui/badge"; +import { Button, buttonVariants } from "@/components/ui/button"; +import { Card as BaseCard } from "@/components/ui/card"; +import { Tabs, TabsTrigger as Tab, TabsList } from "@/components/ui/tabs"; +// import { PiCheckCircleDuotone } from 'react-icons/pi'; +import { cn } from "@/lib/utils"; +import { ChevronRight as MdChevronRight } from "lucide-react"; +import { CheckCircle as PiCheckCircleDuotone } from "lucide-react"; +import Link from "next/link"; import type { HTMLAttributeAnchorTarget, HTMLAttributes, ReactNode, -} from 'react'; -import { twMerge } from 'tailwind-merge'; -import { ChevronRight as MdChevronRight} from 'lucide-react' +} from "react"; // import { MdChevronRight } from 'react-icons/md'; -import React from 'react'; -import { CheckCircle as PiCheckCircleDuotone } from 'lucide-react' -// import { PiCheckCircleDuotone } from 'react-icons/pi'; -import { cn } from '@/lib/utils'; +import React from "react"; +import { twMerge } from "tailwind-merge"; + +type ButtonProps = React.ComponentProps; +type LinkProps = React.ComponentProps; + +type CardProps = React.ComponentProps; + +type TabsProps = React.ComponentProps; + +type AccordionProps = React.ComponentProps; + +type ChipProps = React.ComponentProps; function Section({ className, @@ -41,8 +45,8 @@ function Section({ return (
@@ -64,12 +68,12 @@ function Title({

{children} @@ -86,12 +90,12 @@ function Subtitle({

{children} @@ -103,7 +107,7 @@ function Announcement({ className, children, href, - target = '_blank', + target = "_blank", ...props }: ChipProps & { href?: string; //string | UrlObject; @@ -112,8 +116,8 @@ function Announcement({ return ( {children} - {' '} + {" "} @@ -186,8 +190,8 @@ function PrimaryAction({ buttonVariants({ variant: variant, }), - 'group', - className, + "group", + className )} {...props} > @@ -206,17 +210,17 @@ function SecondaryAction({ hideArrow, ...props }: ActionLinkProps | ActionProps) { - if (props.be === 'button') { + if (props.be === "button") { return ( + + + } + data={{ + id: deployment.id, + description: deployment.description, + showcase_media: deployment.showcase_media ?? [], + }} + title="Share Page" + description="Edit share page details." + serverAction={updateSharePageInfo} + formSchema={publicShareDeployment} + /> + + ); +} diff --git a/web/src/components/VersionSelect.tsx b/web/src/components/VersionSelect.tsx index c37e521..b1067d7 100644 --- a/web/src/components/VersionSelect.tsx +++ b/web/src/components/VersionSelect.tsx @@ -39,7 +39,7 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; -import type { workflowAPINodeType } from "@/db/schema"; +import type { showcaseMediaNullable, workflowAPINodeType } from "@/db/schema"; import { checkStatus, createRun } from "@/server/createRun"; import { createDeployments } from "@/server/curdDeploments"; import type { getMachines } from "@/server/curdMachine"; @@ -154,7 +154,9 @@ export const publicRunStore = create((set) => ({ setStatus: (status) => set({ status }), })); -export function PublicRunOutputs() { +export function PublicRunOutputs(props: { + preview: z.infer; +}) { const { image, loading, runId, status, setStatus, setImage, setLoading } = publicRunStore(); @@ -176,6 +178,15 @@ export function PublicRunOutputs() { return (
+ {!loading && !image && props.preview && props.preview.length > 0 && ( + <> + Generated image + + )} {!loading && image && ( void; + error: Error & { digest?: string }; + reset: () => void; }) { useEffect(() => { - // Log the error to an error reporting service - console.log(error?.message); + console.log(error.message); }, [error]); return ( @@ -26,9 +25,14 @@ export default function ErrorPage({
Unexpected error.
-
Error: {error?.message}
+
Error: {error.message}
-
@@ -39,14 +43,20 @@ export default function ErrorPage({ ); } -export function ErrorFullPage() { +export function ErrorFullPage({ + error, + reset, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) { return (
- +
); } diff --git a/web/src/components/ui/auto-form/utils.ts b/web/src/components/ui/auto-form/utils.ts index 517447a..a69761a 100644 --- a/web/src/components/ui/auto-form/utils.ts +++ b/web/src/components/ui/auto-form/utils.ts @@ -1,5 +1,5 @@ -import { DefaultValues } from "react-hook-form"; -import { z } from "zod"; +import type { DefaultValues } from "react-hook-form"; +import type { z } from "zod"; // TODO: This should support recursive ZodEffects but TypeScript doesn't allow circular type definitions. export type ZodObjectOrWrapped = @@ -21,7 +21,7 @@ export function beautifyObjectName(string: string) { * This will unpack optionals, refinements, etc. */ export function getBaseSchema< - ChildType extends z.ZodAny | z.AnyZodObject = z.ZodAny, + ChildType extends z.ZodAny | z.AnyZodObject = z.ZodAny >(schema: ChildType | z.ZodEffects): ChildType { if ("innerType" in schema._def) { return getBaseSchema(schema._def.innerType as ChildType); @@ -54,12 +54,12 @@ export function getDefaultValueInZodStack(schema: z.ZodAny): any { if ("innerType" in typedSchema._def) { return getDefaultValueInZodStack( - typedSchema._def.innerType as unknown as z.ZodAny, + typedSchema._def.innerType as unknown as z.ZodAny ); } if ("schema" in typedSchema._def) { return getDefaultValueInZodStack( - (typedSchema._def as any).schema as z.ZodAny, + (typedSchema._def as any).schema as z.ZodAny ); } return undefined; @@ -69,7 +69,7 @@ export function getDefaultValueInZodStack(schema: z.ZodAny): any { * Get all default values from a Zod schema. */ export function getDefaultValues>( - schema: Schema, + schema: Schema ) { const { shape } = schema; type DefaultValuesType = DefaultValues>>; @@ -80,7 +80,7 @@ export function getDefaultValues>( if (getBaseType(item) === "ZodObject") { const defaultItems = getDefaultValues( - getBaseSchema(item) as unknown as z.ZodObject, + getBaseSchema(item) as unknown as z.ZodObject ); for (const defaultItemKey of Object.keys(defaultItems)) { const pathKey = `${key}.${defaultItemKey}` as keyof DefaultValuesType; @@ -98,7 +98,7 @@ export function getDefaultValues>( } export function getObjectFormSchema( - schema: ZodObjectOrWrapped, + schema: ZodObjectOrWrapped ): z.ZodObject { if (schema._def.typeName === "ZodEffects") { const typedSchema = schema as z.ZodEffects>; @@ -116,7 +116,7 @@ export function zodToHtmlInputProps( | z.ZodNumber | z.ZodString | z.ZodOptional - | any, + | any ): React.InputHTMLAttributes { if (["ZodOptional", "ZodNullable"].includes(schema._def.typeName)) { const typedSchema = schema as z.ZodOptional; @@ -128,9 +128,10 @@ export function zodToHtmlInputProps( const typedSchema = schema as z.ZodNumber | z.ZodString; - if (!("checks" in typedSchema._def)) return { - required: true - }; + if (!("checks" in typedSchema._def)) + return { + required: true, + }; const { checks } = typedSchema._def; const inputProps: React.InputHTMLAttributes = { diff --git a/web/src/components/ui/carousel.tsx b/web/src/components/ui/carousel.tsx new file mode 100644 index 0000000..ec505d0 --- /dev/null +++ b/web/src/components/ui/carousel.tsx @@ -0,0 +1,262 @@ +"use client" + +import * as React from "react" +import useEmblaCarousel, { + type UseEmblaCarouselType, +} from "embla-carousel-react" +import { ArrowLeft, ArrowRight } from "lucide-react" + +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" + +type CarouselApi = UseEmblaCarouselType[1] +type UseCarouselParameters = Parameters +type CarouselOptions = UseCarouselParameters[0] +type CarouselPlugin = UseCarouselParameters[1] + +type CarouselProps = { + opts?: CarouselOptions + plugins?: CarouselPlugin + orientation?: "horizontal" | "vertical" + setApi?: (api: CarouselApi) => void +} + +type CarouselContextProps = { + carouselRef: ReturnType[0] + api: ReturnType[1] + scrollPrev: () => void + scrollNext: () => void + canScrollPrev: boolean + canScrollNext: boolean +} & CarouselProps + +const CarouselContext = React.createContext(null) + +function useCarousel() { + const context = React.useContext(CarouselContext) + + if (!context) { + throw new Error("useCarousel must be used within a ") + } + + return context +} + +const Carousel = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & CarouselProps +>( + ( + { + orientation = "horizontal", + opts, + setApi, + plugins, + className, + children, + ...props + }, + ref + ) => { + const [carouselRef, api] = useEmblaCarousel( + { + ...opts, + axis: orientation === "horizontal" ? "x" : "y", + }, + plugins + ) + const [canScrollPrev, setCanScrollPrev] = React.useState(false) + const [canScrollNext, setCanScrollNext] = React.useState(false) + + const onSelect = React.useCallback((api: CarouselApi) => { + if (!api) { + return + } + + setCanScrollPrev(api.canScrollPrev()) + setCanScrollNext(api.canScrollNext()) + }, []) + + const scrollPrev = React.useCallback(() => { + api?.scrollPrev() + }, [api]) + + const scrollNext = React.useCallback(() => { + api?.scrollNext() + }, [api]) + + const handleKeyDown = React.useCallback( + (event: React.KeyboardEvent) => { + if (event.key === "ArrowLeft") { + event.preventDefault() + scrollPrev() + } else if (event.key === "ArrowRight") { + event.preventDefault() + scrollNext() + } + }, + [scrollPrev, scrollNext] + ) + + React.useEffect(() => { + if (!api || !setApi) { + return + } + + setApi(api) + }, [api, setApi]) + + React.useEffect(() => { + if (!api) { + return + } + + onSelect(api) + api.on("reInit", onSelect) + api.on("select", onSelect) + + return () => { + api?.off("select", onSelect) + } + }, [api, onSelect]) + + return ( + +
+ {children} +
+
+ ) + } +) +Carousel.displayName = "Carousel" + +const CarouselContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const { carouselRef, orientation } = useCarousel() + + return ( +
+
+
+ ) +}) +CarouselContent.displayName = "CarouselContent" + +const CarouselItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const { orientation } = useCarousel() + + return ( +
+ ) +}) +CarouselItem.displayName = "CarouselItem" + +const CarouselPrevious = React.forwardRef< + HTMLButtonElement, + React.ComponentProps +>(({ className, variant = "outline", size = "icon", ...props }, ref) => { + const { orientation, scrollPrev, canScrollPrev } = useCarousel() + + return ( + + ) +}) +CarouselPrevious.displayName = "CarouselPrevious" + +const CarouselNext = React.forwardRef< + HTMLButtonElement, + React.ComponentProps +>(({ className, variant = "outline", size = "icon", ...props }, ref) => { + const { orientation, scrollNext, canScrollNext } = useCarousel() + + return ( + + ) +}) +CarouselNext.displayName = "CarouselNext" + +export { + type CarouselApi, + Carousel, + CarouselContent, + CarouselItem, + CarouselPrevious, + CarouselNext, +} diff --git a/web/src/components/useServerActionData.tsx b/web/src/components/useServerActionData.tsx new file mode 100644 index 0000000..4e57b79 --- /dev/null +++ b/web/src/components/useServerActionData.tsx @@ -0,0 +1,26 @@ +"use client"; + +import { callServerPromise } from "@/components/callServerPromise"; +import { useEffect, useState, useTransition } from "react"; + +export function useServerActionData( + action: (data: I) => Promise, + input: I +) { + const [data, setData] = useState(null); + const [pending, startTransition] = useTransition(); + const [started, setStarted] = useState(false); + + useEffect(() => { + startTransition(() => { + setStarted(true); + callServerPromise(action(input)).then(setData); + }); + }, [action, input]); + + return { + started, + data, + pending, + }; +} diff --git a/web/src/db/schema.ts b/web/src/db/schema.ts index 0cc2678..f5f8fdf 100644 --- a/web/src/db/schema.ts +++ b/web/src/db/schema.ts @@ -239,6 +239,22 @@ export const insertMachineSchema = createInsertSchema(machinesTable, { type: (schema) => schema.type.default("classic"), }); +export const showcaseMedia = z.array( + z.object({ + url: z.string(), + isCover: z.boolean().default(false), + }) +); + +export const showcaseMediaNullable = z + .array( + z.object({ + url: z.string(), + isCover: z.boolean().default(false), + }) + ) + .nullable(); + export const deploymentsTable = dbSchema.table("deployments", { id: uuid("id").primaryKey().defaultRandom().notNull(), user_id: text("user_id") @@ -246,6 +262,7 @@ export const deploymentsTable = dbSchema.table("deployments", { onDelete: "cascade", }) .notNull(), + org_id: text("org_id"), workflow_version_id: uuid("workflow_version_id") .notNull() .references(() => workflowVersionTable.id), @@ -257,11 +274,27 @@ export const deploymentsTable = dbSchema.table("deployments", { machine_id: uuid("machine_id") .notNull() .references(() => machinesTable.id), + description: text("description"), + showcase_media: + jsonb("showcase_media").$type>(), environment: deploymentEnvironment("environment").notNull(), created_at: timestamp("created_at").defaultNow().notNull(), updated_at: timestamp("updated_at").defaultNow().notNull(), }); +export const publicShareDeployment = z.object({ + description: z.string().nullable(), + showcase_media: showcaseMedia, +}); + +// createInsertSchema(deploymentsTable, { +// description: (schema) => schema.description.default(""), +// showcase_media: () => showcaseMedia.default([]), +// }).pick({ +// description: true, +// showcase_media: true, +// }); + export const deploymentsRelations = relations(deploymentsTable, ({ one }) => ({ machine: one(machinesTable, { fields: [deploymentsTable.machine_id], diff --git a/web/src/server/curdDeploments.ts b/web/src/server/curdDeploments.ts index 4724f54..85270cf 100644 --- a/web/src/server/curdDeploments.ts +++ b/web/src/server/curdDeploments.ts @@ -1,7 +1,7 @@ "use server"; import { db } from "@/db/db"; -import type { DeploymentType } from "@/db/schema"; +import type { DeploymentType, publicShareDeployment } from "@/db/schema"; import { deploymentsTable, workflowTable } from "@/db/schema"; import { createNewWorkflow } from "@/server/createNewWorkflow"; import { addCustomMachine } from "@/server/curdMachine"; @@ -11,6 +11,7 @@ import { and, eq, isNull } from "drizzle-orm"; import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; import "server-only"; +import type { z } from "zod"; export async function createDeployments( workflow_id: string, @@ -18,7 +19,7 @@ export async function createDeployments( machine_id: string, environment: DeploymentType["environment"] ) { - const { userId } = auth(); + const { userId, orgId } = auth(); if (!userId) throw new Error("No user id"); if (!machine_id) { @@ -40,6 +41,7 @@ export async function createDeployments( workflow_id, workflow_version_id: version_id, machine_id, + org_id: orgId, }) .where(eq(deploymentsTable.id, existingDeployment.id)); } else { @@ -49,6 +51,7 @@ export async function createDeployments( workflow_version_id: version_id, machine_id, environment, + org_id: orgId, }); } revalidatePath(`/${workflow_id}`); @@ -195,3 +198,56 @@ export const cloneMachine = withServerPromise(async (deployment_id: string) => { message: "Successfully cloned workflow", }; }); + +export async function findUserShareDeployment(share_id: string) { + const { userId, orgId } = auth(); + + if (!userId) throw new Error("No user id"); + + const [deployment] = await db + .select() + .from(deploymentsTable) + .where( + and( + eq(deploymentsTable.id, share_id), + eq(deploymentsTable.environment, "public-share"), + orgId + ? eq(deploymentsTable.org_id, orgId) + : and( + eq(deploymentsTable.user_id, userId), + isNull(deploymentsTable.org_id) + ) + ) + ); + + if (!deployment) throw new Error("No deployment found"); + + return deployment; +} + +export const updateSharePageInfo = withServerPromise( + async ({ + id, + ...data + }: z.infer & { + id: string; + }) => { + const { userId } = auth(); + if (!userId) return { error: "No user id" }; + + console.log(data); + + const [deployment] = await db + .update(deploymentsTable) + .set(data) + .where( + and( + eq(deploymentsTable.environment, "public-share"), + eq(deploymentsTable.id, id) + ) + ) + .returning(); + + return { message: "Info Updated" }; + } +);