feat: add preview image builder
This commit is contained in:
parent
314eb9fd16
commit
9937252777
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
__pycache__
|
__pycache__
|
||||||
|
.DS_Store
|
@ -13,5 +13,7 @@ SPACES_SECRET="aaa"
|
|||||||
SPACES_CDN_DONT_INCLUDE_BUCKET="false"
|
SPACES_CDN_DONT_INCLUDE_BUCKET="false"
|
||||||
SPACES_CDN_FORCE_PATH_STYLE="true"
|
SPACES_CDN_FORCE_PATH_STYLE="true"
|
||||||
|
|
||||||
|
MODAL_BUILDER_URL=
|
||||||
|
|
||||||
JWT_SECRET="openssl rand -hex 32"
|
JWT_SECRET="openssl rand -hex 32"
|
||||||
PLAUSIBLE_DOMAIN=
|
PLAUSIBLE_DOMAIN=
|
9
web/drizzle/0020_complete_black_tom.sql
Normal file
9
web/drizzle/0020_complete_black_tom.sql
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE "machine_status" AS ENUM('ready', 'building', 'error');
|
||||||
|
EXCEPTION
|
||||||
|
WHEN duplicate_object THEN null;
|
||||||
|
END $$;
|
||||||
|
--> statement-breakpoint
|
||||||
|
ALTER TABLE "comfyui_deploy"."machines" ADD COLUMN "status" "machine_status" DEFAULT 'ready' NOT NULL;--> statement-breakpoint
|
||||||
|
ALTER TABLE "comfyui_deploy"."machines" ADD COLUMN "snapshot" jsonb;--> statement-breakpoint
|
||||||
|
ALTER TABLE "comfyui_deploy"."machines" ADD COLUMN "build_log" text;
|
703
web/drizzle/meta/0020_snapshot.json
Normal file
703
web/drizzle/meta/0020_snapshot.json
Normal file
@ -0,0 +1,703 @@
|
|||||||
|
{
|
||||||
|
"id": "60fd6aaf-0bd4-4b77-b707-fea7c44e829d",
|
||||||
|
"prevId": "4c2423cd-420e-49fc-a6fe-2e6c56ce8c61",
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"workflow_run_origin": {
|
||||||
|
"name": "workflow_run_origin",
|
||||||
|
"values": {
|
||||||
|
"manual": "manual",
|
||||||
|
"api": "api"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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": {}
|
||||||
|
}
|
||||||
|
}
|
@ -141,6 +141,13 @@
|
|||||||
"when": 1704174903117,
|
"when": 1704174903117,
|
||||||
"tag": "0019_damp_stick",
|
"tag": "0019_damp_stick",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 20,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1704350033885,
|
||||||
|
"tag": "0020_complete_black_tom",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
50
web/src/app/(app)/api/machine-built/route.ts
Normal file
50
web/src/app/(app)/api/machine-built/route.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { parseDataSafe } from "../../../../lib/parseDataSafe";
|
||||||
|
import { db } from "@/db/db";
|
||||||
|
import { machinesTable } from "@/db/schema";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const Request = z.object({
|
||||||
|
machine_id: z.string(),
|
||||||
|
endpoint: z.string().optional(),
|
||||||
|
build_log: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
const [data, error] = await parseDataSafe(Request, request);
|
||||||
|
if (!data || error) return error;
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
const { machine_id, endpoint, build_log } = data;
|
||||||
|
|
||||||
|
if (endpoint) {
|
||||||
|
await db
|
||||||
|
.update(machinesTable)
|
||||||
|
.set({
|
||||||
|
status: "ready",
|
||||||
|
endpoint: endpoint,
|
||||||
|
build_log: build_log,
|
||||||
|
})
|
||||||
|
.where(eq(machinesTable.id, machine_id));
|
||||||
|
} else {
|
||||||
|
// console.log(data);
|
||||||
|
await db
|
||||||
|
.update(machinesTable)
|
||||||
|
.set({
|
||||||
|
status: "error",
|
||||||
|
build_log: build_log,
|
||||||
|
})
|
||||||
|
.where(eq(machinesTable.id, machine_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
message: "success",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: 200,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
9
web/src/app/(app)/machines/[machine_id]/loading.tsx
Normal file
9
web/src/app/(app)/machines/[machine_id]/loading.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { LoadingPageWrapper } from "@/components/LoadingWrapper";
|
||||||
|
import { usePathname } from "next/navigation";
|
||||||
|
|
||||||
|
export default function Loading() {
|
||||||
|
const pathName = usePathname();
|
||||||
|
return <LoadingPageWrapper className="h-full" tag={pathName.toLowerCase()} />;
|
||||||
|
}
|
43
web/src/app/(app)/machines/[machine_id]/page.tsx
Normal file
43
web/src/app/(app)/machines/[machine_id]/page.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { MachineBuildLog } from "../../../../components/MachineBuildLog";
|
||||||
|
import { LogsViewer } from "@/components/LogsViewer";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "@/components/ui/card";
|
||||||
|
import { getRelativeTime } from "@/lib/getRelativeTime";
|
||||||
|
import { getMachineById } from "@/server/curdMachine";
|
||||||
|
|
||||||
|
export default async function Page({
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
params: { machine_id: string };
|
||||||
|
}) {
|
||||||
|
const machine = await getMachineById(params.machine_id);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Card className="w-full h-fit mt-4">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>{machine.name}</CardTitle>
|
||||||
|
<CardDescription suppressHydrationWarning={true}>
|
||||||
|
{getRelativeTime(machine?.updated_at)}
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
{machine.status == "building" && (
|
||||||
|
<MachineBuildLog
|
||||||
|
machine_id={params.machine_id}
|
||||||
|
endpoint={process.env.MODAL_BUILDER_URL!}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{machine.build_log && (
|
||||||
|
<LogsViewer logs={JSON.parse(machine.build_log)} />
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -1,6 +0,0 @@
|
|||||||
import { LoadingPageWrapper } from "@/components/LoadingWrapper";
|
|
||||||
|
|
||||||
export default function Loading() {
|
|
||||||
// You can add any UI inside Loading, including a Skeleton.
|
|
||||||
return <LoadingPageWrapper tag="workflow" />;
|
|
||||||
}
|
|
46
web/src/components/LogsViewer.tsx
Normal file
46
web/src/components/LogsViewer.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
export type LogsType = {
|
||||||
|
machine_id?: string;
|
||||||
|
logs: string;
|
||||||
|
timestamp: number;
|
||||||
|
}[];
|
||||||
|
|
||||||
|
export function LogsViewer({ logs }: { logs: LogsType }) {
|
||||||
|
const container = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// console.log(logs.length, container.current);
|
||||||
|
if (container.current) {
|
||||||
|
const scrollHeight = container.current.scrollHeight;
|
||||||
|
|
||||||
|
container.current.scrollTo({
|
||||||
|
top: scrollHeight,
|
||||||
|
behavior: "smooth",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [logs.length]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={(ref) => {
|
||||||
|
if (!container.current && ref) {
|
||||||
|
const scrollHeight = ref.scrollHeight;
|
||||||
|
|
||||||
|
ref.scrollTo({
|
||||||
|
top: scrollHeight,
|
||||||
|
behavior: "instant",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
container.current = ref;
|
||||||
|
}}
|
||||||
|
className="flex flex-col text-xs p-2 overflow-y-scroll max-h-[400px] whitespace-break-spaces"
|
||||||
|
>
|
||||||
|
{logs.map((x, i) => (
|
||||||
|
<div key={i}>{x.logs}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
48
web/src/components/MachineBuildLog.tsx
Normal file
48
web/src/components/MachineBuildLog.tsx
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import type { LogsType } from "@/components/LogsViewer";
|
||||||
|
import { LogsViewer } from "@/components/LogsViewer";
|
||||||
|
import { getConnectionStatus } from "@/components/getConnectionStatus";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import useWebSocket from "react-use-websocket";
|
||||||
|
|
||||||
|
export function MachineBuildLog({
|
||||||
|
machine_id,
|
||||||
|
endpoint,
|
||||||
|
}: {
|
||||||
|
machine_id: string;
|
||||||
|
endpoint: string;
|
||||||
|
}) {
|
||||||
|
const [logs, setLogs] = useState<LogsType>([]);
|
||||||
|
|
||||||
|
const wsEndpoint = endpoint.replace(/^http/, "ws");
|
||||||
|
const { lastMessage, readyState } = useWebSocket(
|
||||||
|
`${wsEndpoint}/ws/${machine_id}`,
|
||||||
|
{
|
||||||
|
shouldReconnect: () => true,
|
||||||
|
reconnectAttempts: 20,
|
||||||
|
reconnectInterval: 1000,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const connectionStatus = getConnectionStatus(readyState);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!lastMessage?.data) return;
|
||||||
|
|
||||||
|
const message = JSON.parse(lastMessage.data);
|
||||||
|
|
||||||
|
console.log(message);
|
||||||
|
|
||||||
|
if (message?.event === "LOGS") {
|
||||||
|
setLogs((logs) => [...(logs ?? []), message.data]);
|
||||||
|
}
|
||||||
|
}, [lastMessage]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{connectionStatus}
|
||||||
|
<LogsViewer logs={logs} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -23,8 +23,12 @@ import {
|
|||||||
TableRow,
|
TableRow,
|
||||||
} from "@/components/ui/table";
|
} from "@/components/ui/table";
|
||||||
import { type MachineType } from "@/db/schema";
|
import { type MachineType } from "@/db/schema";
|
||||||
import { addMachineSchema } from "@/server/addMachineSchema";
|
|
||||||
import {
|
import {
|
||||||
|
addCustomMachineSchema,
|
||||||
|
addMachineSchema,
|
||||||
|
} from "@/server/addMachineSchema";
|
||||||
|
import {
|
||||||
|
addCustomMachine,
|
||||||
addMachine,
|
addMachine,
|
||||||
deleteMachine,
|
deleteMachine,
|
||||||
disableMachine,
|
disableMachine,
|
||||||
@ -92,10 +96,15 @@ export const columns: ColumnDef<Machine>[] = [
|
|||||||
return (
|
return (
|
||||||
// <a className="hover:underline" href={`/${row.original.id}`}>
|
// <a className="hover:underline" href={`/${row.original.id}`}>
|
||||||
<div className="flex flex-row gap-2 items-center">
|
<div className="flex flex-row gap-2 items-center">
|
||||||
<div>{row.getValue("name")}</div>
|
<a href={`/machines/${row.original.id}`} className="hover:underline">
|
||||||
|
{row.getValue("name")}
|
||||||
|
</a>
|
||||||
{row.original.disabled && (
|
{row.original.disabled && (
|
||||||
<Badge variant="destructive">Disabled</Badge>
|
<Badge variant="destructive">Disabled</Badge>
|
||||||
)}
|
)}
|
||||||
|
{!row.original.disabled && row.original.status && (
|
||||||
|
<Badge variant="outline">{row.original.status}</Badge>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
// </a>
|
// </a>
|
||||||
);
|
);
|
||||||
@ -254,6 +263,20 @@ export function MachineList({ data }: { data: Machine[] }) {
|
|||||||
serverAction={addMachine}
|
serverAction={addMachine}
|
||||||
formSchema={addMachineSchema}
|
formSchema={addMachineSchema}
|
||||||
/>
|
/>
|
||||||
|
<InsertModal
|
||||||
|
title="Custom Machine"
|
||||||
|
description="Add custom Comfyui machines to your account."
|
||||||
|
serverAction={addCustomMachine}
|
||||||
|
formSchema={addCustomMachineSchema}
|
||||||
|
fieldConfig={{
|
||||||
|
type: {
|
||||||
|
fieldType: "fallback",
|
||||||
|
inputProps: {
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-md border overflow-x-auto w-full">
|
<div className="rounded-md border overflow-x-auto w-full">
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { LogsViewer } from "./LogsViewer";
|
||||||
|
import { getConnectionStatus } from "./getConnectionStatus";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@ -10,9 +12,8 @@ import {
|
|||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import type { getMachines } from "@/server/curdMachine";
|
import type { getMachines } from "@/server/curdMachine";
|
||||||
import { Check, CircleOff, SatelliteDish } from "lucide-react";
|
import React, { useEffect, useState } from "react";
|
||||||
import React, { useEffect, useRef, useState } from "react";
|
import useWebSocket from "react-use-websocket";
|
||||||
import useWebSocket, { ReadyState } from "react-use-websocket";
|
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
@ -98,17 +99,7 @@ function MachineWS({
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const connectionStatus = {
|
const connectionStatus = getConnectionStatus(readyState);
|
||||||
[ReadyState.CONNECTING]: (
|
|
||||||
<SatelliteDish size={14} className="text-orange-500" />
|
|
||||||
),
|
|
||||||
[ReadyState.OPEN]: <Check size={14} className="text-green-500" />,
|
|
||||||
[ReadyState.CLOSING]: <CircleOff size={14} className="text-orange-500" />,
|
|
||||||
[ReadyState.CLOSED]: <CircleOff size={14} className="text-red-500" />,
|
|
||||||
[ReadyState.UNINSTANTIATED]: "Uninstantiated",
|
|
||||||
}[readyState];
|
|
||||||
|
|
||||||
const container = useRef<HTMLDivElement | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!lastMessage?.data) return;
|
if (!lastMessage?.data) return;
|
||||||
@ -130,18 +121,6 @@ function MachineWS({
|
|||||||
}
|
}
|
||||||
}, [lastMessage]);
|
}, [lastMessage]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// console.log(logs.length, container.current);
|
|
||||||
if (container.current) {
|
|
||||||
const scrollHeight = container.current.scrollHeight;
|
|
||||||
|
|
||||||
container.current.scrollTo({
|
|
||||||
top: scrollHeight,
|
|
||||||
behavior: "smooth",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [logs.length]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger asChild className="">
|
<DialogTrigger asChild className="">
|
||||||
@ -156,24 +135,7 @@ function MachineWS({
|
|||||||
You can view your run's outputs here
|
You can view your run's outputs here
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div
|
<LogsViewer logs={logs} />
|
||||||
ref={(ref) => {
|
|
||||||
if (!container.current && ref) {
|
|
||||||
const scrollHeight = ref.scrollHeight;
|
|
||||||
|
|
||||||
ref.scrollTo({
|
|
||||||
top: scrollHeight,
|
|
||||||
behavior: "instant",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
container.current = ref;
|
|
||||||
}}
|
|
||||||
className="flex flex-col text-xs p-2 overflow-y-scroll max-h-[400px] whitespace-break-spaces"
|
|
||||||
>
|
|
||||||
{logs.map((x, i) => (
|
|
||||||
<div key={i}>{x.logs}</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
|
17
web/src/components/getConnectionStatus.tsx
Normal file
17
web/src/components/getConnectionStatus.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { Check, CircleOff, SatelliteDish } from "lucide-react";
|
||||||
|
import React from "react";
|
||||||
|
import { ReadyState } from "react-use-websocket";
|
||||||
|
|
||||||
|
export function getConnectionStatus(readyState: ReadyState) {
|
||||||
|
const connectionStatus = {
|
||||||
|
[ReadyState.CONNECTING]: (
|
||||||
|
<SatelliteDish size={14} className="text-orange-500" />
|
||||||
|
),
|
||||||
|
[ReadyState.OPEN]: <Check size={14} className="text-green-500" />,
|
||||||
|
[ReadyState.CLOSING]: <CircleOff size={14} className="text-orange-500" />,
|
||||||
|
[ReadyState.CLOSED]: <CircleOff size={14} className="text-red-500" />,
|
||||||
|
[ReadyState.UNINSTANTIATED]: "Uninstantiated",
|
||||||
|
}[readyState];
|
||||||
|
|
||||||
|
return connectionStatus;
|
||||||
|
}
|
@ -108,6 +108,12 @@ export const machinesType = pgEnum("machine_type", [
|
|||||||
"modal-serverless",
|
"modal-serverless",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
export const machinesStatus = pgEnum("machine_status", [
|
||||||
|
"ready",
|
||||||
|
"building",
|
||||||
|
"error",
|
||||||
|
]);
|
||||||
|
|
||||||
// We still want to keep the workflow run record.
|
// We still want to keep the workflow run record.
|
||||||
export const workflowRunsTable = dbSchema.table("workflow_runs", {
|
export const workflowRunsTable = dbSchema.table("workflow_runs", {
|
||||||
id: uuid("id").primaryKey().defaultRandom().notNull(),
|
id: uuid("id").primaryKey().defaultRandom().notNull(),
|
||||||
@ -193,6 +199,9 @@ export const machinesTable = dbSchema.table("machines", {
|
|||||||
disabled: boolean("disabled").default(false).notNull(),
|
disabled: boolean("disabled").default(false).notNull(),
|
||||||
auth_token: text("auth_token"),
|
auth_token: text("auth_token"),
|
||||||
type: machinesType("type").notNull().default("classic"),
|
type: machinesType("type").notNull().default("classic"),
|
||||||
|
status: machinesStatus("status").notNull().default("ready"),
|
||||||
|
snapshot: jsonb("snapshot").$type<any>(),
|
||||||
|
build_log: text("build_log"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const insertMachineSchema = createInsertSchema(machinesTable, {
|
export const insertMachineSchema = createInsertSchema(machinesTable, {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { insertMachineSchema } from "@/db/schema";
|
import { insertMachineSchema, machinesTable } from "@/db/schema";
|
||||||
|
import { createInsertSchema } from "drizzle-zod";
|
||||||
|
|
||||||
export const addMachineSchema = insertMachineSchema.pick({
|
export const addMachineSchema = insertMachineSchema.pick({
|
||||||
name: true,
|
name: true,
|
||||||
@ -6,3 +7,14 @@ export const addMachineSchema = insertMachineSchema.pick({
|
|||||||
type: true,
|
type: true,
|
||||||
auth_token: true,
|
auth_token: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const insertCustomMachineSchema = createInsertSchema(machinesTable, {
|
||||||
|
name: (schema) => schema.name.default("My Machine"),
|
||||||
|
type: (schema) => schema.type.default("modal-serverless"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const addCustomMachineSchema = insertCustomMachineSchema.pick({
|
||||||
|
name: true,
|
||||||
|
type: true,
|
||||||
|
snapshot: true,
|
||||||
|
});
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
import type { addMachineSchema } from "./addMachineSchema";
|
import type {
|
||||||
|
addCustomMachineSchema,
|
||||||
|
addMachineSchema,
|
||||||
|
} from "./addMachineSchema";
|
||||||
import { withServerPromise } from "./withServerPromise";
|
import { withServerPromise } from "./withServerPromise";
|
||||||
import { db } from "@/db/db";
|
import { db } from "@/db/db";
|
||||||
import { machinesTable } from "@/db/schema";
|
import { machinesTable } from "@/db/schema";
|
||||||
import { auth } from "@clerk/nextjs";
|
import { auth } from "@clerk/nextjs";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq, isNull } from "drizzle-orm";
|
||||||
import { revalidatePath } from "next/cache";
|
import { revalidatePath } from "next/cache";
|
||||||
|
import { headers } from "next/headers";
|
||||||
|
import { redirect } from "next/navigation";
|
||||||
import "server-only";
|
import "server-only";
|
||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
|
|
||||||
@ -27,6 +32,26 @@ export async function getMachines() {
|
|||||||
return machines;
|
return machines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getMachineById(id: string) {
|
||||||
|
const { userId, orgId } = auth();
|
||||||
|
if (!userId) throw new Error("No user id");
|
||||||
|
const machines = await db
|
||||||
|
.select()
|
||||||
|
.from(machinesTable)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
orgId
|
||||||
|
? eq(machinesTable.org_id, orgId)
|
||||||
|
: and(
|
||||||
|
eq(machinesTable.user_id, userId),
|
||||||
|
isNull(machinesTable.org_id)
|
||||||
|
),
|
||||||
|
eq(machinesTable.id, id)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return machines[0];
|
||||||
|
}
|
||||||
|
|
||||||
export const addMachine = withServerPromise(
|
export const addMachine = withServerPromise(
|
||||||
async (data: z.infer<typeof addMachineSchema>) => {
|
async (data: z.infer<typeof addMachineSchema>) => {
|
||||||
const { userId, orgId } = auth();
|
const { userId, orgId } = auth();
|
||||||
@ -42,6 +67,72 @@ export const addMachine = withServerPromise(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const addCustomMachine = withServerPromise(
|
||||||
|
async (data: z.infer<typeof addCustomMachineSchema>) => {
|
||||||
|
const { userId, orgId } = auth();
|
||||||
|
const headersList = headers();
|
||||||
|
|
||||||
|
if (!userId) return { error: "No user id" };
|
||||||
|
|
||||||
|
// Insert to our db
|
||||||
|
const a = await db
|
||||||
|
.insert(machinesTable)
|
||||||
|
.values({
|
||||||
|
...data,
|
||||||
|
org_id: orgId,
|
||||||
|
user_id: userId,
|
||||||
|
status: "building",
|
||||||
|
endpoint: "not-ready",
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
const b = a[0];
|
||||||
|
|
||||||
|
// const origin = new URL(request.url).origin;
|
||||||
|
const domain = headersList.get("x-forwarded-host") || "";
|
||||||
|
const protocol = headersList.get("x-forwarded-proto") || "";
|
||||||
|
// console.log("domain", domain);
|
||||||
|
// console.log("domain", `${protocol}://${domain}/api/machine-built`);
|
||||||
|
// return { message: "Machine Building" };
|
||||||
|
|
||||||
|
if (domain === "") {
|
||||||
|
throw new Error("No domain");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call remote builder
|
||||||
|
const result = await fetch(`${process.env.MODAL_BUILDER_URL!}/create`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
machine_id: b.id,
|
||||||
|
name: b.id,
|
||||||
|
snapshot: JSON.parse(data.snapshot as string),
|
||||||
|
callback_url: `${protocol}://${domain}/api/machine-built`,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result.ok) {
|
||||||
|
const error_log = await result.text();
|
||||||
|
await db
|
||||||
|
.update(machinesTable)
|
||||||
|
.set({
|
||||||
|
...data,
|
||||||
|
status: "error",
|
||||||
|
build_log: error_log,
|
||||||
|
})
|
||||||
|
.where(eq(machinesTable.id, b.id));
|
||||||
|
throw new Error(`Error: ${result.statusText} ${error_log}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
redirect(`/machines/${b.id}`);
|
||||||
|
|
||||||
|
// revalidatePath("/machines");
|
||||||
|
return { message: "Machine Building" };
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export const updateMachine = withServerPromise(
|
export const updateMachine = withServerPromise(
|
||||||
async ({
|
async ({
|
||||||
id,
|
id,
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
|
import { isRedirectError } from "next/dist/client/components/redirect";
|
||||||
|
|
||||||
export async function wrapServerPromise<T>(result: Promise<T>) {
|
export async function wrapServerPromise<T>(result: Promise<T>) {
|
||||||
return result.catch((error) => {
|
return result.catch((error) => {
|
||||||
|
if (isRedirectError(error)) throw error;
|
||||||
return {
|
return {
|
||||||
error: error.message,
|
error: error.message,
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user